false, 'autoRun' => true ); /** * Controller instance reference * * @var object */ var $controller; /** * Components used by DebugToolbar * * @var array */ var $components = array('RequestHandler', 'Session'); /** * The default panels the toolbar uses. * which panels are used can be configured when attaching the component * * @var array */ var $_defaultPanels = array('history', 'session', 'request', 'sqlLog', 'timer', 'log', 'variables'); /** * Loaded panel objects. * * @var array */ var $panels = array(); /** * javascript files component will be using * * @var array **/ var $javascript = array( 'behavior' => '/debug_kit/js/js_debug_toolbar' ); /** * CacheKey used for the cache file. * * @var string **/ var $cacheKey = 'toolbar_cache'; /** * Duration of the debug kit history cache * * @var string **/ var $cacheDuration = '+4 hours'; /** * initialize * * If debug is off the component will be disabled and not do any further time tracking * or load the toolbar helper. * * @return bool **/ function initialize(&$controller, $settings) { $this->settings = am($this->settings, $settings); if (!Configure::read('debug') && empty($this->settings['forceEnable'])) { $this->enabled = false; return false; } if ($this->settings['autoRun'] == false && !isset($controller->params['url']['debug'])) { $this->enabled = false; return false; } App::import('Vendor', 'DebugKit.DebugKitDebugger'); DebugKitDebugger::setMemoryPoint(__d('debug_kit', 'Component initialization', true)); DebugKitDebugger::startTimer('componentInit', __d('debug_kit', 'Component initialization and startup', true)); $panels = $this->_defaultPanels; if (isset($settings['panels'])) { $panels = $this->_makePanelList($settings['panels']); unset($settings['panels']); } $this->cacheKey .= $this->Session->read('Config.userAgent'); if (in_array('history', $panels) || (isset($settings['history']) && $settings['history'] !== false)) { $this->_createCacheConfig(); } $this->_loadPanels($panels, $settings); $this->_set($settings); $this->controller =& $controller; return false; } /** * 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. **/ 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]); } } } return $panels; } /** * Component Startup * * @return bool **/ function startup(&$controller) { $currentViewClass = $controller->view; $this->_makeViewClass($currentViewClass); $controller->view = 'DebugKit.Debug'; $isHtml = ( !isset($controller->params['url']['ext']) || (isset($controller->params['url']['ext']) && $controller->params['url']['ext'] == 'html') ); if (!$this->RequestHandler->isAjax() && $isHtml) { $format = 'Html'; } else { $format = 'FirePhp'; } $controller->helpers['DebugKit.Toolbar'] = array( 'output' => sprintf('DebugKit.%sToolbar', $format), 'cacheKey' => $this->cacheKey, 'cacheConfig' => 'debug_kit', 'forceEnable' => $this->settings['forceEnable'], ); $panels = array_keys($this->panels); foreach ($panels as $panelName) { $this->panels[$panelName]->startup($controller); } DebugKitDebugger::stopTimer('componentInit'); DebugKitDebugger::startTimer('controllerAction', __d('debug_kit', 'Controller action', true)); DebugKitDebugger::setMemoryPoint(__d('debug_kit', 'Controller action start', true)); } /** * beforeRedirect callback * * @return void **/ function beforeRedirect(&$controller) { if (!class_exists('DebugKitDebugger')) { return null; } DebugKitDebugger::stopTimer('controllerAction'); $vars = $this->_gatherVars($controller); $this->_saveState($controller, $vars); } /** * beforeRender callback * * Calls beforeRender on all the panels and set the aggregate to the controller. * * @return void **/ function beforeRender(&$controller) { if (!class_exists('DebugKitDebugger')) { return null; } DebugKitDebugger::stopTimer('controllerAction'); $vars = $this->_gatherVars($controller); $this->_saveState($controller, $vars); $controller->set(array('debugToolbarPanels' => $vars, 'debugToolbarJavascript' => $this->javascript)); DebugKitDebugger::startTimer('controllerRender', __d('debug_kit', 'Render Controller Action', true)); DebugKitDebugger::setMemoryPoint(__d('debug_kit', 'Controller render start', true)); } /** * Load a toolbar state from cache * * @param int $key * @return array **/ 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 * @access protected **/ function _createCacheConfig() { if (Configure::read('Cache.disable') !== true) { Cache::config('debug_kit', array( 'duration' => $this->cacheDuration, 'engine' => 'File', 'path' => CACHE )); Cache::config('default'); } } /** * collects the panel contents * * @return array Array of all panel beforeRender() * @access protected **/ function _gatherVars(&$controller) { $vars = 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; } return $vars; } /** * Load Panels used in the debug toolbar * * @return void * @access protected **/ function _loadPanels($panels, $settings) { foreach ($panels as $panel) { $className = $panel . 'Panel'; if (!class_exists($className) && !App::import('Vendor', $className)) { trigger_error(sprintf(__d('debug_kit', 'Could not load DebugToolbar panel %s', true), $panel), E_USER_WARNING); continue; } list($plugin, $className) = pluginSplit($className); $panelObj =& new $className($settings); if (is_subclass_of($panelObj, 'DebugPanel') || is_subclass_of($panelObj, 'debugpanel')) { list(, $panel) = pluginSplit($panel); $this->panels[$panel] =& $panelObj; } } } /** * Makes the DoppleGangerView class if it doesn't already exist. * This allows DebugView to be compatible with all view classes. * * @param string $baseClassName * @access protected * @return void */ function _makeViewClass($baseClassName) { if (!class_exists('DoppelGangerView')) { $parent = strtolower($baseClassName) === 'view' ? false : true; App::import('View', $baseClassName, $parent); if (strpos($baseClassName, '.') !== false) { list($plugin, $baseClassName) = explode('.', $baseClassName); } if (strpos($baseClassName, 'View') === false) { $baseClassName .= 'View'; } $class = "class DoppelGangerView extends $baseClassName {}"; $this->_eval($class); } } /** * Method wrapper for eval() for testing uses. * * @return void **/ function _eval($code) { eval($code); } /** * Save the current state of the toolbar varibles to the cache file. * * @param object $controller Controller instance * @param array $vars Vars to save. * @access protected * @return void **/ function _saveState(&$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); } unset($vars['history']); array_unshift($history, $vars); Cache::write($this->cacheKey, $history, 'debug_kit'); } } /** * Debug Panel * * Abstract class for debug panels. * * @package cake.debug_kit */ class DebugPanel extends Object { /** * Defines which plugin this panel is from so the element can be located. * * @var string */ var $plugin = null; /** * Defines the title for displaying on the toolbar. If null, the class name will be used. * Overriding this allows you to define a custom name in the toolbar. * * @var string */ var $title = null; /** * Provide a custom element name for this panel. If null, the underscored version of the class * name will be used. * * @var string */ var $elementName = null; /** * startup the panel * * Pull information from the controller / request * * @param object $controller Controller reference. * @return void **/ function startup(&$controller) { } /** * Prepare output vars before Controller Rendering. * * @param object $controller Controller reference. * @return void **/ function beforeRender(&$controller) { } } /** * History Panel * * Provides debug information on previous requests. * * @package cake.debug_kit.panels **/ class HistoryPanel extends DebugPanel { var $plugin = 'debug_kit'; /** * Number of history elements to keep * * @var string **/ var $history = 5; /** * Constructor * * @param array $settings Array of settings. * @return void **/ function __construct($settings) { if (isset($settings['history'])) { $this->history = $settings['history']; } } /** * beforeRender callback function * * @return array contents for panel **/ function beforeRender(&$controller) { $cacheKey = $controller->Toolbar->cacheKey; $toolbarHistory = Cache::read($cacheKey, 'debug_kit'); $historyStates = array(); if (is_array($toolbarHistory) && !empty($toolbarHistory)) { $prefix = array(); if (!empty($controller->params['prefix'])) { $prefix[$controller->params['prefix']] = false; } foreach ($toolbarHistory as $i => $state) { if (!isset($state['request']['content']['params']['url']['url'])) { continue; } $historyStates[] = array( 'title' => $state['request']['content']['params']['url']['url'], 'url' => array_merge($prefix, array( 'plugin' => 'debug_kit', 'controller' => 'toolbar_access', 'action' => 'history_state', $i + 1)) ); } } if (count($historyStates) >= $this->history) { array_pop($historyStates); } return $historyStates; } } /** * Variables Panel * * Provides debug information on the View variables. * * @package cake.debug_kit.panels **/ class VariablesPanel extends DebugPanel { var $plugin = 'debug_kit'; /** * beforeRender callback * * @return array **/ function beforeRender(&$controller) { return array_merge($controller->viewVars, array('$this->data' => $controller->data)); } } /** * Session Panel * * Provides debug information on the Session contents. * * @package cake.debug_kit.panels **/ class SessionPanel extends DebugPanel { var $plugin = 'debug_kit'; /** * beforeRender callback * * @param object $controller * @access public * @return array */ function beforeRender(&$controller) { $sessions = $controller->Toolbar->Session->read(); return $sessions; } } /** * Request Panel * * Provides debug information on the Current request params. * * @package cake.debug_kit.panels **/ class RequestPanel extends DebugPanel { var $plugin = 'debug_kit'; /** * beforeRender callback - grabs request params * * @return array **/ function beforeRender(&$controller) { $out = array(); $out['params'] = $controller->params; if (isset($controller->Cookie)) { $out['cookie'] = $controller->Cookie->read(); } $out['get'] = $_GET; $out['currentRoute'] = Router::currentRoute(); return $out; } } /** * Timer Panel * * Provides debug information on all timers used in a request. * * @package cake.debug_kit.panels **/ class TimerPanel extends DebugPanel { var $plugin = 'debug_kit'; /** * startup - add in necessary helpers * * @return void **/ function startup(&$controller) { if (!in_array('Number', $controller->helpers)) { $controller->helpers[] = 'Number'; } if (!in_array('SimpleGraph', $controller->helpers)) { $controller->helpers[] = 'DebugKit.SimpleGraph'; } } } /** * SqlLog Panel * * Provides debug information on the SQL logs and provides links to an ajax explain interface. * * @package cake.debug_kit.panels **/ class SqlLogPanel extends DebugPanel { var $plugin = 'debug_kit'; /** * Minimum number of Rows Per Millisecond that must be returned by a query before an explain * is done. * * @var int **/ var $slowRate = 20; /** * Gets the connection names that should have logs + dumps generated. * * @param string $controller * @access public * @return void */ function beforeRender(&$controller) { if (!class_exists('ConnectionManager')) { return array(); } $connections = array(); $dbConfigs = ConnectionManager::sourceList(); foreach ($dbConfigs as $configName) { $driver = null; $db =& ConnectionManager::getDataSource($configName); if ( (empty($db->config['driver']) && empty($db->config['datasource'])) || !$db->isInterfaceSupported('getLog') ) { continue; } if (isset($db->config['driver'])) { $driver = $db->config['driver']; } if (empty($driver) && isset($db->config['datasource'])) { $driver = $db->config['datasource']; } $explain = false; $isExplainable = ($driver === 'mysql' || $driver === 'mysqli' || $driver === 'postgres'); if ($isExplainable) { $explain = true; } $connections[$configName] = $explain; } return array('connections' => $connections, 'threshold' => $this->slowRate); } } /** * Log Panel - Reads log entries made this request. * * @package cake.debug_kit.panels */ class LogPanel extends DebugPanel { var $plugin = 'debug_kit'; /** * Constructor - sets up the log listener. * * @return void */ function __construct($settings) { parent::__construct(); if (!class_exists('CakeLog')) { App::import('Core', 'CakeLog'); } $existing = CakeLog::configured(); if (empty($existing)) { CakeLog::config('default', array( 'engine' => 'FileLog' )); } CakeLog::config('debug_kit_log_panel', array( 'engine' => 'DebugKitLogListener', 'panel' => $this )); } /** * beforeRender Callback * * @return array **/ function beforeRender(&$controller) { $logs = $this->logger->logs; return $logs; } } /** * A CakeLog listener which saves having to munge files or other configured loggers. * * @package debug_kit.components */ class DebugKitLogListener { var $logs = array(); /** * Makes the reverse link needed to get the logs later. * * @return void */ function DebugKitLogListener($options) { $options['panel']->logger =& $this; } /** * Captures log messages in memory * * @return void */ function write($type, $message) { if (!isset($this->logs[$type])) { $this->logs[$type] = array(); } $this->logs[$type][] = array(date('Y-m-d H:i:s'), $message); } }