* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * sfWebDebug creates debug information for easy debugging in the browser. * * @package symfony * @subpackage debug * @author Fabien Potencier * @version SVN: $Id: sfWebDebug.class.php 4641 2007-07-17 11:19:54Z fabien $ */ class sfWebDebug { protected $log = array(), $short_log = array(), $max_priority = 1000, $types = array(), $last_time_log = -1; protected static $instance = null; public function initialize() { } /** * Retrieves the singleton instance of this class. * * @return sfWebDebug A sfWebDebug implementation instance */ public static function getInstance() { if (!isset(self::$instance)) { $class = __CLASS__; self::$instance = new $class(); self::$instance->initialize(); } return self::$instance; } /** * Registers javascripts and stylesheets needed for the web debug toolbar. */ public function registerAssets() { $response = sfContext::getInstance()->getResponse(); // register our css and js $response->addJavascript(sfConfig::get('sf_web_debug_web_dir').'/js/main'); $response->addStylesheet(sfConfig::get('sf_web_debug_web_dir').'/css/main'); } /** * Logs a short message to be displayed in the web debug toolbar. * * @param string The message string */ public function logShortMessage($message) { $this->short_log[] = $message; } /** * Logs a message to the web debug toolbar. * * @param array An array of parameter * * @see sfWebDebugLogger */ public function log($logEntry) { // elapsed time if ($this->last_time_log == -1) { $this->last_time_log = sfConfig::get('sf_timer_start'); } $this->last_time_log = microtime(true); // update max priority if ($logEntry['priority'] < $this->max_priority) { $this->max_priority = $logEntry['priority']; } // update types if (!isset($this->types[$logEntry['type']])) { $this->types[$logEntry['type']] = 1; } else { ++$this->types[$logEntry['type']]; } $this->log[] = $logEntry; } /** * Loads helpers needed for the web debug toolbar. */ protected function loadHelpers() { sfLoader::loadHelpers(array('Helper', 'Url', 'Asset', 'Tag')); } /** * Formats a log line. * * @param string The log line to format * * @return string The formatted log lin */ protected function formatLogLine($log_line) { static $constants; if (!$constants) { foreach (array('sf_app_dir', 'sf_root_dir', 'sf_symfony_lib_dir', 'sf_symfony_data_dir') as $constant) { $constants[realpath(sfConfig::get($constant)).DIRECTORY_SEPARATOR] = $constant.DIRECTORY_SEPARATOR; } } // escape HTML $log_line = htmlentities($log_line, ENT_QUOTES, sfConfig::get('sf_charset')); // replace constants value with constant name $log_line = str_replace(array_keys($constants), array_values($constants), $log_line); $log_line = sfToolkit::pregtr($log_line, array('/"(.+?)"/s' => '"\\1"', '/^(.+?)\(\)\:/S' => '\\1():', '/line (\d+)$/' => 'line \\1')); // special formatting for SQL lines $log_line = preg_replace('/\b(SELECT|FROM|AS|LIMIT|ASC|COUNT|DESC|WHERE|LEFT JOIN|INNER JOIN|RIGHT JOIN|ORDER BY|GROUP BY|IN|LIKE|DISTINCT|DELETE|INSERT|INTO|VALUES)\b/', '\\1', $log_line); // remove username/password from DSN if (strpos($log_line, 'DSN') !== false) { $log_line = preg_replace("/=>\s+'?[^'\s,]+'?/", "=> '****'", $log_line); } return $log_line; } /** * Returns the web debug toolbar as HTML. * * @return string The web debug toolbar HTML */ public function getResults() { if (!sfConfig::get('sf_web_debug')) { return ''; } $this->loadHelpers(); $result = ''; // max priority $max_priority = ''; if (sfConfig::get('sf_logging_enabled')) { $max_priority = $this->getPriority($this->max_priority); } $logs = ''; $sql_logs = array(); if (sfConfig::get('sf_logging_enabled')) { $logs = ''."\n"; $line_nb = 0; foreach ($this->log as $logEntry) { $log = $logEntry['message']; $priority = $this->getPriority($logEntry['priority']); if (strpos($type = $logEntry['type'], 'sf') === 0) { $type = substr($type, 2); } // xdebug information $debug_info = ''; if ($logEntry['debugStack']) { $debug_info .= ' '.image_tag(sfConfig::get('sf_web_debug_web_dir').'/images/toggle.gif').'\n"; } // format log $log = $this->formatLogLine($log); // sql queries log if (preg_match('/execute(?:Query|Update).+?\:\s+(.+)$/', $log, $match)) { $sql_logs[] .= $match[1]; } ++$line_nb; $logs .= sprintf("\n", ucfirst($priority), $logEntry['type'], $line_nb, image_tag(sfConfig::get('sf_web_debug_web_dir').'/images/'.$priority.'.png'), $type, $log, $debug_info ); } $logs .= '
# type message
%s%s %s%s%s
'; ksort($this->types); $types = array(); foreach ($this->types as $type => $nb) { $types[] = ''.$type.''; } } // ignore cache link $cacheLink = ''; if (sfConfig::get('sf_debug') && sfConfig::get('sf_cache')) { $self_url = $_SERVER['PHP_SELF'].((strpos($_SERVER['PHP_SELF'], '_sf_ignore_cache') === false) ? '?_sf_ignore_cache=1' : ''); $cacheLink = '
  • '.image_tag(sfConfig::get('sf_web_debug_web_dir').'/images/reload.png').'
  • '; } // logging information $logLink = ''; if (sfConfig::get('sf_logging_enabled')) { $logLink = '
  • '.image_tag(sfConfig::get('sf_web_debug_web_dir').'/images/comment.png').' logs & msgs
  • '; } // database information $dbInfo = ''; $dbInfoDetails = ''; if ($sql_logs) { $dbInfo = '
  • '.image_tag(sfConfig::get('sf_web_debug_web_dir').'/images/database.png').' '.count($sql_logs).'
  • '; $dbInfoDetails = '
    1. '.implode("
    2. \n
    3. ", $sql_logs).'
    '; } // memory used $memoryInfo = ''; if (sfConfig::get('sf_debug') && function_exists('memory_get_usage')) { $total_memory = sprintf('%.1f', (memory_get_usage() / 1024)); $memoryInfo = '
  • '.image_tag(sfConfig::get('sf_web_debug_web_dir').'/images/memory.png').' '.$total_memory.' KB
  • '; } // total time elapsed $timeInfo = ''; if (sfConfig::get('sf_debug')) { $total_time = (microtime(true) - sfConfig::get('sf_timer_start')) * 1000; $total_time = sprintf(($total_time <= 1) ? '%.2f' : '%.0f', $total_time); $timeInfo = '
  • '.image_tag(sfConfig::get('sf_web_debug_web_dir').'/images/time.png').' '.$total_time.' ms
  • '; } // timers $timeInfoDetails = ''; foreach (sfTimerManager::getTimers() as $name => $timer) { $timeInfoDetails .= sprintf('', $name, $timer->getCalls(), $timer->getElapsedTime() * 1000); } $timeInfoDetails .= '
    typecallstime (ms)
    %s%d%.2f
    '; // short log messages $short_messages = ''; if ($this->short_log) { $short_messages = ''; } // logs $logInfo = ''; if (sfConfig::get('sf_logging_enabled')) { $logInfo .= $short_messages.'
    '.$logs.'
    '; } $result .= '
    '; return $result; } /** * Returns the current configuration as HTML. * * @return string The current configuration as HTML */ protected function getCurrentConfigAsHtml() { $config = array( 'debug' => sfConfig::get('sf_debug') ? 'on' : 'off', 'xdebug' => (extension_loaded('xdebug')) ? 'on' : 'off', 'logging' => sfConfig::get('sf_logging_enabled') ? 'on' : 'off', 'cache' => sfConfig::get('sf_cache') ? 'on' : 'off', 'eaccelerator' => (extension_loaded('eaccelerator') && ini_get('eaccelerator.enable')) ? 'on' : 'off', 'apc' => (extension_loaded('apc') && ini_get('apc.enabled')) ? 'on' : 'off', 'xcache' => (extension_loaded('xcache') && ini_get('xcache.cacher')) ? 'on' : 'off', 'compression' => sfConfig::get('sf_compressed') ? 'on' : 'off', 'syck' => (extension_loaded('syck')) ? 'on' : 'off', ); $result = ''; $context = sfContext::getInstance(); $result .= $this->formatArrayAsHtml('request', sfDebug::requestAsArray($context->getRequest())); $result .= $this->formatArrayAsHtml('response', sfDebug::responseAsArray($context->getResponse())); $result .= $this->formatArrayAsHtml('settings', sfDebug::settingsAsArray()); $result .= $this->formatArrayAsHtml('globals', sfDebug::globalsAsArray()); $result .= $this->formatArrayAsHtml('php', sfDebug::phpInfoAsArray()); return $result; } /** * Converts an array to HTML. * * @param string The identifier to use * @param array The array of values * * @return string An HTML string */ protected function formatArrayAsHtml($id, $values) { $id = ucfirst(strtolower($id)); $content = '

    '.$id.' '.image_tag(sfConfig::get('sf_web_debug_web_dir').'/images/toggle.gif').'

    '; return $content; } /** * Decorates a chunk of HTML with cache information. * * @param string The internalUri representing the content * @param string The HTML content * @param boolean true if the content is new in the cache, false otherwise * * @return string The decorated HTML string */ public function decorateContentWithDebug($internalUri, $content, $new = false) { $context = sfContext::getInstance(); // don't decorate if not html or if content is null if (!sfConfig::get('sf_web_debug') || !$content || false === strpos($context->getResponse()->getContentType(), 'html')) { return $content; } $cache = $context->getViewCacheManager(); $this->loadHelpers(); $bg_color = $new ? '#9ff' : '#ff9'; $last_modified = $cache->lastModified($internalUri); $id = md5($internalUri); $content = '
    cache information '.image_tag(sfConfig::get('sf_web_debug_web_dir').'/images/close.png').' 
    '.$content.'
    '; return $content; } /** * Converts a proprity value to a string. * * @param integer The priority value * * @return string The priority as a string */ protected function getPriority($value) { if ($value >= 6) { return 'info'; } else if ($value >= 4) { return 'warning'; } else { return 'error'; } } }