Não é muito raro vermos em alguns sites, principalmente no rodapé, a informação do tempo decorrido para exibir uma página. E nos sites em PHP, geralmente é usado a função microtime
para calcular esse tempo, chamando-a antes e depois do trecho de código que queremos calcular o tempo de execução, e depois subtraindo o primeiro do último.
Isso é muito útil, principalmente para trabalharmos na melhora da performance do código. Foi daí que me deu a idéia de criar um plugin para Zend Framework para facilitar o uso dessa função, e eis o resultado:
class My_Controller_Plugin_ElapsedTime extends Zend_Controller_Plugin_Abstract { /* * Constantes que serão usadas para identificar a partir de * que parte da execução deseja-se calcular o tempo decorrido. * O nome das constantes é baseado no nome das funções que * inicializam os valores correspondentes na propriedade * $_startTime, logo abaixo */ const EVENT_ROUTE_STARTUP = 1; const EVENT_ROUTE_SHUTDOWN = 2; const EVENT_DISPATCH_LOOP_STARTUP = 3; const EVENT_PRE_DISPATCH = 4; const EVENT_POST_DISPATCH = 5; const EVENT_DISPATCH_LOOP_SHUTDOWN = 6; // Guarda o tempo de inicialização de cada parte da execução static protected $_startTime = array( self::EVENT_ROUTE_STARTUP => null, self::EVENT_ROUTE_SHUTDOWN => null, self::EVENT_DISPATCH_LOOP_STARTUP => null, self::EVENT_PRE_DISPATCH => null, self::EVENT_POST_DISPATCH => null, self::EVENT_DISPATCH_LOOP_SHUTDOWN => null ); // Define o tempo de inicialização antes de as rotas serem // inicializadas public function routeStartup( Zend_Controller_Request_Abstract $request) { self::$_startTime[self::EVENT_ROUTE_STARTUP] = $this->microtime(); } // Define o tempo de inicialização depois de as rotas serem // inicializadas public function routeShutdown( Zend_Controller_Request_Abstract $request) { self::$_startTime[self::EVENT_ROUTE_SHUTDOWN] = $this->microtime(); } // Define o tempo de inicialização antes de iniciar o loop // de dispatch public function dispatchLoopStartup( Zend_Controller_Request_Abstract $request) { self::$_startTime[self::EVENT_DISPATCH_LOOP_STARTUP] = $this->microtime(); } // Define o tempo de inicialização antes do dispatch de // uma action public function preDispatch( Zend_Controller_Request_Abstract $request) { self::$_startTime[self::EVENT_PRE_DISPATCH] = $this->microtime(); } // Define o tempo de inicialização depois do dispatch de // uma action public function postDispatch( Zend_Controller_Request_Abstract $request) { self::$_startTime[self::EVENT_POST_DISPATCH] = $this->microtime(); } // Define o tempo de inicialização depois de finalizar o // loop de dispatch public function dispatchLoopShutdown() { self::$_startTime[self::EVENT_DISPATCH_LOOP_SHUTDOWN] = $this->microtime(); } /** * Retorna o tempo de inicialização de cada evento do plugin, * ou de um evento específico, que pode ser passado como * parâmetro baseado no valor das contantes desta classe * * @param int $event * @return mixed Float se receber $event, senão, array */ public function getStartTime($event=null) { if ($event) { if (!isset(self::$_startTime[$event])) { throw new Zend_Exception('Invalid value for $event: ' . $event); } return self::$_startTime[$event]; } return self::$_startTime; } /** * Calcula o tempo decorrido de um trecho e código. * * @param int $event * @return float */ public function getElapsedTime( $event=self::EVENT_ROUTE_STARTUP) { $startTime = $this->getStartTime($event); // Lança uma excessão Caso o evento não tenha sido // inicializado ainda if (!$startTime) { throw new Zend_Exception('Event has not been ' . 'initialized yet'); } // Pega o microtime atual e substrai dele o tempo // inicial para calcular o tempo decorrido $endTime = $this->microtime(); $elapsedTime = $endTime - $startTime; return $elapsedTime; } /** * Pega o microtime em formato float, levando em conta as * variações sofridas pela função de acordo com a versão do * PHP */ private function _microtime() { if (version_compare(PHP_VERSION, '5.0.0', '>=')) { // O parâmetro já converte o restulado da função para // float a partir da versão 5.0.0 return microtime(true); } else { // Antes da versão 5.0.0, a conversão para float tinha // que ser feita manualmente list($usec, $sec) = explode(" ", microtime()); return ((float)$usec + (float)$sec); } } } |
Ufa, parece coisa demais pra uma coisa tão simples, não é? Mas tudo isso nos dá a possibilidade de medir o tempo de execução a partir de várias partes diferentes do código, que é uma facilidade que os plugins do ZF nos oferece, então, por que não aproveitar isso pra fazer um plugin mais completo, não é?
Feito o plugin, agora vamos ao View Helper, que vai se encarregar de pegar o valor definido no plugin e subtraí-lo de um novo microtime
para exibir na página:
class My_View_Helper_ElapsedTime extends Zend_View_Helper_Abstract { /** * Exibe o tempo decorrido de um trecho de código * baseado nos valores inicializados no plugin * 'My_Controller_Plugin_ElapsedTime' * * @param int $precision * @param string|Zend_Locale $locale * @return float * @throws Zend_Exception */ public function elapsedTime($precision = null, $locale = null, $event = My_Controller_Plugin_ElapsedTime::EVENT_ROUTE_STARTUP) { $pluginClass = 'My_Controller_Plugin_ElapsedTime'; // Verifica se o plugin foi registrado if (!Zend_Controller_Front::getInstance() ->hasPlugin($pluginClass)) { throw new Exception("Plugin '{$_pluginClass}' is not ' . 'registered"); } // Primeiro vamos pegar o plugin lá do front controller, // que já foi inicializado $elapsedTimePlugin = Zend_Controller_Front::getInstance() ->getPlugin($pluginClass); // Agora vamos pegar o valor calculado do tempo decorrido, // passando como parâmetro uma daquelas constantes definidas // na classe do plugin, pra dizer a partir de que ponto do // código onde calcular o tempo de execução. O padrão é de // antes da inicialização das rotas. $elapsedTime = $elapsedTimePlugin->getElapsedTime($event); // Retorna o tempo decorrido formatado, levando em conta o // número de decimais a ser exibido e a localização, para // apresentar os símbolos de separação de milhares e de // decimais corretos return Zend_Locale_Format::toFloat($elapsedTime, array( 'precision' => $precision, 'locale' => $locale)); } } |
Agora é só chamar o view helper lá no script pra exibir o tempo? Não, ainda falta uma coisa. É preciso inicializar o plugin primeiro. Basta colocar lá no application.ini
uma linhazinha de código, que ele será inicializado automaticamente:
resources.frontController.plugins[] = My_Controller_Plugin_ElapsedTime |
Agora sim, vamos ao script phtml
chamar nosso view helper e ver ele funcionando:
Essa página foi carregada em <?php echo $this->elapsedTime(2) ?> segundos |
Agora vamos experimentar alterar os parâmetros pra ver os tempo decorrido a partir de diferentes trechos do código:
<ul><?php $events = array( 1 => 'routeStartup', 2 => 'routeShutdown', 3 => 'dispatchLoopStartup', 4 => 'preDispatch', 5 => 'postDispatch', // 6 => 'dispatchLoopShutdown' ); foreach($events as $eventId => $eventName) { echo '<li>' . $eventName . ': ' . $this->elapsedTime(2, 'pt_BR', $eventId) . ' segundos</li>'; } ?></ul> |
O último tipo de evento (dispatchLoopShutdown) ficou comentado por uma boa razão. Quando a view é executada, esse ponto do código ainda não foi alcançado, portanto será lançada uma excessão, como foi definido na classe do plugin. Esse caso é usado mais para fins de debug mesmo, e para ver o resultado desse cálculo, é preciso chamar o plugin diretamente do front controller. Um exemplo, é chamá-lo no index.php no fim do arquivo:
$application->bootstrap()->run(); echo Zend_Controller_Front::getInstance() ->getPlugin('Case_Controller_Plugin_ElapsedTime') ->getElapsedTime( Case_Controller_Plugin_ElapsedTime::EVENT_DISPATCH_LOOP_SHUTDOWN ); |
Prontinho. Façam bom proveito!
Parabéns muito interessante o plugin, andei muito tempo atrás de um medidor de performance desse tipo xD