First commit

This commit is contained in:
Pierre Hubert
2016-11-19 12:08:12 +01:00
commit 990540b2b9
4706 changed files with 931207 additions and 0 deletions

View File

@ -0,0 +1,75 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\Bridge;
use CacheCache\Cache;
use CacheCache\LoggingBackend;
use Monolog\Logger;
/**
* Collects CacheCache operations
*
* http://maximebf.github.io/CacheCache/
*
* Example:
* <code>
* $debugbar->addCollector(new CacheCacheCollector(CacheManager::get('default')));
* // or
* $debugbar->addCollector(new CacheCacheCollector());
* $debugbar['cache']->addCache(CacheManager::get('default'));
* </code>
*/
class CacheCacheCollector extends MonologCollector
{
protected $logger;
/**
* CacheCacheCollector constructor.
* @param Cache|null $cache
* @param Logger|null $logger
* @param bool $level
* @param bool $bubble
*/
public function __construct(Cache $cache = null, Logger $logger = null, $level = Logger::DEBUG, $bubble = true)
{
parent::__construct(null, $level, $bubble);
if ($logger === null) {
$logger = new Logger('Cache');
}
$this->logger = $logger;
if ($cache !== null) {
$this->addCache($cache);
}
}
/**
* @param Cache $cache
*/
public function addCache(Cache $cache)
{
$backend = $cache->getBackend();
if (!($backend instanceof LoggingBackend)) {
$backend = new LoggingBackend($backend, $this->logger);
}
$cache->setBackend($backend);
$this->addLogger($backend->getLogger());
}
/**
* @return string
*/
public function getName()
{
return 'cache';
}
}

View File

@ -0,0 +1,115 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\Bridge;
use DebugBar\DataCollector\AssetProvider;
use DebugBar\DataCollector\DataCollector;
use DebugBar\DataCollector\Renderable;
use DebugBar\DebugBarException;
use Doctrine\DBAL\Logging\DebugStack;
use Doctrine\ORM\EntityManager;
/**
* Collects Doctrine queries
*
* http://doctrine-project.org
*
* Uses the DebugStack logger to collects data about queries
*
* <code>
* $debugStack = new Doctrine\DBAL\Logging\DebugStack();
* $entityManager->getConnection()->getConfiguration()->setSQLLogger($debugStack);
* $debugbar->addCollector(new DoctrineCollector($debugStack));
* </code>
*/
class DoctrineCollector extends DataCollector implements Renderable, AssetProvider
{
protected $debugStack;
/**
* DoctrineCollector constructor.
* @param $debugStackOrEntityManager
* @throws DebugBarException
*/
public function __construct($debugStackOrEntityManager)
{
if ($debugStackOrEntityManager instanceof EntityManager) {
$debugStackOrEntityManager = $debugStackOrEntityManager->getConnection()->getConfiguration()->getSQLLogger();
}
if (!($debugStackOrEntityManager instanceof DebugStack)) {
throw new DebugBarException("'DoctrineCollector' requires an 'EntityManager' or 'DebugStack' object");
}
$this->debugStack = $debugStackOrEntityManager;
}
/**
* @return array
*/
public function collect()
{
$queries = array();
$totalExecTime = 0;
foreach ($this->debugStack->queries as $q) {
$queries[] = array(
'sql' => $q['sql'],
'params' => (object) $q['params'],
'duration' => $q['executionMS'],
'duration_str' => $this->formatDuration($q['executionMS'])
);
$totalExecTime += $q['executionMS'];
}
return array(
'nb_statements' => count($queries),
'accumulated_duration' => $totalExecTime,
'accumulated_duration_str' => $this->formatDuration($totalExecTime),
'statements' => $queries
);
}
/**
* @return string
*/
public function getName()
{
return 'doctrine';
}
/**
* @return array
*/
public function getWidgets()
{
return array(
"database" => array(
"icon" => "arrow-right",
"widget" => "PhpDebugBar.Widgets.SQLQueriesWidget",
"map" => "doctrine",
"default" => "[]"
),
"database:badge" => array(
"map" => "doctrine.nb_statements",
"default" => 0
)
);
}
/**
* @return array
*/
public function getAssets()
{
return array(
'css' => 'widgets/sqlqueries/widget.css',
'js' => 'widgets/sqlqueries/widget.js'
);
}
}

View File

@ -0,0 +1,118 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\Bridge;
use DebugBar\DataCollector\DataCollectorInterface;
use DebugBar\DataCollector\MessagesAggregateInterface;
use DebugBar\DataCollector\Renderable;
use Monolog\Handler\AbstractProcessingHandler;
use Monolog\Logger;
/**
* A monolog handler as well as a data collector
*
* https://github.com/Seldaek/monolog
*
* <code>
* $debugbar->addCollector(new MonologCollector($logger));
* </code>
*/
class MonologCollector extends AbstractProcessingHandler implements DataCollectorInterface, Renderable, MessagesAggregateInterface
{
protected $name;
protected $records = array();
/**
* @param Logger $logger
* @param int $level
* @param boolean $bubble
* @param string $name
*/
public function __construct(Logger $logger = null, $level = Logger::DEBUG, $bubble = true, $name = 'monolog')
{
parent::__construct($level, $bubble);
$this->name = $name;
if ($logger !== null) {
$this->addLogger($logger);
}
}
/**
* Adds logger which messages you want to log
*
* @param Logger $logger
*/
public function addLogger(Logger $logger)
{
$logger->pushHandler($this);
}
/**
* @param array $record
*/
protected function write(array $record)
{
$this->records[] = array(
'message' => $record['formatted'],
'is_string' => true,
'label' => strtolower($record['level_name']),
'time' => $record['datetime']->format('U')
);
}
/**
* @return array
*/
public function getMessages()
{
return $this->records;
}
/**
* @return array
*/
public function collect()
{
return array(
'count' => count($this->records),
'records' => $this->records
);
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return array
*/
public function getWidgets()
{
$name = $this->getName();
return array(
$name => array(
"icon" => "suitcase",
"widget" => "PhpDebugBar.Widgets.MessagesWidget",
"map" => "$name.records",
"default" => "[]"
),
"$name:badge" => array(
"map" => "$name.count",
"default" => "null"
)
);
}
}

View File

@ -0,0 +1,307 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\Bridge;
use DebugBar\DataCollector\AssetProvider;
use DebugBar\DataCollector\DataCollector;
use DebugBar\DataCollector\Renderable;
use Monolog\Handler\TestHandler;
use Monolog\Logger;
use Propel\Runtime\Connection\ConnectionInterface;
use Propel\Runtime\Connection\ProfilerConnectionWrapper;
use Propel\Runtime\Propel;
use Psr\Log\LogLevel;
use Psr\Log\LoggerInterface;
/**
* A Propel logger which acts as a data collector
*
* http://propelorm.org/
*
* Will log queries and display them using the SQLQueries widget.
*
* Example:
* <code>
* $debugbar->addCollector(new \DebugBar\Bridge\Propel2Collector(\Propel\Runtime\Propel::getServiceContainer()->getReadConnection()));
* </code>
*/
class Propel2Collector extends DataCollector implements Renderable, AssetProvider
{
/**
* @var null|TestHandler
*/
protected $handler = null;
/**
* @var null|Logger
*/
protected $logger = null;
/**
* @var array
*/
protected $config = array();
/**
* @var array
*/
protected $errors = array();
/**
* @var int
*/
protected $queryCount = 0;
/**
* @param ConnectionInterface $connection Propel connection
*/
public function __construct(
ConnectionInterface $connection,
array $logMethods = array(
'beginTransaction',
'commit',
'rollBack',
'forceRollBack',
'exec',
'query',
'execute'
)
) {
if ($connection instanceof ProfilerConnectionWrapper) {
$connection->setLogMethods($logMethods);
$this->config = $connection->getProfiler()->getConfiguration();
$this->handler = new TestHandler();
if ($connection->getLogger() instanceof Logger) {
$this->logger = $connection->getLogger();
$this->logger->pushHandler($this->handler);
} else {
$this->errors[] = 'Supported only monolog logger';
}
} else {
$this->errors[] = 'You need set ProfilerConnectionWrapper';
}
}
/**
* @return TestHandler|null
*/
public function getHandler()
{
return $this->handler;
}
/**
* @return array
*/
public function getConfig()
{
return $this->config;
}
/**
* @return Logger|null
*/
public function getLogger()
{
return $this->logger;
}
/**
* @return LoggerInterface
*/
protected function getDefaultLogger()
{
return Propel::getServiceContainer()->getLogger();
}
/**
* @return int
*/
protected function getQueryCount()
{
return $this->queryCount;
}
/**
* @param array $records
* @param array $config
* @return array
*/
protected function getStatements($records, $config)
{
$statements = array();
foreach ($records as $record) {
$duration = null;
$memory = null;
$isSuccess = ( LogLevel::INFO === strtolower($record['level_name']) );
$detailsCount = count($config['details']);
$parameters = explode($config['outerGlue'], $record['message'], $detailsCount + 1);
if (count($parameters) === ($detailsCount + 1)) {
$parameters = array_map('trim', $parameters);
$_details = array();
foreach (array_splice($parameters, 0, $detailsCount) as $string) {
list($key, $value) = array_map('trim', explode($config['innerGlue'], $string, 2));
$_details[$key] = $value;
}
$details = array();
foreach ($config['details'] as $key => $detail) {
if (isset($_details[$detail['name']])) {
$value = $_details[$detail['name']];
if ('time' === $key) {
if (substr_count($value, 'ms')) {
$value = (float)$value / 1000;
} else {
$value = (float)$value;
}
} else {
$suffixes = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
$suffix = substr($value, -2);
$i = array_search($suffix, $suffixes, true);
$i = (false === $i) ? 0 : $i;
$value = ((float)$value) * pow(1024, $i);
}
$details[$key] = $value;
}
}
if (isset($details['time'])) {
$duration = $details['time'];
}
if (isset($details['memDelta'])) {
$memory = $details['memDelta'];
}
$message = end($parameters);
if ($isSuccess) {
$this->queryCount++;
}
} else {
$message = $record['message'];
}
$statement = array(
'sql' => $message,
'is_success' => $isSuccess,
'duration' => $duration,
'duration_str' => $this->getDataFormatter()->formatDuration($duration),
'memory' => $memory,
'memory_str' => $this->getDataFormatter()->formatBytes($memory),
);
if (false === $isSuccess) {
$statement['sql'] = '';
$statement['error_code'] = $record['level'];
$statement['error_message'] = $message;
}
$statements[] = $statement;
}
return $statements;
}
/**
* @return array
*/
public function collect()
{
if (count($this->errors)) {
return array(
'statements' => array_map(function ($message) {
return array('sql' => '', 'is_success' => false, 'error_code' => 500, 'error_message' => $message);
}, $this->errors),
'nb_statements' => 0,
'nb_failed_statements' => count($this->errors),
);
}
if ($this->getHandler() === null) {
return array();
}
$statements = $this->getStatements($this->getHandler()->getRecords(), $this->getConfig());
$failedStatement = count(array_filter($statements, function ($statement) {
return false === $statement['is_success'];
}));
$accumulatedDuration = array_reduce($statements, function ($accumulatedDuration, $statement) {
$time = isset($statement['duration']) ? $statement['duration'] : 0;
return $accumulatedDuration += $time;
});
$memoryUsage = array_reduce($statements, function ($memoryUsage, $statement) {
$time = isset($statement['memory']) ? $statement['memory'] : 0;
return $memoryUsage += $time;
});
return array(
'nb_statements' => $this->getQueryCount(),
'nb_failed_statements' => $failedStatement,
'accumulated_duration' => $accumulatedDuration,
'accumulated_duration_str' => $this->getDataFormatter()->formatDuration($accumulatedDuration),
'memory_usage' => $memoryUsage,
'memory_usage_str' => $this->getDataFormatter()->formatBytes($memoryUsage),
'statements' => $statements
);
}
/**
* @return string
*/
public function getName()
{
$additionalName = '';
if ($this->getLogger() !== $this->getDefaultLogger()) {
$additionalName = ' ('.$this->getLogger()->getName().')';
}
return 'propel2'.$additionalName;
}
/**
* @return array
*/
public function getWidgets()
{
return array(
$this->getName() => array(
'icon' => 'bolt',
'widget' => 'PhpDebugBar.Widgets.SQLQueriesWidget',
'map' => $this->getName(),
'default' => '[]'
),
$this->getName().':badge' => array(
'map' => $this->getName().'.nb_statements',
'default' => 0
),
);
}
/**
* @return array
*/
public function getAssets()
{
return array(
'css' => 'widgets/sqlqueries/widget.css',
'js' => 'widgets/sqlqueries/widget.js'
);
}
}

View File

@ -0,0 +1,253 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\Bridge;
use BasicLogger;
use DebugBar\DataCollector\AssetProvider;
use DebugBar\DataCollector\DataCollector;
use DebugBar\DataCollector\Renderable;
use Propel;
use PropelConfiguration;
use PropelPDO;
use Psr\Log\LogLevel;
use Psr\Log\LoggerInterface;
/**
* A Propel logger which acts as a data collector
*
* http://propelorm.org/
*
* Will log queries and display them using the SQLQueries widget.
* You can provide a LoggerInterface object to forward non-query related message to.
*
* Example:
* <code>
* $debugbar->addCollector(new PropelCollector($debugbar['messages']));
* PropelCollector::enablePropelProfiling();
* </code>
*/
class PropelCollector extends DataCollector implements BasicLogger, Renderable, AssetProvider
{
protected $logger;
protected $statements = array();
protected $accumulatedTime = 0;
protected $peakMemory = 0;
/**
* Sets the needed configuration option in propel to enable query logging
*
* @param PropelConfiguration $config Apply profiling on a specific config
*/
public static function enablePropelProfiling(PropelConfiguration $config = null)
{
if ($config === null) {
$config = Propel::getConfiguration(PropelConfiguration::TYPE_OBJECT);
}
$config->setParameter('debugpdo.logging.details.method.enabled', true);
$config->setParameter('debugpdo.logging.details.time.enabled', true);
$config->setParameter('debugpdo.logging.details.mem.enabled', true);
$allMethods = array(
'PropelPDO::__construct', // logs connection opening
'PropelPDO::__destruct', // logs connection close
'PropelPDO::exec', // logs a query
'PropelPDO::query', // logs a query
'PropelPDO::beginTransaction', // logs a transaction begin
'PropelPDO::commit', // logs a transaction commit
'PropelPDO::rollBack', // logs a transaction rollBack (watch out for the capital 'B')
'DebugPDOStatement::execute', // logs a query from a prepared statement
);
$config->setParameter('debugpdo.logging.methods', $allMethods, false);
}
/**
* @param LoggerInterface $logger A logger to forward non-query log lines to
* @param PropelPDO $conn Bound this collector to a connection only
*/
public function __construct(LoggerInterface $logger = null, PropelPDO $conn = null)
{
if ($conn) {
$conn->setLogger($this);
} else {
Propel::setLogger($this);
}
$this->logger = $logger;
$this->logQueriesToLogger = false;
}
public function setLogQueriesToLogger($enable = true)
{
$this->logQueriesToLogger = $enable;
return $this;
}
public function isLogQueriesToLogger()
{
return $this->logQueriesToLogger;
}
public function emergency($m)
{
$this->log($m, Propel::LOG_EMERG);
}
public function alert($m)
{
$this->log($m, Propel::LOG_ALERT);
}
public function crit($m)
{
$this->log($m, Propel::LOG_CRIT);
}
public function err($m)
{
$this->log($m, Propel::LOG_ERR);
}
public function warning($m)
{
$this->log($m, Propel::LOG_WARNING);
}
public function notice($m)
{
$this->log($m, Propel::LOG_NOTICE);
}
public function info($m)
{
$this->log($m, Propel::LOG_INFO);
}
public function debug($m)
{
$this->log($m, Propel::LOG_DEBUG);
}
public function log($message, $severity = null)
{
if (strpos($message, 'DebugPDOStatement::execute') !== false) {
list($sql, $duration_str) = $this->parseAndLogSqlQuery($message);
if (!$this->logQueriesToLogger) {
return;
}
$message = "$sql ($duration_str)";
}
if ($this->logger !== null) {
$this->logger->log($this->convertLogLevel($severity), $message);
}
}
/**
* Converts Propel log levels to PSR log levels
*
* @param int $level
* @return string
*/
protected function convertLogLevel($level)
{
$map = array(
Propel::LOG_EMERG => LogLevel::EMERGENCY,
Propel::LOG_ALERT => LogLevel::ALERT,
Propel::LOG_CRIT => LogLevel::CRITICAL,
Propel::LOG_ERR => LogLevel::ERROR,
Propel::LOG_WARNING => LogLevel::WARNING,
Propel::LOG_NOTICE => LogLevel::NOTICE,
Propel::LOG_DEBUG => LogLevel::DEBUG
);
return $map[$level];
}
/**
* Parse a log line to extract query information
*
* @param string $message
*/
protected function parseAndLogSqlQuery($message)
{
$parts = explode('|', $message, 4);
$sql = trim($parts[3]);
$duration = 0;
if (preg_match('/([0-9]+\.[0-9]+)/', $parts[1], $matches)) {
$duration = (float) $matches[1];
}
$memory = 0;
if (preg_match('/([0-9]+\.[0-9]+) ([A-Z]{1,2})/', $parts[2], $matches)) {
$memory = (float) $matches[1];
if ($matches[2] == 'KB') {
$memory *= 1024;
} elseif ($matches[2] == 'MB') {
$memory *= 1024 * 1024;
}
}
$this->statements[] = array(
'sql' => $sql,
'is_success' => true,
'duration' => $duration,
'duration_str' => $this->formatDuration($duration),
'memory' => $memory,
'memory_str' => $this->formatBytes($memory)
);
$this->accumulatedTime += $duration;
$this->peakMemory = max($this->peakMemory, $memory);
return array($sql, $this->formatDuration($duration));
}
public function collect()
{
return array(
'nb_statements' => count($this->statements),
'nb_failed_statements' => 0,
'accumulated_duration' => $this->accumulatedTime,
'accumulated_duration_str' => $this->formatDuration($this->accumulatedTime),
'peak_memory_usage' => $this->peakMemory,
'peak_memory_usage_str' => $this->formatBytes($this->peakMemory),
'statements' => $this->statements
);
}
public function getName()
{
return 'propel';
}
public function getWidgets()
{
return array(
"propel" => array(
"icon" => "bolt",
"widget" => "PhpDebugBar.Widgets.SQLQueriesWidget",
"map" => "propel",
"default" => "[]"
),
"propel:badge" => array(
"map" => "propel.nb_statements",
"default" => 0
)
);
}
public function getAssets()
{
return array(
'css' => 'widgets/sqlqueries/widget.css',
'js' => 'widgets/sqlqueries/widget.js'
);
}
}

View File

@ -0,0 +1,66 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\Bridge;
use DebugBar\DataCollector\MessagesCollector;
use Psr\Log\LogLevel;
use Slim\Log;
use Slim\Slim;
/**
* Collects messages from a Slim logger
*
* http://slimframework.com
*/
class SlimCollector extends MessagesCollector
{
protected $slim;
protected $originalLogWriter;
public function __construct(Slim $slim)
{
$this->slim = $slim;
if ($log = $slim->getLog()) {
$this->originalLogWriter = $log->getWriter();
$log->setWriter($this);
$log->setEnabled(true);
}
}
public function write($message, $level)
{
if ($this->originalLogWriter) {
$this->originalLogWriter->write($message, $level);
}
$this->addMessage($message, $this->getLevelName($level));
}
protected function getLevelName($level)
{
$map = array(
Log::EMERGENCY => LogLevel::EMERGENCY,
Log::ALERT => LogLevel::ALERT,
Log::CRITICAL => LogLevel::CRITICAL,
Log::ERROR => LogLevel::ERROR,
Log::WARN => LogLevel::WARNING,
Log::NOTICE => LogLevel::NOTICE,
Log::INFO => LogLevel::INFO,
Log::DEBUG => LogLevel::DEBUG
);
return $map[$level];
}
public function getName()
{
return 'slim';
}
}

View File

@ -0,0 +1,44 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\Bridge\SwiftMailer;
use DebugBar\DataCollector\MessagesCollector;
use Swift_Mailer;
use Swift_Plugins_Logger;
use Swift_Plugins_LoggerPlugin;
/**
* Collects log messages
*
* http://swiftmailer.org/
*/
class SwiftLogCollector extends MessagesCollector implements Swift_Plugins_Logger
{
public function __construct(Swift_Mailer $mailer)
{
$mailer->registerPlugin(new Swift_Plugins_LoggerPlugin($this));
}
public function add($entry)
{
$this->addMessage($entry);
}
public function dump()
{
return implode(PHP_EOL, $this->_log);
}
public function getName()
{
return 'swiftmailer_logs';
}
}

View File

@ -0,0 +1,92 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\Bridge\SwiftMailer;
use DebugBar\DataCollector\AssetProvider;
use DebugBar\DataCollector\DataCollector;
use DebugBar\DataCollector\Renderable;
use Swift_Mailer;
use Swift_Plugins_MessageLogger;
/**
* Collects data about sent mails
*
* http://swiftmailer.org/
*/
class SwiftMailCollector extends DataCollector implements Renderable, AssetProvider
{
protected $messagesLogger;
public function __construct(Swift_Mailer $mailer)
{
$this->messagesLogger = new Swift_Plugins_MessageLogger();
$mailer->registerPlugin($this->messagesLogger);
}
public function collect()
{
$mails = array();
foreach ($this->messagesLogger->getMessages() as $msg) {
$mails[] = array(
'to' => $this->formatTo($msg->getTo()),
'subject' => $msg->getSubject(),
'headers' => $msg->getHeaders()->toString()
);
}
return array(
'count' => count($mails),
'mails' => $mails
);
}
protected function formatTo($to)
{
if (!$to) {
return '';
}
$f = array();
foreach ($to as $k => $v) {
$f[] = (empty($v) ? '' : "$v ") . "<$k>";
}
return implode(', ', $f);
}
public function getName()
{
return 'swiftmailer_mails';
}
public function getWidgets()
{
return array(
'emails' => array(
'icon' => 'inbox',
'widget' => 'PhpDebugBar.Widgets.MailsWidget',
'map' => 'swiftmailer_mails.mails',
'default' => '[]',
'title' => 'Mails'
),
'emails:badge' => array(
'map' => 'swiftmailer_mails.count',
'default' => 'null'
)
);
}
public function getAssets()
{
return array(
'css' => 'widgets/mails/widget.css',
'js' => 'widgets/mails/widget.js'
);
}
}

View File

@ -0,0 +1,417 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\Bridge\Twig;
use DebugBar\DataCollector\TimeDataCollector;
use Twig_CompilerInterface;
use Twig_Environment;
use Twig_ExtensionInterface;
use Twig_LexerInterface;
use Twig_LoaderInterface;
use Twig_NodeInterface;
use Twig_NodeVisitorInterface;
use Twig_ParserInterface;
use Twig_TokenParserInterface;
use Twig_TokenStream;
/**
* Wrapped a Twig Environment to provide profiling features
*/
class TraceableTwigEnvironment extends Twig_Environment
{
protected $twig;
protected $renderedTemplates = array();
protected $timeDataCollector;
/**
* @param Twig_Environment $twig
* @param TimeDataCollector $timeDataCollector
*/
public function __construct(Twig_Environment $twig, TimeDataCollector $timeDataCollector = null)
{
$this->twig = $twig;
$this->timeDataCollector = $timeDataCollector;
}
public function __call($name, $arguments)
{
return call_user_func_array(array($this->twig, $name), $arguments);
}
public function getRenderedTemplates()
{
return $this->renderedTemplates;
}
public function addRenderedTemplate(array $info)
{
$this->renderedTemplates[] = $info;
}
public function getTimeDataCollector()
{
return $this->timeDataCollector;
}
public function getBaseTemplateClass()
{
return $this->twig->getBaseTemplateClass();
}
public function setBaseTemplateClass($class)
{
$this->twig->setBaseTemplateClass($class);
}
public function enableDebug()
{
$this->twig->enableDebug();
}
public function disableDebug()
{
$this->twig->disableDebug();
}
public function isDebug()
{
return $this->twig->isDebug();
}
public function enableAutoReload()
{
$this->twig->enableAutoReload();
}
public function disableAutoReload()
{
$this->twig->disableAutoReload();
}
public function isAutoReload()
{
return $this->twig->isAutoReload();
}
public function enableStrictVariables()
{
$this->twig->enableStrictVariables();
}
public function disableStrictVariables()
{
$this->twig->disableStrictVariables();
}
public function isStrictVariables()
{
return $this->twig->isStrictVariables();
}
public function getCache($original = true)
{
return $this->twig->getCache($original);
}
public function setCache($cache)
{
$this->twig->setCache($cache);
}
public function getCacheFilename($name)
{
return $this->twig->getCacheFilename($name);
}
public function getTemplateClass($name, $index = null)
{
return $this->twig->getTemplateClass($name, $index);
}
public function getTemplateClassPrefix()
{
return $this->twig->getTemplateClassPrefix();
}
public function render($name, array $context = array())
{
return $this->loadTemplate($name)->render($context);
}
public function display($name, array $context = array())
{
$this->loadTemplate($name)->display($context);
}
public function loadTemplate($name, $index = null)
{
$cls = $this->twig->getTemplateClass($name, $index);
if (isset($this->twig->loadedTemplates[$cls])) {
return $this->twig->loadedTemplates[$cls];
}
if (!class_exists($cls, false)) {
if (false === $cache = $this->getCacheFilename($name)) {
eval('?>'.$this->compileSource($this->getLoader()->getSource($name), $name));
} else {
if (!is_file($cache) || ($this->isAutoReload() && !$this->isTemplateFresh($name, filemtime($cache)))) {
$this->writeCacheFile($cache, $this->compileSource($this->getLoader()->getSource($name), $name));
}
require_once $cache;
}
}
if (!$this->twig->runtimeInitialized) {
$this->initRuntime();
}
return $this->twig->loadedTemplates[$cls] = new TraceableTwigTemplate($this, new $cls($this));
}
public function isTemplateFresh($name, $time)
{
return $this->twig->isTemplateFresh($name, $time);
}
public function resolveTemplate($names)
{
return $this->twig->resolveTemplate($names);
}
public function clearTemplateCache()
{
$this->twig->clearTemplateCache();
}
public function clearCacheFiles()
{
$this->twig->clearCacheFiles();
}
public function getLexer()
{
return $this->twig->getLexer();
}
public function setLexer(Twig_LexerInterface $lexer)
{
$this->twig->setLexer($lexer);
}
public function tokenize($source, $name = null)
{
return $this->twig->tokenize($source, $name);
}
public function getParser()
{
return $this->twig->getParser();
}
public function setParser(Twig_ParserInterface $parser)
{
$this->twig->setParser($parser);
}
public function parse(Twig_TokenStream $tokens)
{
return $this->twig->parse($tokens);
}
public function getCompiler()
{
return $this->twig->getCompiler();
}
public function setCompiler(Twig_CompilerInterface $compiler)
{
$this->twig->setCompiler($compiler);
}
public function compile(Twig_NodeInterface $node)
{
return $this->twig->compile($node);
}
public function compileSource($source, $name = null)
{
return $this->twig->compileSource($source, $name);
}
public function setLoader(Twig_LoaderInterface $loader)
{
$this->twig->setLoader($loader);
}
public function getLoader()
{
return $this->twig->getLoader();
}
public function setCharset($charset)
{
$this->twig->setCharset($charset);
}
public function getCharset()
{
return $this->twig->getCharset();
}
public function initRuntime()
{
$this->twig->initRuntime();
}
public function hasExtension($name)
{
return $this->twig->hasExtension($name);
}
public function getExtension($name)
{
return $this->twig->getExtension($name);
}
public function addExtension(Twig_ExtensionInterface $extension)
{
$this->twig->addExtension($extension);
}
public function removeExtension($name)
{
$this->twig->removeExtension($name);
}
public function setExtensions(array $extensions)
{
$this->twig->setExtensions($extensions);
}
public function getExtensions()
{
return $this->twig->getExtensions();
}
public function addTokenParser(Twig_TokenParserInterface $parser)
{
$this->twig->addTokenParser($parser);
}
public function getTokenParsers()
{
return $this->twig->getTokenParsers();
}
public function getTags()
{
return $this->twig->getTags();
}
public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
{
$this->twig->addNodeVisitor($visitor);
}
public function getNodeVisitors()
{
return $this->twig->getNodeVisitors();
}
public function addFilter($name, $filter = null)
{
$this->twig->addFilter($name, $filter);
}
public function getFilter($name)
{
return $this->twig->getFilter($name);
}
public function registerUndefinedFilterCallback($callable)
{
$this->twig->registerUndefinedFilterCallback($callable);
}
public function getFilters()
{
return $this->twig->getFilters();
}
public function addTest($name, $test = null)
{
$this->twig->addTest($name, $test);
}
public function getTests()
{
return $this->twig->getTests();
}
public function getTest($name)
{
return $this->twig->getTest($name);
}
public function addFunction($name, $function = null)
{
$this->twig->addFunction($name, $function);
}
public function getFunction($name)
{
return $this->twig->getFunction($name);
}
public function registerUndefinedFunctionCallback($callable)
{
$this->twig->registerUndefinedFunctionCallback($callable);
}
public function getFunctions()
{
return $this->twig->getFunctions();
}
public function addGlobal($name, $value)
{
$this->twig->addGlobal($name, $value);
}
public function getGlobals()
{
return $this->twig->getGlobals();
}
public function mergeGlobals(array $context)
{
return $this->twig->mergeGlobals($context);
}
public function getUnaryOperators()
{
return $this->twig->getUnaryOperators();
}
public function getBinaryOperators()
{
return $this->twig->getBinaryOperators();
}
public function computeAlternatives($name, $items)
{
return $this->twig->computeAlternatives($name, $items);
}
}

View File

@ -0,0 +1,131 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\Bridge\Twig;
use Twig_Template;
use Twig_TemplateInterface;
/**
* Wraps a Twig_Template to add profiling features
*/
class TraceableTwigTemplate implements Twig_TemplateInterface
{
protected $template;
/**
* @param TraceableTwigEnvironment $env
* @param Twig_Template $template
*/
public function __construct(TraceableTwigEnvironment $env, Twig_Template $template)
{
$this->env = $env;
$this->template = $template;
}
public function __call($name, $arguments)
{
return call_user_func_array(array($this->template, $name), $arguments);
}
public function getTemplateName()
{
return $this->template->getTemplateName();
}
public function getEnvironment()
{
return $this->template->getEnvironment();
}
public function getParent(array $context)
{
return $this->template->getParent($context);
}
public function isTraitable()
{
return $this->template->isTraitable();
}
public function displayParentBlock($name, array $context, array $blocks = array())
{
$this->template->displayParentBlock($name, $context, $blocks);
}
public function displayBlock($name, array $context, array $blocks = array(), $useBlocks = true)
{
$this->template->displayBlock($name, $context, $blocks, $useBlocks);
}
public function renderParentBlock($name, array $context, array $blocks = array())
{
return $this->template->renderParentBlock($name, $context, $blocks);
}
public function renderBlock($name, array $context, array $blocks = array(), $useBlocks = true)
{
return $this->template->renderBlock($name, $context, $blocks, $useBlocks);
}
public function hasBlock($name)
{
return $this->template->hasBlock($name);
}
public function getBlockNames()
{
return $this->template->getBlockNames();
}
public function getBlocks()
{
return $this->template->getBlocks();
}
public function display(array $context, array $blocks = array())
{
$start = microtime(true);
$this->template->display($context, $blocks);
$end = microtime(true);
if ($timeDataCollector = $this->env->getTimeDataCollector()) {
$name = sprintf("twig.render(%s)", $this->template->getTemplateName());
$timeDataCollector->addMeasure($name, $start, $end);
}
$this->env->addRenderedTemplate(array(
'name' => $this->template->getTemplateName(),
'render_time' => $end - $start
));
}
public function render(array $context)
{
$level = ob_get_level();
ob_start();
try {
$this->display($context);
} catch (Exception $e) {
while (ob_get_level() > $level) {
ob_end_clean();
}
throw $e;
}
return ob_get_clean();
}
public static function clearCache()
{
Twig_Template::clearCache();
}
}

View File

@ -0,0 +1,87 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\Bridge\Twig;
use DebugBar\DataCollector\AssetProvider;
use DebugBar\DataCollector\DataCollector;
use DebugBar\DataCollector\Renderable;
/**
* Collects data about rendered templates
*
* http://twig.sensiolabs.org/
*
* Your Twig_Environment object needs to be wrapped in a
* TraceableTwigEnvironment object
*
* <code>
* $env = new TraceableTwigEnvironment(new Twig_Environment($loader));
* $debugbar->addCollector(new TwigCollector($env));
* </code>
*/
class TwigCollector extends DataCollector implements Renderable, AssetProvider
{
public function __construct(TraceableTwigEnvironment $twig)
{
$this->twig = $twig;
}
public function collect()
{
$templates = array();
$accuRenderTime = 0;
foreach ($this->twig->getRenderedTemplates() as $tpl) {
$accuRenderTime += $tpl['render_time'];
$templates[] = array(
'name' => $tpl['name'],
'render_time' => $tpl['render_time'],
'render_time_str' => $this->formatDuration($tpl['render_time'])
);
}
return array(
'nb_templates' => count($templates),
'templates' => $templates,
'accumulated_render_time' => $accuRenderTime,
'accumulated_render_time_str' => $this->formatDuration($accuRenderTime)
);
}
public function getName()
{
return 'twig';
}
public function getWidgets()
{
return array(
'twig' => array(
'icon' => 'leaf',
'widget' => 'PhpDebugBar.Widgets.TemplatesWidget',
'map' => 'twig',
'default' => '[]'
),
'twig:badge' => array(
'map' => 'twig.nb_templates',
'default' => 0
)
);
}
public function getAssets()
{
return array(
'css' => 'widgets/templates/widget.css',
'js' => 'widgets/templates/widget.js'
);
}
}

View File

@ -0,0 +1,189 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\DataCollector;
use ArrayAccess;
use DebugBar\DebugBarException;
/**
* Aggregates data from multiple collectors
*
* <code>
* $aggcollector = new AggregateCollector('foobar');
* $aggcollector->addCollector(new MessagesCollector('msg1'));
* $aggcollector->addCollector(new MessagesCollector('msg2'));
* $aggcollector['msg1']->addMessage('hello world');
* </code>
*/
class AggregatedCollector implements DataCollectorInterface, ArrayAccess
{
protected $name;
protected $mergeProperty;
protected $sort;
protected $collectors = array();
/**
* @param string $name
* @param string $mergeProperty
* @param boolean $sort
*/
public function __construct($name, $mergeProperty = null, $sort = false)
{
$this->name = $name;
$this->mergeProperty = $mergeProperty;
$this->sort = $sort;
}
/**
* @param DataCollectorInterface $collector
*/
public function addCollector(DataCollectorInterface $collector)
{
$this->collectors[$collector->getName()] = $collector;
}
/**
* @return array
*/
public function getCollectors()
{
return $this->collectors;
}
/**
* Merge data from one of the key/value pair of the collected data
*
* @param string $property
*/
public function setMergeProperty($property)
{
$this->mergeProperty = $property;
}
/**
* @return string
*/
public function getMergeProperty()
{
return $this->mergeProperty;
}
/**
* Sorts the collected data
*
* If true, sorts using sort()
* If it is a string, sorts the data using the value from a key/value pair of the array
*
* @param bool|string $sort
*/
public function setSort($sort)
{
$this->sort = $sort;
}
/**
* @return bool|string
*/
public function getSort()
{
return $this->sort;
}
/**
* @return array
*/
public function collect()
{
$aggregate = array();
foreach ($this->collectors as $collector) {
$data = $collector->collect();
if ($this->mergeProperty !== null) {
$data = $data[$this->mergeProperty];
}
$aggregate = array_merge($aggregate, $data);
}
return $this->sort($aggregate);
}
/**
* Sorts the collected data
*
* @param array $data
* @return array
*/
protected function sort($data)
{
if (is_string($this->sort)) {
$p = $this->sort;
usort($data, function ($a, $b) use ($p) {
if ($a[$p] == $b[$p]) {
return 0;
}
return $a[$p] < $b[$p] ? -1 : 1;
});
} elseif ($this->sort === true) {
sort($data);
}
return $data;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
// --------------------------------------------
// ArrayAccess implementation
/**
* @param mixed $key
* @param mixed $value
* @throws DebugBarException
*/
public function offsetSet($key, $value)
{
throw new DebugBarException("AggregatedCollector[] is read-only");
}
/**
* @param mixed $key
* @return mixed
*/
public function offsetGet($key)
{
return $this->collectors[$key];
}
/**
* @param mixed $key
* @return bool
*/
public function offsetExists($key)
{
return isset($this->collectors[$key]);
}
/**
* @param mixed $key
* @throws DebugBarException
*/
public function offsetUnset($key)
{
throw new DebugBarException("AggregatedCollector[] is read-only");
}
}

View File

@ -0,0 +1,28 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\DataCollector;
/**
* Indicates that a DataCollector provides some assets
*/
interface AssetProvider
{
/**
* Returns an array with the following keys:
* - base_path
* - base_url
* - css: an array of filenames
* - js: an array of filenames
*
* @return array
*/
function getAssets();
}

View File

@ -0,0 +1,80 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\DataCollector;
/**
* Collects array data
*/
class ConfigCollector extends DataCollector implements Renderable
{
protected $name;
protected $data;
/**
* @param array $data
* @param string $name
*/
public function __construct(array $data = array(), $name = 'config')
{
$this->name = $name;
$this->data = $data;
}
/**
* Sets the data
*
* @param array $data
*/
public function setData(array $data)
{
$this->data = $data;
}
/**
* @return array
*/
public function collect()
{
$data = array();
foreach ($this->data as $k => $v) {
if (!is_string($v)) {
$v = $this->getDataFormatter()->formatVar($v);
}
$data[$k] = $v;
}
return $data;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return array
*/
public function getWidgets()
{
$name = $this->getName();
return array(
"$name" => array(
"icon" => "gear",
"widget" => "PhpDebugBar.Widgets.VariableListWidget",
"map" => "$name",
"default" => "{}"
)
);
}
}

View File

@ -0,0 +1,94 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\DataCollector;
use DebugBar\DataFormatter\DataFormatter;
use DebugBar\DataFormatter\DataFormatterInterface;
/**
* Abstract class for data collectors
*/
abstract class DataCollector implements DataCollectorInterface
{
private static $defaultDataFormatter;
protected $dataFormater;
/**
* Sets the default data formater instance used by all collectors subclassing this class
*
* @param DataFormatterInterface $formater
*/
public static function setDefaultDataFormatter(DataFormatterInterface $formater)
{
self::$defaultDataFormatter = $formater;
}
/**
* Returns the default data formater
*
* @return DataFormatterInterface
*/
public static function getDefaultDataFormatter()
{
if (self::$defaultDataFormatter === null) {
self::$defaultDataFormatter = new DataFormatter();
}
return self::$defaultDataFormatter;
}
/**
* Sets the data formater instance used by this collector
*
* @param DataFormatterInterface $formater
* @return $this
*/
public function setDataFormatter(DataFormatterInterface $formater)
{
$this->dataFormater = $formater;
return $this;
}
/**
* @return DataFormatterInterface
*/
public function getDataFormatter()
{
if ($this->dataFormater === null) {
$this->dataFormater = self::getDefaultDataFormatter();
}
return $this->dataFormater;
}
/**
* @deprecated
*/
public function formatVar($var)
{
return $this->getDataFormatter()->formatVar($var);
}
/**
* @deprecated
*/
public function formatDuration($seconds)
{
return $this->getDataFormatter()->formatDuration($seconds);
}
/**
* @deprecated
*/
public function formatBytes($size, $precision = 2)
{
return $this->getDataFormatter()->formatBytes($size, $precision);
}
}

View File

@ -0,0 +1,31 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\DataCollector;
/**
* DataCollector Interface
*/
interface DataCollectorInterface
{
/**
* Called by the DebugBar when data needs to be collected
*
* @return array Collected data
*/
function collect();
/**
* Returns the unique name of the collector
*
* @return string
*/
function getName();
}

View File

@ -0,0 +1,117 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\DataCollector;
use Exception;
/**
* Collects info about exceptions
*/
class ExceptionsCollector extends DataCollector implements Renderable
{
protected $exceptions = array();
protected $chainExceptions = false;
/**
* Adds an exception to be profiled in the debug bar
*
* @param Exception $e
*/
public function addException(Exception $e)
{
$this->exceptions[] = $e;
if ($this->chainExceptions && $previous = $e->getPrevious()) {
$this->addException($previous);
}
}
/**
* Configure whether or not all chained exceptions should be shown.
*
* @param bool $chainExceptions
*/
public function setChainExceptions($chainExceptions = true)
{
$this->chainExceptions = $chainExceptions;
}
/**
* Returns the list of exceptions being profiled
*
* @return array[Exception]
*/
public function getExceptions()
{
return $this->exceptions;
}
public function collect()
{
return array(
'count' => count($this->exceptions),
'exceptions' => array_map(array($this, 'formatExceptionData'), $this->exceptions)
);
}
/**
* Returns exception data as an array
*
* @param Exception $e
* @return array
*/
public function formatExceptionData(Exception $e)
{
$filePath = $e->getFile();
if ($filePath && file_exists($filePath)) {
$lines = file($filePath);
$start = $e->getLine() - 4;
$lines = array_slice($lines, $start < 0 ? 0 : $start, 7);
} else {
$lines = array("Cannot open the file ($filePath) in which the exception occurred ");
}
return array(
'type' => get_class($e),
'message' => $e->getMessage(),
'code' => $e->getCode(),
'file' => $filePath,
'line' => $e->getLine(),
'surrounding_lines' => $lines
);
}
/**
* @return string
*/
public function getName()
{
return 'exceptions';
}
/**
* @return array
*/
public function getWidgets()
{
return array(
'exceptions' => array(
'icon' => 'bug',
'widget' => 'PhpDebugBar.Widgets.ExceptionsWidget',
'map' => 'exceptions.exceptions',
'default' => '[]'
),
'exceptions:badge' => array(
'map' => 'exceptions.count',
'default' => 'null'
)
);
}
}

View File

@ -0,0 +1,73 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\DataCollector;
/**
* Collects info about the current localization state
*/
class LocalizationCollector extends DataCollector implements Renderable
{
/**
* Get the current locale
*
* @return string
*/
public function getLocale()
{
return setlocale(LC_ALL, 0);
}
/**
* Get the current translations domain
*
* @return string
*/
public function getDomain()
{
return textdomain();
}
/**
* @return array
*/
public function collect()
{
return array(
'locale' => $this->getLocale(),
'domain' => $this->getDomain(),
);
}
/**
* @return string
*/
public function getName()
{
return 'localization';
}
/**
* @return array
*/
public function getWidgets()
{
return array(
'domain' => array(
'icon' => 'bookmark',
'map' => 'localization.domain',
),
'locale' => array(
'icon' => 'flag',
'map' => 'localization.locale',
)
);
}
}

View File

@ -0,0 +1,72 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\DataCollector;
/**
* Collects info about memory usage
*/
class MemoryCollector extends DataCollector implements Renderable
{
protected $peakUsage = 0;
/**
* Returns the peak memory usage
*
* @return integer
*/
public function getPeakUsage()
{
return $this->peakUsage;
}
/**
* Updates the peak memory usage value
*/
public function updatePeakUsage()
{
$this->peakUsage = memory_get_peak_usage(true);
}
/**
* @return array
*/
public function collect()
{
$this->updatePeakUsage();
return array(
'peak_usage' => $this->peakUsage,
'peak_usage_str' => $this->getDataFormatter()->formatBytes($this->peakUsage)
);
}
/**
* @return string
*/
public function getName()
{
return 'memory';
}
/**
* @return array
*/
public function getWidgets()
{
return array(
"memory" => array(
"icon" => "cogs",
"tooltip" => "Memory Usage",
"map" => "memory.peak_usage_str",
"default" => "'0B'"
)
);
}
}

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\DataCollector;
interface MessagesAggregateInterface
{
/**
* Returns collected messages
*
* @return array
*/
public function getMessages();
}

View File

@ -0,0 +1,174 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\DataCollector;
use Psr\Log\AbstractLogger;
use DebugBar\DataFormatter\DataFormatterInterface;
/**
* Provides a way to log messages
*/
class MessagesCollector extends AbstractLogger implements DataCollectorInterface, MessagesAggregateInterface, Renderable
{
protected $name;
protected $messages = array();
protected $aggregates = array();
protected $dataFormater;
/**
* @param string $name
*/
public function __construct($name = 'messages')
{
$this->name = $name;
}
/**
* Sets the data formater instance used by this collector
*
* @param DataFormatterInterface $formater
* @return $this
*/
public function setDataFormatter(DataFormatterInterface $formater)
{
$this->dataFormater = $formater;
return $this;
}
/**
* @return DataFormatterInterface
*/
public function getDataFormatter()
{
if ($this->dataFormater === null) {
$this->dataFormater = DataCollector::getDefaultDataFormatter();
}
return $this->dataFormater;
}
/**
* Adds a message
*
* A message can be anything from an object to a string
*
* @param mixed $message
* @param string $label
*/
public function addMessage($message, $label = 'info', $isString = true)
{
if (!is_string($message)) {
$message = $this->getDataFormatter()->formatVar($message);
$isString = false;
}
$this->messages[] = array(
'message' => $message,
'is_string' => $isString,
'label' => $label,
'time' => microtime(true)
);
}
/**
* Aggregates messages from other collectors
*
* @param MessagesAggregateInterface $messages
*/
public function aggregate(MessagesAggregateInterface $messages)
{
$this->aggregates[] = $messages;
}
/**
* @return array
*/
public function getMessages()
{
$messages = $this->messages;
foreach ($this->aggregates as $collector) {
$msgs = array_map(function ($m) use ($collector) {
$m['collector'] = $collector->getName();
return $m;
}, $collector->getMessages());
$messages = array_merge($messages, $msgs);
}
// sort messages by their timestamp
usort($messages, function ($a, $b) {
if ($a['time'] === $b['time']) {
return 0;
}
return $a['time'] < $b['time'] ? -1 : 1;
});
return $messages;
}
/**
* @param $level
* @param $message
* @param array $context
*/
public function log($level, $message, array $context = array())
{
$this->addMessage($message, $level);
}
/**
* Deletes all messages
*/
public function clear()
{
$this->messages = array();
}
/**
* @return array
*/
public function collect()
{
$messages = $this->getMessages();
return array(
'count' => count($messages),
'messages' => $messages
);
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return array
*/
public function getWidgets()
{
$name = $this->getName();
return array(
"$name" => array(
'icon' => 'list-alt',
"widget" => "PhpDebugBar.Widgets.MessagesWidget",
"map" => "$name.messages",
"default" => "[]"
),
"$name:badge" => array(
"map" => "$name.count",
"default" => "null"
)
);
}
}

View File

@ -0,0 +1,200 @@
<?php
namespace DebugBar\DataCollector\PDO;
use DebugBar\DataCollector\AssetProvider;
use DebugBar\DataCollector\DataCollector;
use DebugBar\DataCollector\Renderable;
use DebugBar\DataCollector\TimeDataCollector;
/**
* Collects data about SQL statements executed with PDO
*/
class PDOCollector extends DataCollector implements Renderable, AssetProvider
{
protected $connections = array();
protected $timeCollector;
protected $renderSqlWithParams = false;
protected $sqlQuotationChar = '<>';
/**
* @param TraceablePDO $pdo
* @param TimeDataCollector $timeCollector
*/
public function __construct(TraceablePDO $pdo = null, TimeDataCollector $timeCollector = null)
{
$this->timeCollector = $timeCollector;
if ($pdo !== null) {
$this->addConnection($pdo, 'default');
}
}
/**
* Renders the SQL of traced statements with params embeded
*
* @param boolean $enabled
*/
public function setRenderSqlWithParams($enabled = true, $quotationChar = '<>')
{
$this->renderSqlWithParams = $enabled;
$this->sqlQuotationChar = $quotationChar;
}
/**
* @return bool
*/
public function isSqlRenderedWithParams()
{
return $this->renderSqlWithParams;
}
/**
* @return string
*/
public function getSqlQuotationChar()
{
return $this->sqlQuotationChar;
}
/**
* Adds a new PDO instance to be collector
*
* @param TraceablePDO $pdo
* @param string $name Optional connection name
*/
public function addConnection(TraceablePDO $pdo, $name = null)
{
if ($name === null) {
$name = spl_object_hash($pdo);
}
$this->connections[$name] = $pdo;
}
/**
* Returns PDO instances to be collected
*
* @return array
*/
public function getConnections()
{
return $this->connections;
}
/**
* @return array
*/
public function collect()
{
$data = array(
'nb_statements' => 0,
'nb_failed_statements' => 0,
'accumulated_duration' => 0,
'memory_usage' => 0,
'peak_memory_usage' => 0,
'statements' => array()
);
foreach ($this->connections as $name => $pdo) {
$pdodata = $this->collectPDO($pdo, $this->timeCollector);
$data['nb_statements'] += $pdodata['nb_statements'];
$data['nb_failed_statements'] += $pdodata['nb_failed_statements'];
$data['accumulated_duration'] += $pdodata['accumulated_duration'];
$data['memory_usage'] += $pdodata['memory_usage'];
$data['peak_memory_usage'] = max($data['peak_memory_usage'], $pdodata['peak_memory_usage']);
$data['statements'] = array_merge($data['statements'],
array_map(function ($s) use ($name) { $s['connection'] = $name; return $s; }, $pdodata['statements']));
}
$data['accumulated_duration_str'] = $this->getDataFormatter()->formatDuration($data['accumulated_duration']);
$data['memory_usage_str'] = $this->getDataFormatter()->formatBytes($data['memory_usage']);
$data['peak_memory_usage_str'] = $this->getDataFormatter()->formatBytes($data['peak_memory_usage']);
return $data;
}
/**
* Collects data from a single TraceablePDO instance
*
* @param TraceablePDO $pdo
* @param TimeDataCollector $timeCollector
* @return array
*/
protected function collectPDO(TraceablePDO $pdo, TimeDataCollector $timeCollector = null)
{
$stmts = array();
foreach ($pdo->getExecutedStatements() as $stmt) {
$stmts[] = array(
'sql' => $this->renderSqlWithParams ? $stmt->getSqlWithParams($this->sqlQuotationChar) : $stmt->getSql(),
'row_count' => $stmt->getRowCount(),
'stmt_id' => $stmt->getPreparedId(),
'prepared_stmt' => $stmt->getSql(),
'params' => (object) $stmt->getParameters(),
'duration' => $stmt->getDuration(),
'duration_str' => $this->getDataFormatter()->formatDuration($stmt->getDuration()),
'memory' => $stmt->getMemoryUsage(),
'memory_str' => $this->getDataFormatter()->formatBytes($stmt->getMemoryUsage()),
'end_memory' => $stmt->getEndMemory(),
'end_memory_str' => $this->getDataFormatter()->formatBytes($stmt->getEndMemory()),
'is_success' => $stmt->isSuccess(),
'error_code' => $stmt->getErrorCode(),
'error_message' => $stmt->getErrorMessage()
);
if ($timeCollector !== null) {
$timeCollector->addMeasure($stmt->getSql(), $stmt->getStartTime(), $stmt->getEndTime());
}
}
return array(
'nb_statements' => count($stmts),
'nb_failed_statements' => count($pdo->getFailedExecutedStatements()),
'accumulated_duration' => $pdo->getAccumulatedStatementsDuration(),
'accumulated_duration_str' => $this->getDataFormatter()->formatDuration($pdo->getAccumulatedStatementsDuration()),
'memory_usage' => $pdo->getMemoryUsage(),
'memory_usage_str' => $this->getDataFormatter()->formatBytes($pdo->getPeakMemoryUsage()),
'peak_memory_usage' => $pdo->getPeakMemoryUsage(),
'peak_memory_usage_str' => $this->getDataFormatter()->formatBytes($pdo->getPeakMemoryUsage()),
'statements' => $stmts
);
}
/**
* @return string
*/
public function getName()
{
return 'pdo';
}
/**
* @return array
*/
public function getWidgets()
{
return array(
"database" => array(
"icon" => "inbox",
"widget" => "PhpDebugBar.Widgets.SQLQueriesWidget",
"map" => "pdo",
"default" => "[]"
),
"database:badge" => array(
"map" => "pdo.nb_statements",
"default" => 0
)
);
}
/**
* @return array
*/
public function getAssets()
{
return array(
'css' => 'widgets/sqlqueries/widget.css',
'js' => 'widgets/sqlqueries/widget.js'
);
}
}

View File

@ -0,0 +1,209 @@
<?php
namespace DebugBar\DataCollector\PDO;
use PDO;
use PDOException;
/**
* A PDO proxy which traces statements
*/
class TraceablePDO extends PDO
{
protected $pdo;
protected $executedStatements = array();
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
$this->pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('DebugBar\DataCollector\PDO\TraceablePDOStatement', array($this)));
}
public function beginTransaction()
{
return $this->pdo->beginTransaction();
}
public function commit()
{
return $this->pdo->commit();
}
public function errorCode()
{
return $this->pdo->errorCode();
}
public function errorInfo()
{
return $this->pdo->errorInfo();
}
public function exec($sql)
{
return $this->profileCall('exec', $sql, func_get_args());
}
public function getAttribute($attr)
{
return $this->pdo->getAttribute($attr);
}
public function inTransaction()
{
return $this->pdo->inTransaction();
}
public function lastInsertId($name = null)
{
return $this->pdo->lastInsertId($name);
}
public function prepare($sql, $driver_options = array())
{
return $this->pdo->prepare($sql, $driver_options);
}
public function query($sql)
{
return $this->profileCall('query', $sql, func_get_args());
}
public function quote($expr, $parameter_type = PDO::PARAM_STR)
{
return $this->pdo->quote($expr, $parameter_type);
}
public function rollBack()
{
return $this->pdo->rollBack();
}
public function setAttribute($attr, $value)
{
return $this->pdo->setAttribute($attr, $value);
}
/**
* Profiles a call to a PDO method
*
* @param string $method
* @param string $sql
* @param array $args
* @return mixed The result of the call
*/
protected function profileCall($method, $sql, array $args)
{
$trace = new TracedStatement($sql);
$trace->start();
$ex = null;
try {
$result = call_user_func_array(array($this->pdo, $method), $args);
} catch (PDOException $e) {
$ex = $e;
}
if ($this->pdo->getAttribute(PDO::ATTR_ERRMODE) !== PDO::ERRMODE_EXCEPTION && $result === false) {
$error = $this->pdo->errorInfo();
$ex = new PDOException($error[2], $error[0]);
}
$trace->end($ex);
$this->addExecutedStatement($trace);
if ($this->pdo->getAttribute(PDO::ATTR_ERRMODE) === PDO::ERRMODE_EXCEPTION && $ex !== null) {
throw $ex;
}
return $result;
}
/**
* Adds an executed TracedStatement
*
* @param TracedStatement $stmt
*/
public function addExecutedStatement(TracedStatement $stmt)
{
$this->executedStatements[] = $stmt;
}
/**
* Returns the accumulated execution time of statements
*
* @return int
*/
public function getAccumulatedStatementsDuration()
{
return array_reduce($this->executedStatements, function ($v, $s) { return $v + $s->getDuration(); });
}
/**
* Returns the peak memory usage while performing statements
*
* @return int
*/
public function getMemoryUsage()
{
return array_reduce($this->executedStatements, function ($v, $s) { return $v + $s->getMemoryUsage(); });
}
/**
* Returns the peak memory usage while performing statements
*
* @return int
*/
public function getPeakMemoryUsage()
{
return array_reduce($this->executedStatements, function ($v, $s) { $m = $s->getEndMemory(); return $m > $v ? $m : $v; });
}
/**
* Returns the list of executed statements as TracedStatement objects
*
* @return array
*/
public function getExecutedStatements()
{
return $this->executedStatements;
}
/**
* Returns the list of failed statements
*
* @return array
*/
public function getFailedExecutedStatements()
{
return array_filter($this->executedStatements, function ($s) { return !$s->isSuccess(); });
}
/**
* @param $name
* @return mixed
*/
public function __get($name)
{
return $this->pdo->$name;
}
/**
* @param $name
* @param $value
*/
public function __set($name, $value)
{
$this->pdo->$name = $value;
}
/**
* @param $name
* @param $args
* @return mixed
*/
public function __call($name, $args)
{
return call_user_func_array(array($this->pdo, $name), $args);
}
}

View File

@ -0,0 +1,105 @@
<?php
namespace DebugBar\DataCollector\PDO;
use PDO;
use PDOException;
use PDOStatement;
/**
* A traceable PDO statement to use with Traceablepdo
*/
class TraceablePDOStatement extends PDOStatement
{
protected $pdo;
protected $boundParameters = array();
/**
* TraceablePDOStatement constructor.
* @param TraceablePDO $pdo
*/
protected function __construct(TraceablePDO $pdo)
{
$this->pdo = $pdo;
}
/**
* @param mixed $column
* @param mixed $param
* @param null $type
* @param null $maxlen
* @param null $driverdata
* @return mixed
*/
public function bindColumn($column, &$param, $type = null, $maxlen = null, $driverdata = null)
{
$this->boundParameters[$column] = $param;
$args = array_merge(array($column, &$param), array_slice(func_get_args(), 2));
return call_user_func_array(array("parent", 'bindColumn'), $args);
}
/**
* @param mixed $param
* @param mixed $var
* @param int $data_type
* @param null $length
* @param null $driver_options
* @return mixed
*/
public function bindParam($param, &$var, $data_type = PDO::PARAM_STR, $length = null, $driver_options = null)
{
$this->boundParameters[$param] = $var;
$args = array_merge(array($param, &$var), array_slice(func_get_args(), 2));
return call_user_func_array(array("parent", 'bindParam'), $args);
}
/**
* @param mixed $param
* @param mixed $value
* @param int $data_type
* @return mixed
*/
public function bindValue($param, $value, $data_type = PDO::PARAM_STR)
{
$this->boundParameters[$param] = $value;
return call_user_func_array(array("parent", 'bindValue'), func_get_args());
}
/**
* @param null $params
* @return bool
* @throws null
*/
public function execute($params = null)
{
$preparedId = spl_object_hash($this);
$boundParameters = $this->boundParameters;
if (is_array($params)) {
$boundParameters = array_merge($boundParameters, $params);
}
$trace = new TracedStatement($this->queryString, $boundParameters, $preparedId);
$trace->start();
$ex = null;
try {
$result = parent::execute($params);
} catch (PDOException $e) {
$ex = $e;
}
if ($this->pdo->getAttribute(PDO::ATTR_ERRMODE) !== PDO::ERRMODE_EXCEPTION && $result === false) {
$error = $this->errorInfo();
$ex = new PDOException($error[2], (int) $error[0]);
}
$trace->end($ex, $this->rowCount());
$this->pdo->addExecutedStatement($trace);
if ($this->pdo->getAttribute(PDO::ATTR_ERRMODE) === PDO::ERRMODE_EXCEPTION && $ex !== null) {
throw $ex;
}
return $result;
}
}

View File

@ -0,0 +1,257 @@
<?php
namespace DebugBar\DataCollector\PDO;
/**
* Holds information about a statement
*/
class TracedStatement
{
protected $sql;
protected $rowCount;
protected $parameters;
protected $startTime;
protected $endTime;
protected $duration;
protected $startMemory;
protected $endMemory;
protected $memoryDelta;
protected $exception;
/**
* @param string $sql
* @param array $params
* @param string $preparedId
*/
public function __construct($sql, array $params = array(), $preparedId = null)
{
$this->sql = $sql;
$this->parameters = $this->checkParameters($params);
$this->preparedId = $preparedId;
}
/**
* @param null $startTime
* @param null $startMemory
*/
public function start($startTime = null, $startMemory = null)
{
$this->startTime = $startTime ?: microtime(true);
$this->startMemory = $startMemory ?: memory_get_usage(true);
}
/**
* @param \Exception|null $exception
* @param int $rowCount
* @param null $endTime
* @param null $endMemory
*/
public function end(\Exception $exception = null, $rowCount = 0, $endTime = null, $endMemory = null)
{
$this->endTime = $endTime ?: microtime(true);
$this->duration = $this->endTime - $this->startTime;
$this->endMemory = $endMemory ?: memory_get_usage(true);
$this->memoryDelta = $this->endMemory - $this->startMemory;
$this->exception = $exception;
$this->rowCount = $rowCount;
}
/**
* Check parameters for illegal (non UTF-8) strings, like Binary data.
*
* @param $params
* @return mixed
*/
public function checkParameters($params)
{
foreach ($params as &$param) {
if (!mb_check_encoding($param, 'UTF-8')) {
$param = '[BINARY DATA]';
}
}
return $params;
}
/**
* Returns the SQL string used for the query
*
* @return string
*/
public function getSql()
{
return $this->sql;
}
/**
* Returns the SQL string with any parameters used embedded
*
* @param string $quotationChar
* @return string
*/
public function getSqlWithParams($quotationChar = '<>')
{
if (($l = strlen($quotationChar)) > 1) {
$quoteLeft = substr($quotationChar, 0, $l / 2);
$quoteRight = substr($quotationChar, $l / 2);
} else {
$quoteLeft = $quoteRight = $quotationChar;
}
$sql = $this->sql;
foreach ($this->parameters as $k => $v) {
$v = "$quoteLeft$v$quoteRight";
if (!is_numeric($k)) {
$sql = str_replace($k, $v, $sql);
} else {
$p = strpos($sql, '?');
$sql = substr($sql, 0, $p) . $v. substr($sql, $p + 1);
}
}
return $sql;
}
/**
* Returns the number of rows affected/returned
*
* @return int
*/
public function getRowCount()
{
return $this->rowCount;
}
/**
* Returns an array of parameters used with the query
*
* @return array
*/
public function getParameters()
{
$params = array();
foreach ($this->parameters as $name => $param) {
$params[$name] = htmlentities($param, ENT_QUOTES, 'UTF-8', false);
}
return $params;
}
/**
* Returns the prepared statement id
*
* @return string
*/
public function getPreparedId()
{
return $this->preparedId;
}
/**
* Checks if this is a prepared statement
*
* @return boolean
*/
public function isPrepared()
{
return $this->preparedId !== null;
}
/**
* @return mixed
*/
public function getStartTime()
{
return $this->startTime;
}
/**
* @return mixed
*/
public function getEndTime()
{
return $this->endTime;
}
/**
* Returns the duration in seconds of the execution
*
* @return int
*/
public function getDuration()
{
return $this->duration;
}
/**
* @return mixed
*/
public function getStartMemory()
{
return $this->startMemory;
}
/**
* @return mixed
*/
public function getEndMemory()
{
return $this->endMemory;
}
/**
* Returns the memory usage during the execution
*
* @return int
*/
public function getMemoryUsage()
{
return $this->memoryDelta;
}
/**
* Checks if the statement was successful
*
* @return boolean
*/
public function isSuccess()
{
return $this->exception === null;
}
/**
* Returns the exception triggered
*
* @return \Exception
*/
public function getException()
{
return $this->exception;
}
/**
* Returns the exception's code
*
* @return string
*/
public function getErrorCode()
{
return $this->exception !== null ? $this->exception->getCode() : 0;
}
/**
* Returns the exception's message
*
* @return string
*/
public function getErrorMessage()
{
return $this->exception !== null ? $this->exception->getMessage() : '';
}
}

View File

@ -0,0 +1,36 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\DataCollector;
/**
* Collects info about PHP
*/
class PhpInfoCollector extends DataCollector
{
/**
* @return array
*/
public function collect()
{
return array(
'version' => PHP_VERSION,
'interface' => PHP_SAPI
);
}
/**
* @return string
*/
public function getName()
{
return 'php';
}
}

View File

@ -0,0 +1,25 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\DataCollector;
/**
* Indicates that a DataCollector is renderable using JavascriptRenderer
*/
interface Renderable
{
/**
* Returns a hash where keys are control names and their values
* an array of options as defined in {@see DebugBar\JavascriptRenderer::addControl()}
*
* @return array
*/
function getWidgets();
}

View File

@ -0,0 +1,57 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\DataCollector;
/**
* Collects info about the current request
*/
class RequestDataCollector extends DataCollector implements Renderable
{
/**
* @return array
*/
public function collect()
{
$vars = array('_GET', '_POST', '_SESSION', '_COOKIE', '_SERVER');
$data = array();
foreach ($vars as $var) {
if (isset($GLOBALS[$var])) {
$data["$" . $var] = $this->getDataFormatter()->formatVar($GLOBALS[$var]);
}
}
return $data;
}
/**
* @return string
*/
public function getName()
{
return 'request';
}
/**
* @return array
*/
public function getWidgets()
{
return array(
"request" => array(
"icon" => "tags",
"widget" => "PhpDebugBar.Widgets.VariableListWidget",
"map" => "request",
"default" => "{}"
)
);
}
}

View File

@ -0,0 +1,238 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\DataCollector;
use DebugBar\DebugBarException;
/**
* Collects info about the request duration as well as providing
* a way to log duration of any operations
*/
class TimeDataCollector extends DataCollector implements Renderable
{
/**
* @var float
*/
protected $requestStartTime;
/**
* @var float
*/
protected $requestEndTime;
/**
* @var array
*/
protected $startedMeasures = array();
/**
* @var array
*/
protected $measures = array();
/**
* @param float $requestStartTime
*/
public function __construct($requestStartTime = null)
{
if ($requestStartTime === null) {
if (isset($_SERVER['REQUEST_TIME_FLOAT'])) {
$requestStartTime = $_SERVER['REQUEST_TIME_FLOAT'];
} else {
$requestStartTime = microtime(true);
}
}
$this->requestStartTime = $requestStartTime;
}
/**
* Starts a measure
*
* @param string $name Internal name, used to stop the measure
* @param string|null $label Public name
* @param string|null $collector The source of the collector
*/
public function startMeasure($name, $label = null, $collector = null)
{
$start = microtime(true);
$this->startedMeasures[$name] = array(
'label' => $label ?: $name,
'start' => $start,
'collector' => $collector
);
}
/**
* Check a measure exists
*
* @param string $name
* @return bool
*/
public function hasStartedMeasure($name)
{
return isset($this->startedMeasures[$name]);
}
/**
* Stops a measure
*
* @param string $name
* @param array $params
* @throws DebugBarException
*/
public function stopMeasure($name, $params = array())
{
$end = microtime(true);
if (!$this->hasStartedMeasure($name)) {
throw new DebugBarException("Failed stopping measure '$name' because it hasn't been started");
}
$this->addMeasure(
$this->startedMeasures[$name]['label'],
$this->startedMeasures[$name]['start'],
$end,
$params,
$this->startedMeasures[$name]['collector']
);
unset($this->startedMeasures[$name]);
}
/**
* Adds a measure
*
* @param string $label
* @param float $start
* @param float $end
* @param array $params
* @param string|null $collector
*/
public function addMeasure($label, $start, $end, $params = array(), $collector = null)
{
$this->measures[] = array(
'label' => $label,
'start' => $start,
'relative_start' => $start - $this->requestStartTime,
'end' => $end,
'relative_end' => $end - $this->requestEndTime,
'duration' => $end - $start,
'duration_str' => $this->getDataFormatter()->formatDuration($end - $start),
'params' => $params,
'collector' => $collector
);
}
/**
* Utility function to measure the execution of a Closure
*
* @param string $label
* @param \Closure $closure
* @param string|null $collector
*/
public function measure($label, \Closure $closure, $collector = null)
{
$name = spl_object_hash($closure);
$this->startMeasure($name, $label, $collector);
$result = $closure();
$params = is_array($result) ? $result : array();
$this->stopMeasure($name, $params);
}
/**
* Returns an array of all measures
*
* @return array
*/
public function getMeasures()
{
return $this->measures;
}
/**
* Returns the request start time
*
* @return float
*/
public function getRequestStartTime()
{
return $this->requestStartTime;
}
/**
* Returns the request end time
*
* @return float
*/
public function getRequestEndTime()
{
return $this->requestEndTime;
}
/**
* Returns the duration of a request
*
* @return float
*/
public function getRequestDuration()
{
if ($this->requestEndTime !== null) {
return $this->requestEndTime - $this->requestStartTime;
}
return microtime(true) - $this->requestStartTime;
}
/**
* @return array
* @throws DebugBarException
*/
public function collect()
{
$this->requestEndTime = microtime(true);
foreach (array_keys($this->startedMeasures) as $name) {
$this->stopMeasure($name);
}
return array(
'start' => $this->requestStartTime,
'end' => $this->requestEndTime,
'duration' => $this->getRequestDuration(),
'duration_str' => $this->getDataFormatter()->formatDuration($this->getRequestDuration()),
'measures' => array_values($this->measures)
);
}
/**
* @return string
*/
public function getName()
{
return 'time';
}
/**
* @return array
*/
public function getWidgets()
{
return array(
"time" => array(
"icon" => "clock-o",
"tooltip" => "Request Duration",
"map" => "time.duration_str",
"default" => "'0ms'"
),
"timeline" => array(
"icon" => "tasks",
"widget" => "PhpDebugBar.Widgets.TimelineWidget",
"map" => "time",
"default" => "{}"
)
);
}
}

View File

@ -0,0 +1,81 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\DataFormatter;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
class DataFormatter implements DataFormatterInterface
{
/**
* DataFormatter constructor.
*/
public function __construct()
{
$this->cloner = new VarCloner();
$this->dumper = new CliDumper();
}
/**
* @param $data
* @return string
*/
public function formatVar($data)
{
$output = '';
$this->dumper->dump(
$this->cloner->cloneVar($data),
function ($line, $depth) use (&$output) {
// A negative depth means "end of dump"
if ($depth >= 0) {
// Adds a two spaces indentation to the line
$output .= str_repeat(' ', $depth).$line."\n";
}
}
);
return trim($output);
}
/**
* @param float $seconds
* @return string
*/
public function formatDuration($seconds)
{
if ($seconds < 0.001) {
return round($seconds * 1000000) . 'μs';
} elseif ($seconds < 1) {
return round($seconds * 1000, 2) . 'ms';
}
return round($seconds, 2) . 's';
}
/**
* @param string $size
* @param int $precision
* @return string
*/
public function formatBytes($size, $precision = 2)
{
if ($size === 0 || $size === null) {
return "0B";
}
$sign = $size < 0 ? '-' : '';
$size = abs($size);
$base = log($size) / log(1024);
$suffixes = array('B', 'KB', 'MB', 'GB', 'TB');
return $sign . round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)];
}
}

View File

@ -0,0 +1,42 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\DataFormatter;
/**
* Formats data to be outputed as string
*/
interface DataFormatterInterface
{
/**
* Transforms a PHP variable to a string representation
*
* @param mixed $var
* @return string
*/
function formatVar($data);
/**
* Transforms a duration in seconds in a readable string
*
* @param float $seconds
* @return string
*/
function formatDuration($seconds);
/**
* Transforms a size in bytes to a human readable string
*
* @param string $size
* @param integer $precision
* @return string
*/
function formatBytes($size, $precision = 2);
}

View File

@ -0,0 +1,474 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar;
use ArrayAccess;
use DebugBar\DataCollector\DataCollectorInterface;
use DebugBar\Storage\StorageInterface;
/**
* Main DebugBar object
*
* Manages data collectors. DebugBar provides an array-like access
* to collectors by name.
*
* <code>
* $debugbar = new DebugBar();
* $debugbar->addCollector(new DataCollector\MessagesCollector());
* $debugbar['messages']->addMessage("foobar");
* </code>
*/
class DebugBar implements ArrayAccess
{
public static $useOpenHandlerWhenSendingDataHeaders = false;
protected $collectors = array();
protected $data;
protected $jsRenderer;
protected $requestIdGenerator;
protected $requestId;
protected $storage;
protected $httpDriver;
protected $stackSessionNamespace = 'PHPDEBUGBAR_STACK_DATA';
protected $stackAlwaysUseSessionStorage = false;
/**
* Adds a data collector
*
* @param DataCollectorInterface $collector
*
* @throws DebugBarException
* @return $this
*/
public function addCollector(DataCollectorInterface $collector)
{
if ($collector->getName() === '__meta') {
throw new DebugBarException("'__meta' is a reserved name and cannot be used as a collector name");
}
if (isset($this->collectors[$collector->getName()])) {
throw new DebugBarException("'{$collector->getName()}' is already a registered collector");
}
$this->collectors[$collector->getName()] = $collector;
return $this;
}
/**
* Checks if a data collector has been added
*
* @param string $name
* @return boolean
*/
public function hasCollector($name)
{
return isset($this->collectors[$name]);
}
/**
* Returns a data collector
*
* @param string $name
* @return DataCollectorInterface
* @throws DebugBarException
*/
public function getCollector($name)
{
if (!isset($this->collectors[$name])) {
throw new DebugBarException("'$name' is not a registered collector");
}
return $this->collectors[$name];
}
/**
* Returns an array of all data collectors
*
* @return array[DataCollectorInterface]
*/
public function getCollectors()
{
return $this->collectors;
}
/**
* Sets the request id generator
*
* @param RequestIdGeneratorInterface $generator
* @return $this
*/
public function setRequestIdGenerator(RequestIdGeneratorInterface $generator)
{
$this->requestIdGenerator = $generator;
return $this;
}
/**
* @return RequestIdGeneratorInterface
*/
public function getRequestIdGenerator()
{
if ($this->requestIdGenerator === null) {
$this->requestIdGenerator = new RequestIdGenerator();
}
return $this->requestIdGenerator;
}
/**
* Returns the id of the current request
*
* @return string
*/
public function getCurrentRequestId()
{
if ($this->requestId === null) {
$this->requestId = $this->getRequestIdGenerator()->generate();
}
return $this->requestId;
}
/**
* Sets the storage backend to use to store the collected data
*
* @param StorageInterface $storage
* @return $this
*/
public function setStorage(StorageInterface $storage = null)
{
$this->storage = $storage;
return $this;
}
/**
* @return StorageInterface
*/
public function getStorage()
{
return $this->storage;
}
/**
* Checks if the data will be persisted
*
* @return boolean
*/
public function isDataPersisted()
{
return $this->storage !== null;
}
/**
* Sets the HTTP driver
*
* @param HttpDriverInterface $driver
* @return $this
*/
public function setHttpDriver(HttpDriverInterface $driver)
{
$this->httpDriver = $driver;
return $this;
}
/**
* Returns the HTTP driver
*
* If no http driver where defined, a PhpHttpDriver is automatically created
*
* @return HttpDriverInterface
*/
public function getHttpDriver()
{
if ($this->httpDriver === null) {
$this->httpDriver = new PhpHttpDriver();
}
return $this->httpDriver;
}
/**
* Collects the data from the collectors
*
* @return array
*/
public function collect()
{
$this->data = array(
'__meta' => array(
'id' => $this->getCurrentRequestId(),
'datetime' => date('Y-m-d H:i:s'),
'utime' => microtime(true),
'method' => isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : null,
'uri' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : null,
'ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null
)
);
foreach ($this->collectors as $name => $collector) {
$this->data[$name] = $collector->collect();
}
// Remove all invalid (non UTF-8) characters
array_walk_recursive($this->data, function (&$item) {
if (is_string($item) && !mb_check_encoding($item, 'UTF-8')) {
$item = mb_convert_encoding($item, 'UTF-8', 'UTF-8');
}
});
if ($this->storage !== null) {
$this->storage->save($this->getCurrentRequestId(), $this->data);
}
return $this->data;
}
/**
* Returns collected data
*
* Will collect the data if none have been collected yet
*
* @return array
*/
public function getData()
{
if ($this->data === null) {
$this->collect();
}
return $this->data;
}
/**
* Returns an array of HTTP headers containing the data
*
* @param string $headerName
* @param integer $maxHeaderLength
* @return array
*/
public function getDataAsHeaders($headerName = 'phpdebugbar', $maxHeaderLength = 4096, $maxTotalHeaderLength = 250000)
{
$data = rawurlencode(json_encode(array(
'id' => $this->getCurrentRequestId(),
'data' => $this->getData()
)));
if (strlen($data) > $maxTotalHeaderLength) {
$data = rawurlencode(json_encode(array(
'error' => 'Maximum header size exceeded'
)));
}
$chunks = array();
while (strlen($data) > $maxHeaderLength) {
$chunks[] = substr($data, 0, $maxHeaderLength);
$data = substr($data, $maxHeaderLength);
}
$chunks[] = $data;
$headers = array();
for ($i = 0, $c = count($chunks); $i < $c; $i++) {
$name = $headerName . ($i > 0 ? "-$i" : '');
$headers[$name] = $chunks[$i];
}
return $headers;
}
/**
* Sends the data through the HTTP headers
*
* @param bool $useOpenHandler
* @param string $headerName
* @param integer $maxHeaderLength
* @return $this
*/
public function sendDataInHeaders($useOpenHandler = null, $headerName = 'phpdebugbar', $maxHeaderLength = 4096)
{
if ($useOpenHandler === null) {
$useOpenHandler = self::$useOpenHandlerWhenSendingDataHeaders;
}
if ($useOpenHandler && $this->storage !== null) {
$this->getData();
$headerName .= '-id';
$headers = array($headerName => $this->getCurrentRequestId());
} else {
$headers = $this->getDataAsHeaders($headerName, $maxHeaderLength);
}
$this->getHttpDriver()->setHeaders($headers);
return $this;
}
/**
* Stacks the data in the session for later rendering
*/
public function stackData()
{
$http = $this->initStackSession();
$data = null;
if (!$this->isDataPersisted() || $this->stackAlwaysUseSessionStorage) {
$data = $this->getData();
} elseif ($this->data === null) {
$this->collect();
}
$stack = $http->getSessionValue($this->stackSessionNamespace);
$stack[$this->getCurrentRequestId()] = $data;
$http->setSessionValue($this->stackSessionNamespace, $stack);
return $this;
}
/**
* Checks if there is stacked data in the session
*
* @return boolean
*/
public function hasStackedData()
{
try {
$http = $this->initStackSession();
} catch (DebugBarException $e) {
return false;
}
return count($http->getSessionValue($this->stackSessionNamespace)) > 0;
}
/**
* Returns the data stacked in the session
*
* @param boolean $delete Whether to delete the data in the session
* @return array
*/
public function getStackedData($delete = true)
{
$http = $this->initStackSession();
$stackedData = $http->getSessionValue($this->stackSessionNamespace);
if ($delete) {
$http->deleteSessionValue($this->stackSessionNamespace);
}
$datasets = array();
if ($this->isDataPersisted() && !$this->stackAlwaysUseSessionStorage) {
foreach ($stackedData as $id => $data) {
$datasets[$id] = $this->getStorage()->get($id);
}
} else {
$datasets = $stackedData;
}
return $datasets;
}
/**
* Sets the key to use in the $_SESSION array
*
* @param string $ns
* @return $this
*/
public function setStackDataSessionNamespace($ns)
{
$this->stackSessionNamespace = $ns;
return $this;
}
/**
* Returns the key used in the $_SESSION array
*
* @return string
*/
public function getStackDataSessionNamespace()
{
return $this->stackSessionNamespace;
}
/**
* Sets whether to only use the session to store stacked data even
* if a storage is enabled
*
* @param boolean $enabled
* @return $this
*/
public function setStackAlwaysUseSessionStorage($enabled = true)
{
$this->stackAlwaysUseSessionStorage = $enabled;
return $this;
}
/**
* Checks if the session is always used to store stacked data
* even if a storage is enabled
*
* @return boolean
*/
public function isStackAlwaysUseSessionStorage()
{
return $this->stackAlwaysUseSessionStorage;
}
/**
* Initializes the session for stacked data
* @return HttpDriverInterface
* @throws DebugBarException
*/
protected function initStackSession()
{
$http = $this->getHttpDriver();
if (!$http->isSessionStarted()) {
throw new DebugBarException("Session must be started before using stack data in the debug bar");
}
if (!$http->hasSessionValue($this->stackSessionNamespace)) {
$http->setSessionValue($this->stackSessionNamespace, array());
}
return $http;
}
/**
* Returns a JavascriptRenderer for this instance
* @param string $baseUrl
* @param string $basePath
* @return JavascriptRenderer
*/
public function getJavascriptRenderer($baseUrl = null, $basePath = null)
{
if ($this->jsRenderer === null) {
$this->jsRenderer = new JavascriptRenderer($this, $baseUrl, $basePath);
}
return $this->jsRenderer;
}
// --------------------------------------------
// ArrayAccess implementation
public function offsetSet($key, $value)
{
throw new DebugBarException("DebugBar[] is read-only");
}
public function offsetGet($key)
{
return $this->getCollector($key);
}
public function offsetExists($key)
{
return $this->hasCollector($key);
}
public function offsetUnset($key)
{
throw new DebugBarException("DebugBar[] is read-only");
}
}

View File

@ -0,0 +1,16 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar;
class DebugBarException extends \Exception
{
//
}

View File

@ -0,0 +1,64 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar;
/**
* Provides an abstraction of PHP native features for easier integration
* in third party frameworks
*/
interface HttpDriverInterface
{
/**
* Sets HTTP headers
*
* @param array $headers
* @return
*/
function setHeaders(array $headers);
/**
* Checks if the session is started
*
* @return boolean
*/
function isSessionStarted();
/**
* Sets a value in the session
*
* @param string $name
* @param string $value
*/
function setSessionValue($name, $value);
/**
* Checks if a value is in the session
*
* @param string $name
* @return boolean
*/
function hasSessionValue($name);
/**
* Returns a value from the session
*
* @param string $name
* @return mixed
*/
function getSessionValue($name);
/**
* Deletes a value from the session
*
* @param string $name
*/
function deleteSessionValue($name);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,117 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar;
/**
* Handler to list and open saved dataset
*/
class OpenHandler
{
protected $debugBar;
/**
* @param DebugBar $debugBar
* @throws DebugBarException
*/
public function __construct(DebugBar $debugBar)
{
if (!$debugBar->isDataPersisted()) {
throw new DebugBarException("DebugBar must have a storage backend to use OpenHandler");
}
$this->debugBar = $debugBar;
}
/**
* Handles the current request
*
* @param array $request Request data
* @param bool $echo
* @param bool $sendHeader
* @return string
* @throws DebugBarException
*/
public function handle($request = null, $echo = true, $sendHeader = true)
{
if ($request === null) {
$request = $_REQUEST;
}
$op = 'find';
if (isset($request['op'])) {
$op = $request['op'];
if (!in_array($op, array('find', 'get', 'clear'))) {
throw new DebugBarException("Invalid operation '{$request['op']}'");
}
}
if ($sendHeader) {
$this->debugBar->getHttpDriver()->setHeaders(array(
'Content-Type' => 'application/json'
));
}
$response = json_encode(call_user_func(array($this, $op), $request));
if ($echo) {
echo $response;
}
return $response;
}
/**
* Find operation
* @param $request
* @return array
*/
protected function find($request)
{
$max = 20;
if (isset($request['max'])) {
$max = $request['max'];
}
$offset = 0;
if (isset($request['offset'])) {
$offset = $request['offset'];
}
$filters = array();
foreach (array('utime', 'datetime', 'ip', 'uri', 'method') as $key) {
if (isset($request[$key])) {
$filters[$key] = $request[$key];
}
}
return $this->debugBar->getStorage()->find($filters, $max, $offset);
}
/**
* Get operation
* @param $request
* @return array
* @throws DebugBarException
*/
protected function get($request)
{
if (!isset($request['id'])) {
throw new DebugBarException("Missing 'id' parameter in 'get' operation");
}
return $this->debugBar->getStorage()->get($request['id']);
}
/**
* Clear operation
*/
protected function clear($request)
{
$this->debugBar->getStorage()->clear();
return array('success' => true);
}
}

View File

@ -0,0 +1,70 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar;
/**
* HTTP driver for native php
*/
class PhpHttpDriver implements HttpDriverInterface
{
/**
* @param array $headers
*/
function setHeaders(array $headers)
{
foreach ($headers as $name => $value) {
header("$name: $value");
}
}
/**
* @return bool
*/
function isSessionStarted()
{
return isset($_SESSION);
}
/**
* @param string $name
* @param string $value
*/
function setSessionValue($name, $value)
{
$_SESSION[$name] = $value;
}
/**
* @param string $name
* @return bool
*/
function hasSessionValue($name)
{
return array_key_exists($name, $_SESSION);
}
/**
* @param string $name
* @return mixed
*/
function getSessionValue($name)
{
return $_SESSION[$name];
}
/**
* @param string $name
*/
function deleteSessionValue($name)
{
unset($_SESSION[$name]);
}
}

View File

@ -0,0 +1,25 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar;
/**
* Request id generator based on the $_SERVER array
*/
class RequestIdGenerator implements RequestIdGeneratorInterface
{
/**
* @return string
*/
public function generate()
{
return md5(serialize($_SERVER) . microtime());
}
}

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar;
interface RequestIdGeneratorInterface
{
/**
* Generates a unique id for the current request
*
* @return string
*/
function generate();
}

View File

@ -0,0 +1,291 @@
/* Hide debugbar when printing a page */
@media print {
div.phpdebugbar {
display: none;
}
}
div.phpdebugbar {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
border-top: 0;
font-family: arial, sans-serif;
background: #fff;
z-index: 10000;
font-size: 14px;
color: #000;
text-align: left;
line-height: 1;
letter-spacing: normal;
}
div.phpdebugbar a,
div.phpdebugbar-openhandler {
cursor: pointer;
}
div.phpdebugbar-drag-capture {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 10001;
background: none;
display: none;
cursor: n-resize;
}
div.phpdebugbar-closed {
width: auto;
}
div.phpdebugbar * {
margin: 0;
padding: 0;
border: 0;
font-weight: normal;
text-decoration: none;
clear: initial;
width: auto;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
div.phpdebugbar ol, div.phpdebugbar ul {
list-style: none;
}
div.phpdebugbar table {
border-collapse: collapse;
border-spacing: 0;
}
div.phpdebugbar code, div.phpdebugbar pre {
background: none;
font-family: monospace;
font-size: 1em;
border: 0;
padding: 0;
margin: 0;
}
a.phpdebugbar-restore-btn {
float: left;
padding: 5px 8px;
font-size: 14px;
color: #555;
text-decoration: none;
border-right: 1px solid #ddd;
}
div.phpdebugbar-resize-handle {
display: none;
height: 4px;
margin-top: -4px;
width: 100%;
background: none;
border-bottom: 1px solid #ccc;
cursor: n-resize;
}
div.phpdebugbar-closed, div.phpdebugbar-minimized{
border-top: 1px solid #ccc;
}
/* -------------------------------------- */
div.phpdebugbar-header, a.phpdebugbar-restore-btn {
background: #efefef url(data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%2020%2020%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Ccircle%20fill%3D%22%23000%22%20cx%3D%2210%22%20cy%3D%2210%22%20r%3D%229%22%2F%3E%3Cpath%20d%3D%22M6.039%208.342c.463%200%20.772.084.927.251.154.168.191.455.11.862-.084.424-.247.727-.487.908-.241.182-.608.272-1.1.272h-.743l.456-2.293h.837zm-2.975%204.615h1.22l.29-1.457H5.62c.461%200%20.84-.047%201.139-.142.298-.095.569-.254.812-.477.205-.184.37-.387.497-.608.127-.222.217-.466.27-.734.13-.65.032-1.155-.292-1.518-.324-.362-.84-.543-1.545-.543H4.153l-1.089%205.479zM9.235%206.02h1.21l-.289%201.458h1.079c.679%200%201.147.115%201.405.347.258.231.335.607.232%201.125l-.507%202.55h-1.23l.481-2.424c.055-.276.035-.464-.06-.565-.095-.1-.298-.15-.608-.15H9.98L9.356%2011.5h-1.21l1.089-5.48M15.566%208.342c.464%200%20.773.084.928.251.154.168.19.455.11.862-.084.424-.247.727-.488.908-.24.182-.607.272-1.1.272h-.742l.456-2.293h.836zm-2.974%204.615h1.22l.29-1.457h1.046c.461%200%20.84-.047%201.139-.142.298-.095.569-.254.812-.477.205-.184.37-.387.497-.608.127-.222.217-.466.27-.734.129-.65.032-1.155-.292-1.518-.324-.362-.84-.543-1.545-.543H13.68l-1.089%205.479z%22%20fill%3D%22%23FFF%22%2F%3E%3C%2Fsvg%3E) no-repeat 5px 4px / 20px 20px;
}
div.phpdebugbar-header {
padding-left: 29px;
min-height: 26px;
line-height: 16px;
}
div.phpdebugbar-header:before, div.phpdebugbar-header:after {
display: table;
line-height: 0;
content: "";
}
div.phpdebugbar-header:after {
clear: both;
}
div.phpdebugbar-header-left {
float: left;
}
div.phpdebugbar-header-right {
float: right;
}
div.phpdebugbar-header > div > * {
padding: 5px 5px;
font-size: 14px;
color: #555;
text-decoration: none;
}
div.phpdebugbar-header-left > * {
float: left;
}
div.phpdebugbar-header-right > * {
float: right;
}
div.phpdebugbar-header-right > select {
padding: 0;
}
/* -------------------------------------- */
span.phpdebugbar-indicator,
a.phpdebugbar-indicator,
a.phpdebugbar-close-btn {
border-right: 1px solid #ddd;
}
a.phpdebugbar-tab.phpdebugbar-active {
background: #ccc;
color: #444;
background-image: linear-gradient(bottom, rgb(173,173,173) 41%, rgb(209,209,209) 71%);
background-image: -o-linear-gradient(bottom, rgb(173,173,173) 41%, rgb(209,209,209) 71%);
background-image: -moz-linear-gradient(bottom, rgb(173,173,173) 41%, rgb(209,209,209) 71%);
background-image: -webkit-linear-gradient(bottom, rgb(173,173,173) 41%, rgb(209,209,209) 71%);
background-image: -ms-linear-gradient(bottom, rgb(173,173,173) 41%, rgb(209,209,209) 71%);
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.41, rgb(173,173,173)), color-stop(0.71, rgb(209,209,209)));
}
a.phpdebugbar-tab span.phpdebugbar-badge {
display: none;
margin-left: 5px;
font-size: 11px;
line-height: 14px;
padding: 0px 6px;
background: #ccc;
border-radius: 4px;
color: #555;
font-weight: normal;
text-shadow: none;
vertical-align: middle;
}
a.phpdebugbar-tab i {
display: none;
vertical-align: middle;
}
a.phpdebugbar-tab span.phpdebugbar-badge.phpdebugbar-important {
background: #ed6868;
color: white;
}
a.phpdebugbar-close-btn, a.phpdebugbar-open-btn, a.phpdebugbar-restore-btn, a.phpdebugbar-minimize-btn , a.phpdebugbar-maximize-btn {
width: 16px;
height: 16px;
}
a.phpdebugbar-minimize-btn , a.phpdebugbar-maximize-btn {
padding-right: 0px !important;
}
a.phpdebugbar-maximize-btn { display: none}
a.phpdebugbar-minimize-btn { display: block}
div.phpdebugbar-minimized a.phpdebugbar-maximize-btn { display: block}
div.phpdebugbar-minimized a.phpdebugbar-minimize-btn { display: none}
a.phpdebugbar-minimize-btn {
background:url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201792%201792%22%20id%3D%22chevron-down%22%3E%3Cpath%20d%3D%22M1683%20808l-742%20741q-19%2019-45%2019t-45-19l-742-741q-19-19-19-45.5t19-45.5l166-165q19-19%2045-19t45%2019l531%20531%20531-531q19-19%2045-19t45%2019l166%20165q19%2019%2019%2045.5t-19%2045.5z%22%2F%3E%3C%2Fsvg%3E) no-repeat 6px 6px / 14px 14px;
}
a.phpdebugbar-maximize-btn {
background:url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201792%201792%22%20id%3D%22chevron-up%22%3E%3Cpath%20d%3D%22M1683%201331l-166%20165q-19%2019-45%2019t-45-19l-531-531-531%20531q-19%2019-45%2019t-45-19l-166-165q-19-19-19-45.5t19-45.5l742-741q19-19%2045-19t45%2019l742%20741q19%2019%2019%2045.5t-19%2045.5z%22%2F%3E%3C%2Fsvg%3E) no-repeat 6px 6px / 14px 14px;
}
a.phpdebugbar-close-btn {
background: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201792%201792%22%20id%3D%22close%22%3E%3Cpath%20d%3D%22M1490%201322q0%2040-28%2068l-136%20136q-28%2028-68%2028t-68-28l-294-294-294%20294q-28%2028-68%2028t-68-28l-136-136q-28-28-28-68t28-68l294-294-294-294q-28-28-28-68t28-68l136-136q28-28%2068-28t68%2028l294%20294%20294-294q28-28%2068-28t68%2028l136%20136q28%2028%2028%2068t-28%2068l-294%20294%20294%20294q28%2028%2028%2068z%22%2F%3E%3C%2Fsvg%3E) no-repeat 9px 6px / 14px 14px;
}
a.phpdebugbar-open-btn {
background: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201792%201792%22%20id%3D%22folder-open%22%3E%3Cpath%20d%3D%22M1815%20952q0%2031-31%2066l-336%20396q-43%2051-120.5%2086.5t-143.5%2035.5h-1088q-34%200-60.5-13t-26.5-43q0-31%2031-66l336-396q43-51%20120.5-86.5t143.5-35.5h1088q34%200%2060.5%2013t26.5%2043zm-343-344v160h-832q-94%200-197%2047.5t-164%20119.5l-337%20396-5%206q0-4-.5-12.5t-.5-12.5v-960q0-92%2066-158t158-66h320q92%200%20158%2066t66%20158v32h544q92%200%20158%2066t66%20158z%22%2F%3E%3C%2Fsvg%3E) no-repeat 8px 6px / 14px 14px;
}
.phpdebugbar-indicator {
position: relative;
cursor: pointer;
}
.phpdebugbar-indicator span.phpdebugbar-text {
margin-left: 5px;
}
.phpdebugbar-indicator span.phpdebugbar-tooltip {
display: none;
position: absolute;
top: -30px;
background: #efefef;
opacity: .7;
border: 1px solid #ccc;
color: #555;
font-size: 11px;
padding: 2px 3px;
z-index: 1000;
text-align: center;
width: 200%;
right: 0;
}
.phpdebugbar-indicator:hover span.phpdebugbar-tooltip:not(.phpdebugbar-disabled) {
display: block;
}
select.phpdebugbar-datasets-switcher {
float: right;
display: none;
margin: 2px 0 0 7px;
max-width: 200px;
max-height: 23px;
padding: 0;
}
/* -------------------------------------- */
div.phpdebugbar-body {
border-top: 1px solid #ccc;
display: none;
position: relative;
height: 300px;
}
/* -------------------------------------- */
div.phpdebugbar-panel {
display: none;
height: 100%;
overflow: auto;
width: 100%;
}
div.phpdebugbar-panel.phpdebugbar-active {
display: block;
}
/* -------------------------------------- */
div.phpdebugbar-mini-design a.phpdebugbar-tab {
position: relative;
border-right: 1px solid #ddd;
}
div.phpdebugbar-mini-design a.phpdebugbar-tab span.phpdebugbar-text {
display: none;
}
div.phpdebugbar-mini-design a.phpdebugbar-tab:hover span.phpdebugbar-text {
display: block;
position: absolute;
top: -30px;
background: #efefef;
opacity: .7;
border: 1px solid #ccc;
color: #555;
font-size: 11px;
padding: 2px 3px;
z-index: 1000;
text-align: center;
right: 0;
}
div.phpdebugbar-mini-design a.phpdebugbar-tab i {
display:inline-block;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,69 @@
div.phpdebugbar-openhandler-overlay {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #000;
opacity: .3;
z-index: 20000;
}
div.phpdebugbar-openhandler {
position: fixed;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 70%;
height: 70%;
background: #fff;
border: 2px solid #888;
overflow: auto;
z-index: 20001;
font-family: arial;
font-size: 14px;
padding-bottom: 10px;
}
div.phpdebugbar-openhandler a {
color: #555;
}
div.phpdebugbar-openhandler .phpdebugbar-openhandler-header {
background: #efefef url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAUCAYAAABvVQZ0AAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAGYktHRAD/AP8A/6C9p5MAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfdBgcKHQH1H7EUAAADV0lEQVQ4y7WUy28bVRSHvzvjJPbYY48dj80rTe28gCbCivPsAhBthJCoBIEQQGr/BMRjh1gA20plEYSQumFFQbBBEWVV0bLoQ1BC1YfcBDt1UicFZZzYje06M57LokVNaZJ2w7e7597zOzpX53fgfhSgzYzGDmk+7YQe0DMD/UNSD+gZzaedMKOxQ0DbnXf3IP5z1hLtyc8k8q1IuFX/N+i6LopyN7dYtNYR4ti1fO5doLqVmD+oBy90JLs6pJQ8CCEE2dxctnyz/AxQ2SwWjYRbzycTHbscx+Fh8Xg85OazC8VVKw2sqIDS3dlzJBo1X3Bdd8skKSVCiPvirusSChmhoB40rKJ1XFFVT/uGvXFwu+pBQ6erp5OdWq9v1A8KIdo9Ab9/MhJu9TUaDdbWVlEUFYlEureTP/n0IwpLNzh75gwetRlN06jdqoF7+5Mcx8br9fk0nzaJ1+s7nU4NysTupLRtW5ZKJVmpVOWpkz/LjkRCFgoFaduOrFarcnb2quzb0ytnZmZktVaT5fJNWSqV5P59+2RTU9Npxa/5e10p0XU/lmUxOryX7q5OIpEw4xPjxOMxnn/uWdqeaCNmxhgeHSSVSvHi2BidyS6OHv2S9z94D1e6exQzauqObZMeSGOtWNiOQ9iI4iIZGhplfb1CNpulNWyiqAr2xi0A5nN5QiEDze+n0QAkmic7/+diZ6K7bXLyTTxNKr19T/Hq+Css5Be4vpinWCwS8BsEQi3UajVMM45t24zsHaKv72leG59gcuINFKEsC6/X+13cfOT1S1cu8u03x8jl8ti2zfT0NCMjo9RqFS5fyhAMBejp6WZsbD9mLM6pk7+gqio/Hf+Ret1hLpv5Xhgh4+WwEZmey84ykO5HuuqWMwXgOA6ffzHF1NQR5jJ5FPWuxZaWCwcEEHzs0cfPeVtangwGjQdOfbVSpcXrRd0ktFZazVzLzw8rQHlpuXA4FAo/lIU0v3aPkBCCxesLh4Gyeic2c+Ov5d0xM57arsWtcF2XCxdnvpJSfgygbrr7wbJWioYRfqm5uXlH+6iqSr1eJ3P1yjuudD/cbp8BJIUQX/enBoYbjcaWQr//8ds5KeXbQG6n5biZXcABIDaYHkn+ev5sDvgbmAYW+L/5B5NrVZNHcIujAAAAAElFTkSuQmCC) no-repeat 5px 4px;
padding-left: 29px;
min-height: 26px;
line-height: 25px;
color: #555;
margin-bottom: 10px;
}
div.phpdebugbar-openhandler .phpdebugbar-openhandler-header a {
font-size: 14px;
color: #555;
text-decoration: none;
float: right;
padding: 5px 8px;
}
div.phpdebugbar-openhandler table {
width: 100%;
table-layout: fixed;
font-size: 14px;
}
div.phpdebugbar-openhandler table td {
padding: 6px 3px;
border-bottom: 1px solid #ddd;
}
div.phpdebugbar-openhandler table td a{
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions {
text-align: center;
padding: 7px 0;
}
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions a {
margin: 0 10px;
color: #555;
}

View File

@ -0,0 +1,202 @@
if (typeof(PhpDebugBar) == 'undefined') {
// namespace
var PhpDebugBar = {};
PhpDebugBar.$ = jQuery;
}
(function($) {
var csscls = function(cls) {
return PhpDebugBar.utils.csscls(cls, 'phpdebugbar-openhandler-');
};
PhpDebugBar.OpenHandler = PhpDebugBar.Widget.extend({
className: 'phpdebugbar-openhandler',
defaults: {
items_per_page: 20
},
render: function() {
var self = this;
this.$el.appendTo('body').hide();
this.$closebtn = $('<a><i class="phpdebugbar-fa phpdebugbar-fa-times"></i></a>');
this.$table = $('<tbody />');
$('<div>PHP DebugBar | Open</div>').addClass(csscls('header')).append(this.$closebtn).appendTo(this.$el);
$('<table><thead><tr><th width="150">Date</th><th width="55">Method</th><th>URL</th><th width="125">IP</th><th width="100">Filter data</th></tr></thead></table>').append(this.$table).appendTo(this.$el);
this.$actions = $('<div />').addClass(csscls('actions')).appendTo(this.$el);
this.$closebtn.on('click', function() {
self.hide();
});
this.$loadmorebtn = $('<a>Load more</a>')
.appendTo(this.$actions)
.on('click', function() {
self.find(self.last_find_request, self.last_find_request.offset + self.get('items_per_page'), self.handleFind.bind(self));
});
this.$showonlycurrentbtn = $('<a>Show only current URL</a>')
.appendTo(this.$actions)
.on('click', function() {
self.$table.empty();
self.find({uri: window.location.pathname}, 0, self.handleFind.bind(self));
});
this.$showallbtn = $('<a>Show all</a>')
.appendTo(this.$actions)
.on('click', function() {
self.refresh();
});
this.$clearbtn = $('<a>Delete all</a>')
.appendTo(this.$actions)
.on('click', function() {
self.clear(function() {
self.hide();
});
});
this.addSearch();
this.$overlay = $('<div />').addClass(csscls('overlay')).hide().appendTo('body');
this.$overlay.on('click', function() {
self.hide();
});
},
refresh: function() {
this.$table.empty();
this.$loadmorebtn.show();
this.find({}, 0, this.handleFind.bind(this));
},
addSearch: function(){
var self = this;
var searchBtn = $('<button />')
.text('Search')
.attr('type', 'submit')
.on('click', function(e) {
self.$table.empty();
var search = {};
var a = $(this).parent().serializeArray();
$.each(a, function() {
if(this.value){
search[this.name] = this.value;
}
});
self.find(search, 0, self.handleFind.bind(self));
e.preventDefault();
});
$('<form />')
.append('<br/><b>Filter results</b><br/>')
.append('Method: <select name="method"><option></option><option>GET</option><option>POST</option><option>PUT</option><option>DELETE</option></select><br/>')
.append('Uri: <input type="text" name="uri"><br/>')
.append('IP: <input type="text" name="ip"><br/>')
.append(searchBtn)
.appendTo(this.$actions);
},
handleFind: function(data) {
var self = this;
$.each(data, function(i, meta) {
var a = $('<a />')
.text('Load dataset')
.on('click', function(e) {
self.hide();
self.load(meta['id'], function(data) {
self.callback(meta['id'], data);
});
e.preventDefault();
});
var method = $('<a />')
.text(meta['method'])
.on('click', function(e) {
self.$table.empty();
self.find({method: meta['method']}, 0, self.handleFind.bind(self));
e.preventDefault();
});
var uri = $('<a />')
.text(meta['uri'])
.on('click', function(e) {
self.hide();
self.load(meta['id'], function(data) {
self.callback(meta['id'], data);
});
e.preventDefault();
});
var ip = $('<a />')
.text(meta['ip'])
.on('click', function(e) {
self.$table.empty();
self.find({ip: meta['ip']}, 0, self.handleFind.bind(self));
e.preventDefault();
});
var search = $('<a />')
.text('Show URL')
.on('click', function(e) {
self.$table.empty();
self.find({uri: meta['uri']}, 0, self.handleFind.bind(self));
e.preventDefault();
});
$('<tr />')
.append('<td>' + meta['datetime'] + '</td>')
.append('<td>' + meta['method'] + '</td>')
.append($('<td />').append(uri))
.append($('<td />').append(ip))
.append($('<td />').append(search))
.appendTo(self.$table);
});
if (data.length < this.get('items_per_page')) {
this.$loadmorebtn.hide();
}
},
show: function(callback) {
this.callback = callback;
this.$el.show();
this.$overlay.show();
this.refresh();
},
hide: function() {
this.$el.hide();
this.$overlay.hide();
},
find: function(filters, offset, callback) {
var data = $.extend({}, filters, {max: this.get('items_per_page'), offset: offset || 0});
this.last_find_request = data;
this.ajax(data, callback);
},
load: function(id, callback) {
this.ajax({op: "get", id: id}, callback);
},
clear: function(callback) {
this.ajax({op: "clear"}, callback);
},
ajax: function(data, callback) {
$.ajax({
dataType: 'json',
url: this.get('url'),
data: data,
success: callback,
ignoreDebugBarAjaxHandler: true
});
}
});
})(PhpDebugBar.$);

View File

@ -0,0 +1,209 @@
ul.phpdebugbar-widgets-list {
margin: 0;
padding: 0;
list-style: none;
font-family: monospace;
}
ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item {
padding: 3px 6px;
border-bottom: 1px solid #eee;
position: relative;
overflow: hidden;
}
ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item:hover {
background: #fafafa;
}
/* -------------------------------------- */
div.phpdebugbar-widgets-messages {
position: relative;
height: 100%;
}
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-list {
padding-bottom: 20px;
}
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-warning:before {
font-family: PhpDebugbarFontAwesome;
content: "\f071";
margin-right: 8px;
font-size: 11px;
color: #ecb03d;
}
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-error {
color: red;
}
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-error:before {
font-family: PhpDebugbarFontAwesome;
content: "\f057";
margin-right: 8px;
font-size: 11px;
color: red;
}
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-collector,
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-label {
float: right;
font-size: 12px;
padding: 2px 4px;
color: #888;
margin: 0 2px;
text-decoration: none;
text-shadow: none;
background: none;
font-weight: normal;
}
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-collector {
color: #555;
font-style: italic;
}
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar {
position: fixed;
bottom: 0;
width: 100%;
background: #fff;
}
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar input {
border: 0;
margin: 0;
margin-left: 7px;
width: 50%;
box-shadow: none;
}
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar input:focus {
outline: none;
}
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter {
float: right;
font-size: 12px;
padding: 2px 4px;
background: #7cacd5;
margin: 0 2px;
border-radius: 4px;
color: #fff;
text-decoration: none;
}
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded {
background: #eee;
color: #888;
}
/* -------------------------------------- */
dl.phpdebugbar-widgets-kvlist {
margin: 0;
}
dl.phpdebugbar-widgets-kvlist dt {
float: left;
width: 150px;
padding: 5px;
border-top: 1px solid #eee;
font-weight: bold;
clear: both;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
dl.phpdebugbar-widgets-kvlist dd {
margin-left: 160px;
padding: 5px;
border-top: 1px solid #eee;
cursor: pointer;
min-height: 17px;
}
/* -------------------------------------- */
dl.phpdebugbar-widgets-varlist {
font-family: monospace;
}
/* -------------------------------------- */
ul.phpdebugbar-widgets-timeline {
margin: 0;
padding: 0;
list-style: none;
}
ul.phpdebugbar-widgets-timeline .phpdebugbar-widgets-measure {
height: 20px;
position: relative;
border-bottom: 1px solid #eee;
display: block;
}
ul.phpdebugbar-widgets-timeline li:hover {
background: #fafafa;
}
ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label,
ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-collector {
position: absolute;
font-size: 12px;
font-family: monospace;
color: #555;
top: 4px;
left: 5px;
background: none;
text-shadow: none;
font-weight: normal;
white-space: pre;
}
ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-collector {
left: initial;
right: 5px;
}
ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-value {
display: block;
position: absolute;
height: 10px;
background: #3db9ec;
top: 5px;
border-radius: 2px;
min-width: 1px;
}
ul.phpdebugbar-widgets-timeline table.phpdebugbar-widgets-params {
display: none;
width: 70%;
margin: 10px;
border: 1px solid #ddd;
font-family: monospace;
border-collapse: collapse;
}
ul.phpdebugbar-widgets-timeline table.phpdebugbar-widgets-params td {
border: 1px solid #ddd;
padding: 0 5px;
}
ul.phpdebugbar-widgets-timeline table.phpdebugbar-widgets-params .phpdebugbar-widgets-name {
width: 20%;
font-weight: bold;
}
/* -------------------------------------- */
div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item {
cursor: pointer;
}
div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-message {
display: block;
color: red;
}
div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-filename {
display: block;
font-style: italic;
color: #555;
}
div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-type {
display: block;
position: absolute;
right: 4px;
top: 4px;
font-weight: bold;
}
div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item pre.phpdebugbar-widgets-file {
display: none;
margin: 10px;
padding: 5px;
border: 1px solid #ddd;
font-family: monospace;
}

View File

@ -0,0 +1,463 @@
if (typeof(PhpDebugBar) == 'undefined') {
// namespace
var PhpDebugBar = {};
PhpDebugBar.$ = jQuery;
}
(function($) {
/**
* @namespace
*/
PhpDebugBar.Widgets = {};
/**
* Replaces spaces with &nbsp; and line breaks with <br>
*
* @param {String} text
* @return {String}
*/
var htmlize = PhpDebugBar.Widgets.htmlize = function(text) {
return text.replace(/\n/g, '<br>').replace(/\s/g, "&nbsp;")
};
/**
* Returns a string representation of value, using JSON.stringify
* if it's an object.
*
* @param {Object} value
* @param {Boolean} prettify Uses htmlize() if true
* @return {String}
*/
var renderValue = PhpDebugBar.Widgets.renderValue = function(value, prettify) {
if (typeof(value) !== 'string') {
if (prettify) {
return htmlize(JSON.stringify(value, undefined, 2));
}
return JSON.stringify(value);
}
return value;
};
/**
* Highlights a block of code
*
* @param {String} code
* @param {String} lang
* @return {String}
*/
var highlight = PhpDebugBar.Widgets.highlight = function(code, lang) {
if (typeof(code) === 'string') {
if (typeof(hljs) === 'undefined') {
return htmlize(code);
}
if (lang) {
return hljs.highlight(lang, code).value;
}
return hljs.highlightAuto(code).value;
}
if (typeof(hljs) === 'object') {
code.each(function(i, e) { hljs.highlightBlock(e); });
}
return code;
};
/**
* Creates a <pre> element with a block of code
*
* @param {String} code
* @param {String} lang
* @return {String}
*/
var createCodeBlock = PhpDebugBar.Widgets.createCodeBlock = function(code, lang) {
var pre = $('<pre />');
$('<code />').text(code).appendTo(pre);
if (lang) {
pre.addClass("language-" + lang);
}
highlight(pre);
return pre;
};
var csscls = PhpDebugBar.utils.makecsscls('phpdebugbar-widgets-');
// ------------------------------------------------------------------
// Generic widgets
// ------------------------------------------------------------------
/**
* Displays array element in a <ul> list
*
* Options:
* - data
* - itemRenderer: a function used to render list items (optional)
*/
var ListWidget = PhpDebugBar.Widgets.ListWidget = PhpDebugBar.Widget.extend({
tagName: 'ul',
className: csscls('list'),
initialize: function(options) {
if (!options['itemRenderer']) {
options['itemRenderer'] = this.itemRenderer;
}
this.set(options);
},
render: function() {
this.bindAttr(['itemRenderer', 'data'], function() {
this.$el.empty();
if (!this.has('data')) {
return;
}
var data = this.get('data');
for (var i = 0; i < data.length; i++) {
var li = $('<li />').addClass(csscls('list-item')).appendTo(this.$el);
this.get('itemRenderer')(li, data[i]);
}
});
},
/**
* Renders the content of a <li> element
*
* @param {jQuery} li The <li> element as a jQuery Object
* @param {Object} value An item from the data array
*/
itemRenderer: function(li, value) {
li.html(renderValue(value));
}
});
// ------------------------------------------------------------------
/**
* Displays object property/value paris in a <dl> list
*
* Options:
* - data
* - itemRenderer: a function used to render list items (optional)
*/
var KVListWidget = PhpDebugBar.Widgets.KVListWidget = ListWidget.extend({
tagName: 'dl',
className: csscls('kvlist'),
render: function() {
this.bindAttr(['itemRenderer', 'data'], function() {
this.$el.empty();
if (!this.has('data')) {
return;
}
var self = this;
$.each(this.get('data'), function(key, value) {
var dt = $('<dt />').addClass(csscls('key')).appendTo(self.$el);
var dd = $('<dd />').addClass(csscls('value')).appendTo(self.$el);
self.get('itemRenderer')(dt, dd, key, value);
});
});
},
/**
* Renders the content of the <dt> and <dd> elements
*
* @param {jQuery} dt The <dt> element as a jQuery Object
* @param {jQuery} dd The <dd> element as a jQuery Object
* @param {String} key Property name
* @param {Object} value Property value
*/
itemRenderer: function(dt, dd, key, value) {
dt.text(key);
dd.html(htmlize(value));
}
});
// ------------------------------------------------------------------
/**
* An extension of KVListWidget where the data represents a list
* of variables
*
* Options:
* - data
*/
var VariableListWidget = PhpDebugBar.Widgets.VariableListWidget = KVListWidget.extend({
className: csscls('kvlist varlist'),
itemRenderer: function(dt, dd, key, value) {
$('<span />').attr('title', key).text(key).appendTo(dt);
var v = value;
if (v && v.length > 100) {
v = v.substr(0, 100) + "...";
}
var prettyVal = null;
dd.text(v).click(function() {
if (dd.hasClass(csscls('pretty'))) {
dd.text(v).removeClass(csscls('pretty'));
} else {
prettyVal = prettyVal || createCodeBlock(value);
dd.addClass(csscls('pretty')).empty().append(prettyVal);
}
});
}
});
// ------------------------------------------------------------------
/**
* Iframe widget
*
* Options:
* - data
*/
var IFrameWidget = PhpDebugBar.Widgets.IFrameWidget = PhpDebugBar.Widget.extend({
tagName: 'iframe',
className: csscls('iframe'),
render: function() {
this.$el.attr({
seamless: "seamless",
border: "0",
width: "100%",
height: "100%"
});
this.bindAttr('data', function(url) { this.$el.attr('src', url); });
}
});
// ------------------------------------------------------------------
// Collector specific widgets
// ------------------------------------------------------------------
/**
* Widget for the MessagesCollector
*
* Uses ListWidget under the hood
*
* Options:
* - data
*/
var MessagesWidget = PhpDebugBar.Widgets.MessagesWidget = PhpDebugBar.Widget.extend({
className: csscls('messages'),
render: function() {
var self = this;
this.$list = new ListWidget({ itemRenderer: function(li, value) {
var m = value.message;
if (m.length > 100) {
m = m.substr(0, 100) + "...";
}
var val = $('<span />').addClass(csscls('value')).text(m).appendTo(li);
if (!value.is_string || value.message.length > 100) {
var prettyVal = value.message;
if (!value.is_string) {
prettyVal = null;
}
li.css('cursor', 'pointer').click(function() {
if (val.hasClass(csscls('pretty'))) {
val.text(m).removeClass(csscls('pretty'));
} else {
prettyVal = prettyVal || createCodeBlock(value.message, 'php');
val.addClass(csscls('pretty')).empty().append(prettyVal);
}
});
}
if (value.label) {
val.addClass(csscls(value.label));
$('<span />').addClass(csscls('label')).text(value.label).appendTo(li);
}
if (value.collector) {
$('<span />').addClass(csscls('collector')).text(value.collector).appendTo(li);
}
}});
this.$list.$el.appendTo(this.$el);
this.$toolbar = $('<div><i class="phpdebugbar-fa phpdebugbar-fa-search"></i></div>').addClass(csscls('toolbar')).appendTo(this.$el);
$('<input type="text" />')
.on('change', function() { self.set('search', this.value); })
.appendTo(this.$toolbar);
this.bindAttr('data', function(data) {
this.set({ exclude: [], search: '' });
this.$toolbar.find(csscls('.filter')).remove();
var filters = [], self = this;
for (var i = 0; i < data.length; i++) {
if (!data[i].label || $.inArray(data[i].label, filters) > -1) {
continue;
}
filters.push(data[i].label);
$('<a />')
.addClass(csscls('filter'))
.text(data[i].label)
.attr('rel', data[i].label)
.on('click', function() { self.onFilterClick(this); })
.appendTo(this.$toolbar);
}
});
this.bindAttr(['exclude', 'search'], function() {
var data = this.get('data'),
exclude = this.get('exclude'),
search = this.get('search'),
caseless = false,
fdata = [];
if (search && search === search.toLowerCase()) {
caseless = true;
}
for (var i = 0; i < data.length; i++) {
var message = caseless ? data[i].message.toLowerCase() : data[i].message;
if ((!data[i].label || $.inArray(data[i].label, exclude) === -1) && (!search || message.indexOf(search) > -1)) {
fdata.push(data[i]);
}
}
this.$list.set('data', fdata);
});
},
onFilterClick: function(el) {
$(el).toggleClass(csscls('excluded'));
var excludedLabels = [];
this.$toolbar.find(csscls('.filter') + csscls('.excluded')).each(function() {
excludedLabels.push(this.rel);
});
this.set('exclude', excludedLabels);
}
});
// ------------------------------------------------------------------
/**
* Widget for the TimeDataCollector
*
* Options:
* - data
*/
var TimelineWidget = PhpDebugBar.Widgets.TimelineWidget = PhpDebugBar.Widget.extend({
tagName: 'ul',
className: csscls('timeline'),
render: function() {
this.bindAttr('data', function(data) {
this.$el.empty();
if (data.measures) {
for (var i = 0; i < data.measures.length; i++) {
var measure = data.measures[i];
var m = $('<div />').addClass(csscls('measure')),
li = $('<li />'),
left = (measure.relative_start * 100 / data.duration).toFixed(2),
width = Math.min((measure.duration * 100 / data.duration).toFixed(2), 100 - left);
m.append($('<span />').addClass(csscls('value')).css({
left: left + "%",
width: width + "%"
}));
m.append($('<span />').addClass(csscls('label')).text(measure.label + " (" + measure.duration_str + ")"));
if (measure.collector) {
$('<span />').addClass(csscls('collector')).text(measure.collector).appendTo(m);
}
m.appendTo(li);
this.$el.append(li);
if (measure.params && !$.isEmptyObject(measure.params)) {
var table = $('<table><tr><th colspan="2">Params</th></tr></table>').addClass(csscls('params')).appendTo(li);
for (var key in measure.params) {
if (typeof measure.params[key] !== 'function') {
table.append('<tr><td class="' + csscls('name') + '">' + key + '</td><td class="' + csscls('value') +
'"><pre><code>' + measure.params[key] + '</code></pre></td></tr>');
}
}
li.css('cursor', 'pointer').click(function() {
var table = $(this).find('table');
if (table.is(':visible')) {
table.hide();
} else {
table.show();
}
});
}
}
}
});
}
});
// ------------------------------------------------------------------
/**
* Widget for the displaying exceptions
*
* Options:
* - data
*/
var ExceptionsWidget = PhpDebugBar.Widgets.ExceptionsWidget = PhpDebugBar.Widget.extend({
className: csscls('exceptions'),
render: function() {
this.$list = new ListWidget({ itemRenderer: function(li, e) {
$('<span />').addClass(csscls('message')).text(e.message).appendTo(li);
if (e.file) {
$('<span />').addClass(csscls('filename')).text(e.file + "#" + e.line).appendTo(li);
}
if (e.type) {
$('<span />').addClass(csscls('type')).text(e.type).appendTo(li);
}
if (e.surrounding_lines) {
var pre = createCodeBlock(e.surrounding_lines.join(""), 'php').addClass(csscls('file')).appendTo(li);
li.click(function() {
if (pre.is(':visible')) {
pre.hide();
} else {
pre.show();
}
});
}
}});
this.$list.$el.appendTo(this.$el);
this.bindAttr('data', function(data) {
this.$list.set('data', data);
if (data.length == 1) {
this.$list.$el.children().first().find(csscls('.file')).show();
}
});
}
});
})(PhpDebugBar.$);

View File

@ -0,0 +1,12 @@
div.phpdebugbar-widgets-mails span.phpdebugbar-widgets-subject {
display: block;
}
div.phpdebugbar-widgets-mails li.phpdebugbar-widgets-list-item pre.phpdebugbar-widgets-headers {
display: none;
margin: 10px;
padding: 5px;
border: 1px solid #ddd;
font-family: monospace;
}

View File

@ -0,0 +1,40 @@
(function($) {
var csscls = PhpDebugBar.utils.makecsscls('phpdebugbar-widgets-');
/**
* Widget for the displaying mails data
*
* Options:
* - data
*/
var MailsWidget = PhpDebugBar.Widgets.MailsWidget = PhpDebugBar.Widget.extend({
className: csscls('mails'),
render: function() {
this.$list = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, mail) {
$('<span />').addClass(csscls('subject')).text(mail.subject).appendTo(li);
$('<span />').addClass(csscls('to')).text(mail.to).appendTo(li);
if (mail.headers) {
var headers = $('<pre />').addClass(csscls('headers')).appendTo(li);
$('<code />').text(mail.headers).appendTo(headers);
li.click(function() {
if (headers.is(':visible')) {
headers.hide();
} else {
headers.show();
}
});
}
}});
this.$list.$el.appendTo(this.$el);
this.bindAttr('data', function(data) {
this.$list.set('data', data);
});
}
});
})(PhpDebugBar.$);

View File

@ -0,0 +1,112 @@
div.phpdebugbar-widgets-sqlqueries .phpdebugbar-widgets-status {
font-family: monospace;
padding: 6px 6px;
border-bottom: 1px solid #ddd;
font-weight: bold;
color: #555;
background: #fafafa;
}
div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-error {
color: red;
}
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-database,
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-duration,
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-memory,
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-row-count,
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-stmt-id {
float: right;
margin-left: 8px;
color: #888;
}
div.phpdebugbar-widgets-sqlqueries div.phpdebugbar-widgets-status span.phpdebugbar-widgets-database,
div.phpdebugbar-widgets-sqlqueries div.phpdebugbar-widgets-status span.phpdebugbar-widgets-duration,
div.phpdebugbar-widgets-sqlqueries div.phpdebugbar-widgets-status span.phpdebugbar-widgets-memory,
div.phpdebugbar-widgets-sqlqueries div.phpdebugbar-widgets-status span.phpdebugbar-widgets-row-count,
div.phpdebugbar-widgets-sqlqueries div.phpdebugbar-widgets-status span.phpdebugbar-widgets-stmt-id {
color: #555;
}
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-database:before,
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-duration:before,
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-memory:before,
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-row-count:before,
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-stmt-id:before {
font-family: PhpDebugbarFontAwesome;
margin-right: 4px;
font-size: 12px;
}
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-database:before {
content: "\f1c0";
}
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-duration:before {
content: "\f017";
}
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-memory:before {
content: "\f085";
}
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-row-count:before {
content: "\f0ce";
}
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-stmt-id:before {
content: "\f08d";
}
div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params {
display: none;
width: 70%;
margin: 10px;
border: 1px solid #ddd;
font-family: monospace;
border-collapse: collapse;
}
div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params td {
border: 1px solid #ddd;
text-align: center;
}
div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params .phpdebugbar-widgets-name {
width: 20%;
font-weight: bold;
}
div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-error {
display: block;
font-weight: bold;
}
code.phpdebugbar-widgets-sql {
white-space: pre-wrap;
overflow-wrap: break-word;
word-wrap: break-word;
}
div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate {
background-color: #edeff0;
}
div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate:hover {
background-color: #ffc;
}
div.phpdebugbar-widgets-sqlqueries div.phpdebugbar-widgets-toolbar {
display:none;
position: fixed;
bottom: 0;
width: 100%;
background: #fff;
z-index: 1;
}
div.phpdebugbar-widgets-sqlqueries div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter {
float: right;
font-size: 12px;
padding: 2px 4px;
background: #7cacd5;
margin: 0 2px;
border-radius: 4px;
color: #fff;
text-decoration: none;
}
div.phpdebugbar-widgets-sqlqueries div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded {
background: #eee;
color: #888;
}

View File

@ -0,0 +1,131 @@
(function($) {
var csscls = PhpDebugBar.utils.makecsscls('phpdebugbar-widgets-');
/**
* Widget for the displaying sql queries
*
* Options:
* - data
*/
var SQLQueriesWidget = PhpDebugBar.Widgets.SQLQueriesWidget = PhpDebugBar.Widget.extend({
className: csscls('sqlqueries'),
onFilterClick: function(el) {
$(el).toggleClass(csscls('excluded'));
var excludedLabels = [];
this.$toolbar.find(csscls('.filter') + csscls('.excluded')).each(function() {
excludedLabels.push(this.rel);
});
this.$list.$el.find("li[connection=" + $(el).attr("rel") + "]").toggle();
this.set('exclude', excludedLabels);
},
render: function() {
this.$status = $('<div />').addClass(csscls('status')).appendTo(this.$el);
this.$toolbar = $('<div></div>').addClass(csscls('toolbar')).appendTo(this.$el);
var filters = [], self = this;
this.$list = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, stmt) {
$('<code />').addClass(csscls('sql')).html(PhpDebugBar.Widgets.highlight(stmt.sql, 'sql')).appendTo(li);
if (stmt.duration_str) {
$('<span title="Duration" />').addClass(csscls('duration')).text(stmt.duration_str).appendTo(li);
}
if (stmt.memory_str) {
$('<span title="Memory usage" />').addClass(csscls('memory')).text(stmt.memory_str).appendTo(li);
}
if (typeof(stmt.row_count) != 'undefined') {
$('<span title="Row count" />').addClass(csscls('row-count')).text(stmt.row_count).appendTo(li);
}
if (typeof(stmt.stmt_id) != 'undefined' && stmt.stmt_id) {
$('<span title="Prepared statement ID" />').addClass(csscls('stmt-id')).text(stmt.stmt_id).appendTo(li);
}
if (stmt.connection) {
$('<span title="Connection" />').addClass(csscls('database')).text(stmt.connection).appendTo(li);
li.attr("connection",stmt.connection);
if ( $.inArray(stmt.connection, filters) == -1 ) {
filters.push(stmt.connection);
$('<a />')
.addClass(csscls('filter'))
.text(stmt.connection)
.attr('rel', stmt.connection)
.on('click', function() { self.onFilterClick(this); })
.appendTo(self.$toolbar);
if (filters.length>1) {
self.$toolbar.show();
self.$list.$el.css("margin-bottom","20px");
}
}
}
if (typeof(stmt.is_success) != 'undefined' && !stmt.is_success) {
li.addClass(csscls('error'));
li.append($('<span />').addClass(csscls('error')).text("[" + stmt.error_code + "] " + stmt.error_message));
}
if (stmt.params && !$.isEmptyObject(stmt.params)) {
var table = $('<table><tr><th colspan="2">Params</th></tr></table>').addClass(csscls('params')).appendTo(li);
for (var key in stmt.params) {
if (typeof stmt.params[key] !== 'function') {
table.append('<tr><td class="' + csscls('name') + '">' + key + '</td><td class="' + csscls('value') +
'">' + stmt.params[key] + '</td></tr>');
}
}
li.css('cursor', 'pointer').click(function() {
if (table.is(':visible')) {
table.hide();
} else {
table.show();
}
});
}
}});
this.$list.$el.appendTo(this.$el);
this.bindAttr('data', function(data) {
this.$list.set('data', data.statements);
this.$status.empty();
// Search for duplicate statements.
for (var sql = {}, duplicate = 0, i = 0; i < data.statements.length; i++) {
var stmt = data.statements[i].sql;
if (data.statements[i].params && !$.isEmptyObject(data.statements[i].params)) {
stmt += ' {' + $.param(data.statements[i].params, false) + '}';
}
sql[stmt] = sql[stmt] || { keys: [] };
sql[stmt].keys.push(i);
}
// Add classes to all duplicate SQL statements.
for (var stmt in sql) {
if (sql[stmt].keys.length > 1) {
duplicate++;
for (var i = 0; i < sql[stmt].keys.length; i++) {
this.$list.$el.find('.' + csscls('list-item')).eq(sql[stmt].keys[i])
.addClass(csscls('sql-duplicate')).addClass(csscls('sql-duplicate-'+duplicate));
}
}
}
var t = $('<span />').text(data.nb_statements + " statements were executed").appendTo(this.$status);
if (data.nb_failed_statements) {
t.append(", " + data.nb_failed_statements + " of which failed");
}
if (duplicate) {
t.append(", " + duplicate + " of which were duplicated");
}
if (data.accumulated_duration_str) {
this.$status.append($('<span title="Accumulated duration" />').addClass(csscls('duration')).text(data.accumulated_duration_str));
}
if (data.memory_usage_str) {
this.$status.append($('<span title="Memory usage" />').addClass(csscls('memory')).text(data.memory_usage_str));
}
});
}
});
})(PhpDebugBar.$);

View File

@ -0,0 +1,60 @@
div.phpdebugbar-widgets-templates div.phpdebugbar-widgets-status {
font-family: monospace;
padding: 6px 6px;
border-bottom: 1px solid #ddd;
font-weight: bold;
color: #555;
background: #fafafa;
}
div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-render-time,
div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-memory,
div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-param-count,
div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-type {
float: right;
margin-left: 8px;
color: #888;
}
div.phpdebugbar-widgets-templates div.phpdebugbar-widgets-status span.phpdebugbar-widgets-render-time,
div.phpdebugbar-widgets-templates div.phpdebugbar-widgets-status span.phpdebugbar-widgets-memory,
div.phpdebugbar-widgets-templates div.phpdebugbar-widgets-status span.phpdebugbar-widgets-param-count,
div.phpdebugbar-widgets-templates div.phpdebugbar-widgets-status span.phpdebugbar-widgets-type {
color: #555;
}
div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-render-time:before,
div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-memory:before,
div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-param-count:before,
div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-type:before {
font-family: PhpDebugbarFontAwesome;
margin-right: 4px;
font-size: 12px;
}
div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-render-time:before {
content: "\f017";
}
div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-memory:before {
content: "\f085";
}
div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-param-count:before {
content: "\f0ce";
}
div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-type:before {
content: "\f121";
}
div.phpdebugbar-widgets-templates table.phpdebugbar-widgets-params {
display: none;
width: 70%;
margin: 10px;
border: 1px solid #ddd;
font-family: monospace;
border-collapse: collapse;
}
div.phpdebugbar-widgets-templates table.phpdebugbar-widgets-params td {
border: 1px solid #ddd;
padding: 0 5px;
}
div.phpdebugbar-widgets-templates table.phpdebugbar-widgets-params .phpdebugbar-widgets-name {
width: 20%;
font-weight: bold;
}

View File

@ -0,0 +1,69 @@
(function($) {
var csscls = PhpDebugBar.utils.makecsscls('phpdebugbar-widgets-');
/**
* Widget for the displaying templates data
*
* Options:
* - data
*/
var TemplatesWidget = PhpDebugBar.Widgets.TemplatesWidget = PhpDebugBar.Widget.extend({
className: csscls('templates'),
render: function() {
this.$status = $('<div />').addClass(csscls('status')).appendTo(this.$el);
this.$list = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, tpl) {
$('<span />').addClass(csscls('name')).text(tpl.name).appendTo(li);
if (tpl.render_time_str) {
$('<span title="Render time" />').addClass(csscls('render-time')).text(tpl.render_time_str).appendTo(li);
}
if (tpl.memory_str) {
$('<span title="Memory usage" />').addClass(csscls('memory')).text(tpl.memory_str).appendTo(li);
}
if (typeof(tpl.param_count) != 'undefined') {
$('<span title="Parameter count" />').addClass(csscls('param-count')).text(tpl.param_count).appendTo(li);
}
if (typeof(tpl.type) != 'undefined' && tpl.type) {
$('<span title="Type" />').addClass(csscls('type')).text(tpl.type).appendTo(li);
}
if (tpl.params && !$.isEmptyObject(tpl.params)) {
var table = $('<table><tr><th colspan="2">Params</th></tr></table>').addClass(csscls('params')).appendTo(li);
for (var key in tpl.params) {
if (typeof tpl.params[key] !== 'function') {
table.append('<tr><td class="' + csscls('name') + '">' + key + '</td><td class="' + csscls('value') +
'"><pre><code>' + tpl.params[key] + '</code></pre></td></tr>');
}
}
li.css('cursor', 'pointer').click(function() {
if (table.is(':visible')) {
table.hide();
} else {
table.show();
}
});
}
}});
this.$list.$el.appendTo(this.$el);
this.bindAttr('data', function(data) {
this.$list.set('data', data.templates);
this.$status.empty();
var sentence = data.sentence || "templates were rendered";
$('<span />').text(data.templates.length + " " + sentence).appendTo(this.$status);
if (data.accumulated_render_time_str) {
this.$status.append($('<span title="Accumulated render time" />').addClass(csscls('render-time')).text(data.accumulated_render_time_str));
}
if (data.memory_usage_str) {
this.$status.append($('<span title="Memory usage" />').addClass(csscls('memory')).text(data.memory_usage_str));
}
});
}
});
})(PhpDebugBar.$);

View File

@ -0,0 +1,34 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar;
use DebugBar\DataCollector\ExceptionsCollector;
use DebugBar\DataCollector\MemoryCollector;
use DebugBar\DataCollector\MessagesCollector;
use DebugBar\DataCollector\PhpInfoCollector;
use DebugBar\DataCollector\RequestDataCollector;
use DebugBar\DataCollector\TimeDataCollector;
/**
* Debug bar subclass which adds all included collectors
*/
class StandardDebugBar extends DebugBar
{
public function __construct()
{
$this->addCollector(new PhpInfoCollector());
$this->addCollector(new MessagesCollector());
$this->addCollector(new RequestDataCollector());
$this->addCollector(new TimeDataCollector());
$this->addCollector(new MemoryCollector());
$this->addCollector(new ExceptionsCollector());
}
}

View File

@ -0,0 +1,128 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\Storage;
/**
* Stores collected data into files
*/
class FileStorage implements StorageInterface
{
protected $dirname;
/**
* @param string $dirname Directories where to store files
*/
public function __construct($dirname)
{
$this->dirname = rtrim($dirname, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
}
/**
* {@inheritdoc}
*/
public function save($id, $data)
{
if (!file_exists($this->dirname)) {
mkdir($this->dirname, 0777, true);
}
file_put_contents($this->makeFilename($id), json_encode($data));
}
/**
* {@inheritdoc}
*/
public function get($id)
{
return json_decode(file_get_contents($this->makeFilename($id)), true);
}
/**
* {@inheritdoc}
*/
public function find(array $filters = array(), $max = 20, $offset = 0)
{
//Loop through all .json files and remember the modified time and id.
$files = array();
foreach (new \DirectoryIterator($this->dirname) as $file) {
if ($file->getExtension() == 'json') {
$files[] = array(
'time' => $file->getMTime(),
'id' => $file->getBasename('.json')
);
}
}
//Sort the files, newest first
usort($files, function ($a, $b) {
return $a['time'] < $b['time'];
});
//Load the metadata and filter the results.
$results = array();
$i = 0;
foreach ($files as $file) {
//When filter is empty, skip loading the offset
if ($i++ < $offset && empty($filters)) {
$results[] = null;
continue;
}
$data = $this->get($file['id']);
$meta = $data['__meta'];
unset($data);
if ($this->filter($meta, $filters)) {
$results[] = $meta;
}
if (count($results) >= ($max + $offset)) {
break;
}
}
return array_slice($results, $offset, $max);
}
/**
* Filter the metadata for matches.
*
* @param array $meta
* @param array $filters
* @return bool
*/
protected function filter($meta, $filters)
{
foreach ($filters as $key => $value) {
if (!isset($meta[$key]) || fnmatch($value, $meta[$key]) === false) {
return false;
}
}
return true;
}
/**
* {@inheritdoc}
*/
public function clear()
{
foreach (new \DirectoryIterator($this->dirname) as $file) {
if (substr($file->getFilename(), 0, 1) !== '.') {
unlink($file->getPathname());
}
}
}
/**
* @param string $id
* @return string
*/
public function makeFilename($id)
{
return $this->dirname . basename($id). ".json";
}
}

View File

@ -0,0 +1,111 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\Storage;
use Memcached;
/**
* Stores collected data into Memcache using the Memcached extension
*/
class MemcachedStorage implements StorageInterface
{
protected $memcached;
protected $keyNamespace;
/**
* @param Memcached $memcached
*/
public function __construct(Memcached $memcached, $keyNamespace = 'phpdebugbar')
{
$this->memcached = $memcached;
$this->keyNamespace = $keyNamespace;
}
/**
* {@inheritdoc}
*/
public function save($id, $data)
{
$key = $this->createKey($id);
$this->memcached->set($key, $data);
if (!$this->memcached->append($this->keyNamespace, "|$key")) {
$this->memcached->set($this->keyNamespace, $key);
}
}
/**
* {@inheritdoc}
*/
public function get($id)
{
return $this->memcached->get($this->createKey($id));
}
/**
* {@inheritdoc}
*/
public function find(array $filters = array(), $max = 20, $offset = 0)
{
if (!($keys = $this->memcached->get($this->keyNamespace))) {
return array();
}
$results = array();
foreach (explode('|', $keys) as $key) {
if ($data = $this->memcached->get($key)) {
$meta = $data['__meta'];
if ($this->filter($meta, $filters)) {
$results[] = $meta;
}
}
}
return array_slice($results, $offset, $max);
}
/**
* Filter the metadata for matches.
*
* @param array $meta
* @param array $filters
* @return bool
*/
protected function filter($meta, $filters)
{
foreach ($filters as $key => $value) {
if (!isset($meta[$key]) || fnmatch($value, $meta[$key]) === false) {
return false;
}
}
return true;
}
/**
* {@inheritdoc}
*/
public function clear()
{
if (!($keys = $this->memcached->get($this->keyNamespace))) {
return;
}
$this->memcached->delete($this->keyNamespace);
$this->memcached->deleteMulti(explode('|', $keys));
}
/**
* @param string $id
* @return string
*/
protected function createKey($id)
{
return md5("{$this->keyNamespace}.$id");
}
}

View File

@ -0,0 +1,137 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\Storage;
use PDO;
/**
* Stores collected data into a database using PDO
*/
class PdoStorage implements StorageInterface
{
protected $pdo;
protected $tableName;
protected $sqlQueries = array(
'save' => "INSERT INTO %tablename% (id, data, meta_utime, meta_datetime, meta_uri, meta_ip, meta_method) VALUES (?, ?, ?, ?, ?, ?, ?)",
'get' => "SELECT data FROM %tablename% WHERE id = ?",
'find' => "SELECT data FROM %tablename% %where% ORDER BY meta_datetime DESC LIMIT %limit% OFFSET %offset%",
'clear' => "DELETE FROM %tablename%"
);
/**
* @param \PDO $pdo The PDO instance
* @param string $tableName
* @param array $sqlQueries
*/
public function __construct(PDO $pdo, $tableName = 'phpdebugbar', array $sqlQueries = array())
{
$this->pdo = $pdo;
$this->tableName = $tableName;
$this->setSqlQueries($sqlQueries);
}
/**
* Sets the sql queries to be used
*
* @param array $queries
*/
public function setSqlQueries(array $queries)
{
$this->sqlQueries = array_merge($this->sqlQueries, $queries);
}
/**
* {@inheritdoc}
*/
public function save($id, $data)
{
$sql = $this->getSqlQuery('save');
$stmt = $this->pdo->prepare($sql);
$meta = $data['__meta'];
$stmt->execute(array($id, serialize($data), $meta['utime'], $meta['datetime'], $meta['uri'], $meta['ip'], $meta['method']));
}
/**
* {@inheritdoc}
*/
public function get($id)
{
$sql = $this->getSqlQuery('get');
$stmt = $this->pdo->prepare($sql);
$stmt->execute(array($id));
if (($data = $stmt->fetchColumn(0)) !== false) {
return unserialize($data);
}
return null;
}
/**
* {@inheritdoc}
*/
public function find(array $filters = array(), $max = 20, $offset = 0)
{
$where = array();
$params = array();
foreach ($filters as $key => $value) {
$where[] = "meta_$key = ?";
$params[] = $value;
}
if (count($where)) {
$where = " WHERE " . implode(' AND ', $where);
} else {
$where = '';
}
$sql = $this->getSqlQuery('find', array(
'where' => $where,
'offset' => $offset,
'limit' => $max
));
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
$results = array();
foreach ($stmt->fetchAll() as $row) {
$data = unserialize($row['data']);
$results[] = $data['__meta'];
unset($data);
}
return $results;
}
/**
* {@inheritdoc}
*/
public function clear()
{
$this->pdo->exec($this->getSqlQuery('clear'));
}
/**
* Get a SQL Query for a task, with the variables replaced
*
* @param string $name
* @param array $vars
* @return string
*/
protected function getSqlQuery($name, array $vars = array())
{
$sql = $this->sqlQueries[$name];
$vars = array_merge(array('tablename' => $this->tableName), $vars);
foreach ($vars as $k => $v) {
$sql = str_replace("%$k%", $v, $sql);
}
return $sql;
}
}

View File

@ -0,0 +1,87 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\Storage;
use Predis\Client;
/**
* Stores collected data into Redis
*/
class RedisStorage implements StorageInterface
{
protected $redis;
protected $hash;
/**
* @param \Predis\Client $redis Redis Client
* @param string $hash
*/
public function __construct(Client $redis, $hash = 'phpdebugbar')
{
$this->redis = $redis;
$this->hash = $hash;
}
/**
* {@inheritdoc}
*/
public function save($id, $data)
{
$this->redis->hset($this->hash, $id, serialize($data));
}
/**
* {@inheritdoc}
*/
public function get($id)
{
return unserialize($this->redis->hget($this->hash, $id));
}
/**
* {@inheritdoc}
*/
public function find(array $filters = array(), $max = 20, $offset = 0)
{
$results = array();
foreach ($this->redis->hgetall($this->hash) as $id => $data) {
if ($data = unserialize($data)) {
$meta = $data['__meta'];
if ($this->filter($meta, $filters)) {
$results[] = $meta;
}
}
}
return array_slice($results, $offset, $max);
}
/**
* Filter the metadata for matches.
*/
protected function filter($meta, $filters)
{
foreach ($filters as $key => $value) {
if (!isset($meta[$key]) || fnmatch($value, $meta[$key]) === false) {
return false;
}
}
return true;
}
/**
* {@inheritdoc}
*/
public function clear()
{
$this->redis->del($this->hash);
}
}

View File

@ -0,0 +1,45 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\Storage;
interface StorageInterface
{
/**
* Saves collected data
*
* @param string $id
* @param string $data
*/
function save($id, $data);
/**
* Returns collected data with the specified id
*
* @param string $id
* @return array
*/
function get($id);
/**
* Returns a metadata about collected data
*
* @param array $filters
* @param integer $max
* @param integer $offset
* @return array
*/
function find(array $filters = array(), $max = 20, $offset = 0);
/**
* Clears all the collected data
*/
function clear();
}

View File

@ -0,0 +1,16 @@
CREATE TABLE phpdebugbar (
id TEXT PRIMARY KEY,
data TEXT,
meta_utime TEXT,
meta_datetime TEXT,
meta_uri TEXT,
meta_ip TEXT,
meta_method TEXT
);
CREATE INDEX idx_debugbar_id ON phpdebugbar (id);
CREATE INDEX idx_debugbar_meta_utime ON phpdebugbar (meta_utime);
CREATE INDEX idx_debugbar_meta_datetime ON phpdebugbar (meta_datetime);
CREATE INDEX idx_debugbar_meta_uri ON phpdebugbar (meta_uri);
CREATE INDEX idx_debugbar_meta_ip ON phpdebugbar (meta_ip);
CREATE INDEX idx_debugbar_meta_method ON phpdebugbar (meta_method);