[Solved]Database Schema Cache In Single Php File, Onendrequest Error

I integrated:

http://weavora.com/blog/2011/07/04/db-scheme-php-file-cache-yii-extension/

so i could use a faster database schema cache. With CFileCache i get about 0.199ms while with this i get 0.098ms. Not a huge improvement but seems very handy in cases where i don’t have data caching enabled but opcode caching is supported.

So here is the issue. After integration when i clear the runtime directory and open the site for first time, i get:




PHP notice

Undefined offset: 2

/home/shoaibi/public_html/yii-1.1.12.b600af/framework/collections/CListIterator.php(73)


61     public function key()

62     {

63         return $this->_i;

64     }

65 

66     /**

67      * Returns the current array item.

68      * This method is required by the interface Iterator.

69      * @return mixed the current array item

70      */

71     public function current()

72     {

73         return $this->_d[$this->_i];

74     }

75 

76     /**

77      * Moves the internal pointer to the next array item.

78      * This method is required by the interface Iterator.

79      */

80     public function next()

81     {

82         $this->_i++;

83     }

84 

85     /**





Stack Trace


#0	

–  /home/shoaibi/public_html/yii-1.1.12.b600af/framework/base/CComponent.php(546): CListIterator->current()

541     public function raiseEvent($name,$event)

542     {

543         $name=strtolower($name);

544         if(isset($this->_e[$name]))

545         {

546             foreach($this->_e[$name] as $handler)

547             {

548                 if(is_string($handler))

549                     call_user_func($handler,$event);

550                 else if(is_callable($handler,true))

551                 {




#1	

–  /home/shoaibi/public_html/yii-1.1.12.b600af/framework/base/CApplication.php(201): CComponent->raiseEvent("onEndRequest", CEvent)

196     public function onEndRequest($event)

197     {

198         if(!$this->_ended)

199         {

200             $this->_ended=true;

201             $this->raiseEvent('onEndRequest',$event);

202         }

203     }

204 

205     /**

206      * Returns the unique identifier for the application.




#2	

–  /home/shoaibi/public_html/yii-1.1.12.b600af/framework/base/CApplication.php(164): CApplication->onEndRequest(CEvent)

159     {

160         if($this->hasEventHandler('onBeginRequest'))

161             $this->onBeginRequest(new CEvent($this));

162         $this->processRequest();

163         if($this->hasEventHandler('onEndRequest'))

164             $this->onEndRequest(new CEvent($this));

165     }

166 

167     /**

168      * Terminates the application.

169      * This method replaces PHP's exit() function by calling




#3	

–  /home/shoaibi/public_html/www.cloudstripe.com/content/index.php(14): CApplication->run()

09 defined('YII_DEBUG') or define('YII_DEBUG', $env->getDebug());

10 defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL', $env->getTraceLevel());

11 

12 // run Yii app

13 require_once($env->getYiiPath());

14 Yii::createWebApplication($env->getConfig())->run();

15 



While when i reload site works just fine, so this happens once every cache clear. I have trimmed it down to endRequest() and _saveSets() functions. I realize that is not lot of progress but i was about to call day off and look into this issue tomorrow morning. In the mean while if anyone has the appetite to test, please do and inform me if you find a patch.

One small patch that i applied was to add “$event” argument in endRequest()'s signatures.

Resolved it. It was actually a bug.

[size="4"]Theory:[/size]

If you are registering event handler on onEndRequest it shall execute before saveGlobalState for sure as saveGlobalState removes itself from event handler stack considering itself last so if you have just one event handler after that, it would get an error as described in first post due to reindexing occurring inside $this->_d in CListIterator.

This means that the event handler previously at index 2 would now become at 1, the one on index 3 would be on index 2. CListIterator would continue from index "2"(index if saveGlobalState[which was 1 in my case] + 1) and hence skip the event handler that is now on index 1(the one that was on index 2 previously). Thats what causes the error. You could say its a bug in yii but its really not. processLogs and saveGlobalState are supposed to be last event handlers for onEndRequest, just like there are few that are supposed to be first on onBeginRequest.

Examples:

0 -> processLogs

1 -> saveGlobalState

2 -> myCustomHandler

When saveGlobalstate is processed CApplication.php:668 would remove it, making it:

0 -> processLogs

1 -> myCustomHandler

But "$this->_i" would be 2 now which is undefined offset.

If you have 2 or more event handlers after saveGlobalState then the one immediately after saveGlobalState would be skipped.

Usually you could just put your custom event handler for onEndRequest at front as that would put it ahead of processLogs too and if an error occurs application would have enough time to gather and store logs in process routes.

[size="4"]Patch:[/size]

Change line 2 inside init block from:


Yii::app()->attachEventHandler('onEndRequest', array($this, 'endRequest'));

to:


Yii::app()->getEventHandlers('onEndRequest')->insertAt(0,array($this, 'endRequest'));