Blame | Last modification | View Log | RSS feed
<?php/*** DebugKit DebugToolbar Component** Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)** Licensed under The MIT License* Redistributions of files must retain the above copyright notice.** @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)* @link http://cakephp.org CakePHP(tm) Project* @since DebugKit 0.1* @license http://www.opensource.org/licenses/mit-license.php MIT License*/App::uses('CakeLog', 'Log');App::uses('CakeLogInterface', 'Log');App::uses('DebugTimer', 'DebugKit.Lib');App::uses('DebugMemory', 'DebugKit.Lib');App::uses('HelperCollection', 'View');App::uses('CakeEventManager', 'Event');App::uses('CakeEventListener', 'Event');/*** Class ToolbarComponent** @since DebugKit 0.1*/class ToolbarComponent extends Component implements CakeEventListener {/*** Settings for the Component** - forceEnable - Force the toolbar to display even if debug == 0. Default = false* - autoRun - Automatically display the toolbar. If set to false, toolbar display can be triggered by adding* `?debug=true` to your URL.** @var array*/public $settings = array('forceEnable' => false,'autoRun' => true);/*** Controller instance reference** @var object*/public $controller;/*** Components used by DebugToolbar** @var array*/public $components = array('RequestHandler', 'Session');/*** The default panels the toolbar uses.* which panels are used can be configured when attaching the component** @var array*/protected $_defaultPanels = array('DebugKit.History','DebugKit.Session','DebugKit.Request','DebugKit.SqlLog','DebugKit.Timer','DebugKit.Log','DebugKit.Variables','DebugKit.Environment','DebugKit.Include');/*** Loaded panel objects.** @var array*/public $panels = array();/*** javascript files component will be using** @var array*/public $javascript = array('libs' => 'DebugKit./js/js_debug_toolbar');/*** CSS files component will be using** @var array*/public $css = array('DebugKit./css/debug_toolbar.css');/*** CacheKey used for the cache file.** @var string*/public $cacheKey = 'toolbar_cache';/*** Duration of the debug kit history cache** @var string*/public $cacheDuration = '+4 hours';/*** Status whether component is enable or disable** @var boolean*/public $enabled = true;/*** Constructor** If debug is off the component will be disabled and not do any further time tracking* or load the toolbar helper.** @param ComponentCollection $collection* @param array $settings* @return \ToolbarComponent*/public function __construct(ComponentCollection $collection, $settings = array()) {$settings = array_merge((array)Configure::read('DebugKit'), $settings);$panels = $this->_defaultPanels;if (isset($settings['panels'])) {$panels = $this->_makePanelList($settings['panels']);unset($settings['panels']);}$this->controller = $collection->getController();parent::__construct($collection, array_merge($this->settings, (array)$settings));if (!Configure::read('debug') &&empty($this->settings['forceEnable'])) {$this->enabled = false;return false;}if ($this->settings['autoRun'] === false &&!isset($this->controller->request->query['debug'])) {$this->enabled = false;return false;}$this->controller->getEventManager()->attach($this);DebugMemory::record(__d('debug_kit', 'Component initialization'));$this->cacheKey .= $this->Session->read('Config.userAgent');if (in_array('DebugKit.History', $panels) ||(isset($settings['history']) && $settings['history'] !== false)) {$this->_createCacheConfig();}$this->_loadPanels($panels, $settings);return false;}/*** Register all the timing handlers for core events.** @return array*/public function implementedEvents() {$before = function ($name) {return function () use ($name) {DebugTimer::start($name, __d('debug_kit', $name));};};$after = function ($name) {return function () use ($name) {DebugTimer::stop($name);};};return array('Controller.initialize' => array(array('priority' => 0, 'callable' => $before('Event: Controller.initialize')),array('priority' => 999, 'callable' => $after('Event: Controller.initialize'))),'Controller.startup' => array(array('priority' => 0, 'callable' => $before('Event: Controller.startup')),array('priority' => 999, 'callable' => $after('Event: Controller.startup'))),'Controller.beforeRender' => array(array('priority' => 0, 'callable' => $before('Event: Controller.beforeRender')),array('priority' => 999, 'callable' => $after('Event: Controller.beforeRender'))),'Controller.shutdown' => array(array('priority' => 0, 'callable' => $before('Event: Controller.shutdown')),array('priority' => 999, 'callable' => $after('Event: Controller.shutdown'))),'View.beforeRender' => array(array('priority' => 0, 'callable' => $before('Event: View.beforeRender')),array('priority' => 999, 'callable' => $after('Event: View.beforeRender'))),'View.afterRender' => array(array('priority' => 0, 'callable' => $before('Event: View.afterRender')),array('priority' => 999, 'callable' => $after('Event: View.afterRender'))),'View.beforeLayout' => array(array('priority' => 0, 'callable' => $before('Event: View.beforeLayout')),array('priority' => 999, 'callable' => $after('Event: View.beforeLayout'))),'View.afterLayout' => array(array('priority' => 0, 'callable' => $before('Event: View.afterLayout')),array('priority' => 999, 'callable' => $after('Event: View.afterLayout'))),);}/*** Initialize callback.* If automatically disabled, tell component collection about the state.** @param Controller $controller* @return boolean*/public function initialize(Controller $controller) {if (!$this->enabled) {$this->_Collection->disable('Toolbar');}}/*** Go through user panels and remove default panels as indicated.** @param array $userPanels The list of panels ther user has added removed.* @return array Array of panels to use.*/protected function _makePanelList($userPanels) {$panels = $this->_defaultPanels;foreach ($userPanels as $key => $value) {if (is_numeric($key)) {$panels[] = $value;}if (is_string($key) && $value === false) {$index = array_search($key, $panels);if ($index !== false) {unset($panels[$index]);}// Compatibility for when panels were not// required to have a plugin prefix.$alternate = 'DebugKit.' . ucfirst($key);$index = array_search($alternate, $panels);if ($index !== false) {unset($panels[$index]);}}}return $panels;}/*** Component Startup** @param Controller $controller* @return boolean*/public function startup(Controller $controller) {$panels = array_keys($this->panels);foreach ($panels as $panelName) {$this->panels[$panelName]->startup($controller);}DebugTimer::start('controllerAction',__d('debug_kit', 'Controller action'));DebugMemory::record(__d('debug_kit', 'Controller action start'));}/*** beforeRedirect callback** @param Controller $controller* @param $url* @param null $status* @param boolean $exit* @return void*/public function beforeRedirect(Controller $controller, $url, $status = null, $exit = true) {if (!class_exists('DebugTimer')) {return null;}DebugTimer::stop('controllerAction');DebugTimer::start('processToolbar',__d('debug_kit', 'Processing toolbar state'));$vars = $this->_gatherVars($controller);$this->_saveState($controller, $vars);DebugTimer::stop('processToolbar');}/*** beforeRender callback** Calls beforeRender on all the panels and set the aggregate to the controller.** @param Controller $controller* @return void*/public function beforeRender(Controller $controller) {if (!class_exists('DebugTimer')) {return null;}DebugTimer::stop('controllerAction');DebugTimer::start('processToolbar',__d('debug_kit', 'Processing toolbar data'));$vars = $this->_gatherVars($controller);$this->_saveState($controller, $vars);$this->javascript = array_unique(array_merge($this->javascript, $vars['javascript']));$this->css = array_unique(array_merge($this->css, $vars['css']));unset($vars['javascript'], $vars['css']);$controller->set(array('debugToolbarPanels' => $vars,'debugToolbarJavascript' => $this->javascript,'debugToolbarCss' => $this->css));$isHtml = (!isset($controller->request->params['ext']) ||$controller->request->params['ext'] === 'html');if (!$controller->request->is('ajax') && $isHtml) {$format = 'Html';} else {$format = 'FirePhp';}$controller->helpers[] = 'DebugKit.DebugTimer';$controller->helpers['DebugKit.Toolbar'] = array('output' => sprintf('DebugKit.%sToolbar', $format),'cacheKey' => $this->cacheKey,'cacheConfig' => 'debug_kit','forceEnable' => $this->settings['forceEnable'],);DebugTimer::stop('processToolbar');DebugMemory::record(__d('debug_kit', 'Controller render start'));}/*** Load a toolbar state from cache** @param integer $key* @return array*/public function loadState($key) {$history = Cache::read($this->cacheKey, 'debug_kit');if (isset($history[$key])) {return $history[$key];}return array();}/*** Create the cache config for the history** @return void*/protected function _createCacheConfig() {if (Configure::read('Cache.disable') === true || Cache::config('debug_kit')) {return;}$cache = array('duration' => $this->cacheDuration,'engine' => 'File','path' => CACHE);if (isset($this->settings['cache'])) {$cache = array_merge($cache, $this->settings['cache']);}Cache::config('debug_kit', $cache);}/*** collects the panel contents** @param Controller $controller* @return array Array of all panel beforeRender()*/protected function _gatherVars(Controller $controller) {$vars = array('javascript' => array(), 'css' => array());$panels = array_keys($this->panels);foreach ($panels as $panelName) {$panel = $this->panels[$panelName];$panelName = Inflector::underscore($panelName);$vars[$panelName]['content'] = $panel->beforeRender($controller);$elementName = Inflector::underscore($panelName) . '_panel';if (isset($panel->elementName)) {$elementName = $panel->elementName;}$vars[$panelName]['elementName'] = $elementName;$vars[$panelName]['plugin'] = $panel->plugin;$vars[$panelName]['title'] = $panel->title;$vars[$panelName]['disableTimer'] = true;if (!empty($panel->javascript)) {$vars['javascript'] = array_merge($vars['javascript'], (array)$panel->javascript);}if (!empty($panel->css)) {$vars['css'] = array_merge($vars['css'], (array)$panel->css);}}return $vars;}/*** Load Panels used in the debug toolbar** @param $panels* @param $settings* @return void*/protected function _loadPanels($panels, $settings) {foreach ($panels as $panel) {$className = ucfirst($panel) . 'Panel';list($plugin, $className) = pluginSplit($className, true);App::uses($className, $plugin . 'Panel');if (!class_exists($className)) {trigger_error(__d('debug_kit', 'Could not load DebugToolbar panel %s', $panel), E_USER_WARNING);continue;}$panelObj = new $className($settings);if ($panelObj instanceof DebugPanel) {list(, $panel) = pluginSplit($panel);$this->panels[Inflector::underscore($panel)] = $panelObj;}}}/*** Save the current state of the toolbar varibles to the cache file.** @param \Controller|object $controller Controller instance* @param array $vars Vars to save.* @return void*/protected function _saveState(Controller $controller, $vars) {$config = Cache::config('debug_kit');if (empty($config) || !isset($this->panels['history'])) {return;}$history = Cache::read($this->cacheKey, 'debug_kit');if (empty($history)) {$history = array();}if (count($history) == $this->panels['history']->history) {array_pop($history);}if (isset($vars['variables']['content'])) {// Remove unserializable native objects.array_walk_recursive($vars['variables']['content'], function (&$item) {if ($item instanceof Closure ||$item instanceof PDO ||$item instanceof SimpleXmlElement) {$item = 'Unserializable object - ' . get_class($item);} elseif ($item instanceof Exception) {$item = sprintf('Unserializable object - %s. Error: %s in %s, line %s',get_class($item),$item,$item->getMessage(),$item->getFile(),$item->getLine());}return $item;});}unset($vars['history']);array_unshift($history, $vars);Cache::write($this->cacheKey, $history, 'debug_kit');}}