first commit

This commit is contained in:
Sampanna Rimal
2024-08-27 17:48:06 +05:45
commit 53c0140f58
10839 changed files with 1125847 additions and 0 deletions

19
vendor/maximebf/debugbar/LICENSE vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright (C) 2013 Maxime Bouroumeau-Fuseau
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

62
vendor/maximebf/debugbar/composer.json vendored Normal file
View File

@ -0,0 +1,62 @@
{
"name": "maximebf/debugbar",
"description": "Debug bar in the browser for php application",
"keywords": ["debug", "debugbar"],
"homepage": "https://github.com/maximebf/php-debugbar",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Maxime Bouroumeau-Fuseau",
"email": "maxime.bouroumeau@gmail.com",
"homepage": "http://maximebf.com"
},
{
"name": "Barry vd. Heuvel",
"email": "barryvdh@gmail.com"
}
],
"require": {
"php": "^7.2|^8",
"psr/log": "^1|^2|^3",
"symfony/var-dumper": "^4|^5|^6|^7"
},
"require-dev": {
"phpunit/phpunit": "^8|^9",
"twig/twig": "^1.38|^2.7|^3.0",
"symfony/panther": "^1|^2.1",
"dbrekelmans/bdi": "^1"
},
"autoload": {
"psr-4": {
"DebugBar\\": "src/DebugBar/"
}
},
"autoload-dev": {
"psr-4": {
"DebugBar\\Tests\\": "tests/DebugBar/Tests"
}
},
"scripts": {
"demo": [
"Composer\\Config::disableProcessTimeout",
"@php -S localhost:8000"
],
"unit-test": "@php vendor/bin/phpunit --testsuite=Unit",
"browser-test": "@php vendor/bin/phpunit --testsuite=Browser",
"browser-debug": [
"@putenv PANTHER_NO_HEADLESS=1",
"@php vendor/bin/phpunit --testsuite=Browser --debug"
]
},
"suggest": {
"kriswallsmith/assetic": "The best way to manage assets",
"monolog/monolog": "Log using Monolog",
"predis/predis": "Redis storage"
},
"extra": {
"branch-alias": {
"dev-master": "1.22-dev"
}
}
}

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|\Monolog\LogRecord $record
*/
protected function write($record): void
{
$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,213 @@
<?php
declare(strict_types=1);
namespace DebugBar\Bridge;
use DebugBar\DataCollector\AssetProvider;
use DebugBar\DataCollector\DataCollector;
use DebugBar\DataCollector\Renderable;
use Twig\Environment;
use Twig\Loader\LoaderInterface;
use Twig\Profiler\Dumper\HtmlDumper;
use Twig\Profiler\Profile;
/**
* Collects data about rendered templates
*
* http://twig.sensiolabs.org/
*
* A \Twig\Profiler\Profile should be added to your \Twig\Environment
* The root-Twig\Profiler\Profile-object should then be injected into this collector
*
* you can optionally provide the \Twig\Environment or the \Twig\Loader to also create
* debug-links.
*
* @see \Twig\Extension\ProfilerExtension, \Twig\Profiler\Profile
*
* <code>
* $env = new \Twig\Environment($loader); // Or from a PSR11-container
* $profile = new \Twig\Profiler\Profile();
* $env->addExtension(new \Twig\Extension\ProfilerExtension($profile));
* $debugbar->addCollector(new ModernTwigProfileCollector($profile, $env));
* // or: $debugbar->addCollector(new ModernTwigProfileCollector($profile, $loader));
* </code>
*/
class NamespacedTwigProfileCollector extends DataCollector implements Renderable, AssetProvider
{
/**
* @var Profile
*/
private $profile;
/**
* @var LoaderInterface|Environment|null
*/
private $loader;
/**
* @var int
*/
private $templateCount;
/**
* @var int
*/
private $blockCount;
/**
* @var int
*/
private $macroCount;
/**
* @var array[] {
* @var string $name
* @var int $render_time
* @var string $render_time_str
* @var string $memory_str
* @var string $xdebug_link
* }
*/
private $templates;
/**
* TwigProfileCollector constructor.
*
* @param Profile $profile
* @param LoaderInterface|Environment $loaderOrEnv
*/
public function __construct(Profile $profile, $loaderOrEnv = null)
{
$this->profile = $profile;
$this->setLoaderOrEnv($loaderOrEnv);
}
/**
* @param LoaderInterface|Environment $loaderOrEnv
*/
public function setLoaderOrEnv($loaderOrEnv)
{
if ($loaderOrEnv instanceof Environment) {
$loaderOrEnv = $loaderOrEnv->getLoader();
}
$this->loader = $loaderOrEnv;
}
/**
* Returns a hash where keys are control names and their values
* an array of options as defined in {@see \DebugBar\JavascriptRenderer::addControl()}
*
* @return array
*/
public function getWidgets()
{
return [
'twig' => [
'icon' => 'leaf',
'widget' => 'PhpDebugBar.Widgets.TemplatesWidget',
'map' => 'twig',
'default' => json_encode(['templates' => []]),
],
'twig:badge' => [
'map' => 'twig.badge',
'default' => 0,
],
];
}
/**
* @return array
*/
public function getAssets()
{
return [
'css' => 'widgets/templates/widget.css',
'js' => 'widgets/templates/widget.js',
];
}
/**
* Called by the DebugBar when data needs to be collected
*
* @return array Collected data
*/
public function collect()
{
$this->templateCount = $this->blockCount = $this->macroCount = 0;
$this->templates = [];
$this->computeData($this->profile);
return [
'nb_templates' => $this->templateCount,
'nb_blocks' => $this->blockCount,
'nb_macros' => $this->macroCount,
'templates' => $this->templates,
'accumulated_render_time' => $this->profile->getDuration(),
'accumulated_render_time_str' => $this->getDataFormatter()->formatDuration($this->profile->getDuration()),
'memory_usage_str' => $this->getDataFormatter()->formatBytes($this->profile->getMemoryUsage()),
'callgraph' => $this->getHtmlCallGraph(),
'badge' => implode(
'/',
[
$this->templateCount,
$this->blockCount,
$this->macroCount,
]
),
];
}
/**
* Returns the unique name of the collector
*
* @return string
*/
public function getName()
{
return 'twig';
}
public function getHtmlCallGraph()
{
$dumper = new HtmlDumper();
return $dumper->dump($this->profile);
}
/**
* Get an Xdebug Link to a file
*
* @return array {
* @var string url
* @var bool ajax
* }
*/
public function getXdebugLink($template, $line = 1)
{
if (is_null($this->loader)) {
return null;
}
$file = $this->loader->getSourceContext($template)->getPath();
return parent::getXdebugLink($file, $line);
}
private function computeData(Profile $profile)
{
$this->templateCount += ($profile->isTemplate() ? 1 : 0);
$this->blockCount += ($profile->isBlock() ? 1 : 0);
$this->macroCount += ($profile->isMacro() ? 1 : 0);
if ($profile->isTemplate()) {
$this->templates[] = [
'name' => $profile->getName(),
'render_time' => $profile->getDuration(),
'render_time_str' => $this->getDataFormatter()->formatDuration($profile->getDuration()),
'memory_str' => $this->getDataFormatter()->formatBytes($profile->getMemoryUsage()),
'xdebug_link' => $this->getXdebugLink($profile->getTemplate()),
];
}
foreach ($profile as $p) {
$this->computeData($p);
}
}
}

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,53 @@
<?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()
{
$dump = '';
foreach ($this->messages as $message) {
if (!$message['is_string']) {
continue;
}
$dump .= $message['message'] . PHP_EOL;
}
return $dump;
}
public function getName()
{
return 'swiftmailer_logs';
}
}

View File

@ -0,0 +1,103 @@
<?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;
/** @var bool */
private $showBody = false;
public function __construct(Swift_Mailer $mailer)
{
$this->messagesLogger = new Swift_Plugins_MessageLogger();
$mailer->registerPlugin($this->messagesLogger);
}
public function showMessageBody($show = true)
{
$this->showBody = $show;
}
public function collect()
{
$mails = array();
foreach ($this->messagesLogger->getMessages() as $msg) {
$html = $this->showBody ? $msg->getBody() : null;
$mails[] = array(
'to' => $this->formatTo($msg->getTo()),
'subject' => $msg->getSubject(),
'headers' => $msg->getHeaders()->toString(),
'body' => $html,
'html' => $html,
);
}
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,110 @@
<?php
namespace DebugBar\Bridge\Symfony;
use DebugBar\DataCollector\AssetProvider;
use DebugBar\DataCollector\DataCollector;
use DebugBar\DataCollector\Renderable;
use Symfony\Component\Mime\Part\AbstractPart;
/**
* Collects data about sent mail events
*
* https://github.com/symfony/mailer
*/
class SymfonyMailCollector extends DataCollector implements Renderable, AssetProvider
{
/** @var array */
private $messages = array();
/** @var bool */
private $showDetailed = false;
/** @var bool */
private $showBody = false;
/** @param \Symfony\Component\Mailer\SentMessage $message */
public function addSymfonyMessage($message)
{
$this->messages[] = $message->getOriginalMessage();
}
/**
* @deprecated use showMessageBody()
*/
public function showMessageDetail()
{
$this->showMessageBody(true);
}
public function showMessageBody($show = true)
{
$this->showBody = $show;
}
public function collect()
{
$mails = array();
foreach ($this->messages as $message) {
/* @var \Symfony\Component\Mime\Message $message */
$mail = [
'to' => array_map(function ($address) {
/* @var \Symfony\Component\Mime\Address $address */
return $address->toString();
}, $message->getTo()),
'subject' => $message->getSubject(),
'headers' => $message->getHeaders()->toString(),
'body' => null,
'html' => null,
];
if ($this->showBody) {
$body = $message->getBody();
if ($body instanceof AbstractPart) {
$mail['html'] = $message->getHtmlBody();
$mail['body'] = $message->getTextBody();
} else {
$mail['body'] = $body->bodyToString();
}
}
$mails[] = $mail;
}
return array(
'count' => count($mails),
'mails' => $mails,
);
}
public function getName()
{
return 'symfonymailer_mails';
}
public function getWidgets()
{
return array(
'emails' => array(
'icon' => 'inbox',
'widget' => 'PhpDebugBar.Widgets.MailsWidget',
'map' => 'symfonymailer_mails.mails',
'default' => '[]',
'title' => 'Mails'
),
'emails:badge' => array(
'map' => 'symfonymailer_mails.count',
'default' => 'null'
)
);
}
public function getAssets()
{
return array(
'css' => 'widgets/mails/widget.css',
'js' => 'widgets/mails/widget.js'
);
}
}

View File

@ -0,0 +1,94 @@
<?php
namespace DebugBar\Bridge\Twig;
use Twig\Environment;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
/**
* Debug messages to debugbar in your Twig templates.
*
* @package DebugBar\Bridge\Twig
*/
class DebugTwigExtension extends AbstractExtension
{
/**
* @var \DebugBar\DataCollector\MessagesCollector|null
*/
protected $messagesCollector;
/**
* @var string
*/
protected $functionName;
/**
*
* @param \DebugBar\DataCollector\MessagesCollector|null $app
* @param string $functionName
*/
public function __construct($messagesCollector, $functionName = 'debug')
{
$this->messagesCollector = $messagesCollector;
$this->functionName = $functionName;
}
/**
* {@inheritDoc}
*/
public function getName()
{
return static::class;
}
/**
* {@inheritDoc}
*/
public function getFunctions()
{
return [
new TwigFunction(
$this->functionName,
[$this, 'debug'],
['needs_context' => true, 'needs_environment' => true]
),
];
}
/**
* Based on Twig_Extension_Debug / twig_var_dump
*
* @param Environment $env
* @param $context
*/
public function debug(Environment $env, $context)
{
if (!$env->isDebug() || !$this->messagesCollector) {
return;
}
$count = func_num_args();
if (2 === $count) {
$data = [];
foreach ($context as $key => $value) {
if (is_object($value)) {
if (method_exists($value, 'toArray')) {
$data[$key] = $value->toArray();
} else {
$data[$key] = "Object (" . get_class($value) . ")";
}
} else {
$data[$key] = $value;
}
}
$this->messagesCollector->addMessage($data, 'debug');
} else {
for ($i = 2; $i < $count; $i++) {
$this->messagesCollector->addMessage(func_get_arg($i), 'debug');
}
}
return;
}
}

View File

@ -0,0 +1,99 @@
<?php
namespace DebugBar\Bridge\Twig;
use DebugBar\DataFormatter\HasDataFormatter;
use Twig\Environment;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
/**
* Dump variables using debugbar DataFormatter
*
* @package DebugBar\Bridge\Twig
*/
class DumpTwigExtension extends AbstractExtension
{
use HasDataFormatter;
/**
* @var string
*/
protected $functionName;
/**
* Create a new auth extension.
*
* @param string $functionName
*/
public function __construct($functionName = 'dump')
{
$this->functionName = $functionName;
}
/**
* {@inheritDoc}
*/
public function getName()
{
return static::class;
}
/**
* {@inheritDoc}
*/
public function getFunctions()
{
return [
new TwigFunction(
$this->functionName,
[$this, 'dump'],
['is_safe' => ['html'], 'needs_context' => true, 'needs_environment' => true]
),
];
}
/**
* Based on Twig_Extension_Debug / twig_var_dump
*
* @param Environment $env
* @param $context
*
* @return string
*/
public function dump(Environment $env, $context)
{
if (!$env->isDebug()) {
return;
}
$output = '';
$count = func_num_args();
if (2 === $count) {
$data = [];
foreach ($context as $key => $value) {
if (is_object($value)) {
if (method_exists($value, 'toArray')) {
$data[$key] = $value->toArray();
} else {
$data[$key] = "Object (" . get_class($value) . ")";
}
} else {
$data[$key] = $value;
}
}
$output .= $this->formatVar($data);
} else {
for ($i = 2; $i < $count; $i++) {
$output .= $this->formatVar(func_get_arg($i));
}
}
if ($this->isHtmlVarDumperUsed()) {
return $output;
}
return '<pre>' . $output . '</pre>';
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace DebugBar\Bridge\Twig;
use Twig\Extension\AbstractExtension;
/**
* Access debugbar timeline measure in your Twig templates.
* Based on Symfony\Bridge\Twig\Extension\StopwatchExtension
*
* @package DebugBar\Bridge\Twig
*/
class MeasureTwigExtension extends AbstractExtension
{
/**
* @var \DebugBar\DataCollector\TimeDataCollector|null
*/
protected $timeCollector;
/**
* @var string
*/
protected $tagName;
/**
* Create a new auth extension.
*
* @param \DebugBar\DataCollector\TimeDataCollector|null $debugbar
* @param string $tagName
*/
public function __construct($timeCollector, $tagName = 'measure')
{
$this->timeCollector = $timeCollector;
$this->tagName = $tagName;
}
/**
* {@inheritDoc}
*/
public function getName()
{
return static::class;
}
/**
* @return \Twig\TokenParser\TokenParserInterface[]
*/
public function getTokenParsers()
{
return [
/*
* {% measure foo %}
* Some stuff which will be recorded on the timeline
* {% endmeasure %}
*/
new MeasureTwigTokenParser(!is_null($this->timeCollector), $this->tagName, $this->getName()),
];
}
public function startMeasure(...$arg)
{
if (!$this->timeCollector) {
return;
}
$this->timeCollector->startMeasure(...$arg);
}
public function stopMeasure(...$arg)
{
if (!$this->timeCollector) {
return;
}
$this->timeCollector->stopMeasure(...$arg);
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace DebugBar\Bridge\Twig;
use DebugBar\Bridge\Twig\MeasureTwigExtension;
use Twig\Compiler;
use Twig\Node\Expression\AssignNameExpression;
use Twig\Node\Node;
/**
* Represents a measure node.
* Based on Symfony\Bridge\Twig\Node\StopwatchNode
*/
class MeasureTwigNode extends Node
{
/**
* @var string
*/
protected $extName;
public function __construct(
Node $name,
$body,
AssignNameExpression $var,
$lineno = 0,
$tag = null,
$extName = null
) {
parent::__construct(['body' => $body, 'name' => $name, 'var' => $var], [], $lineno, $tag);
$this->extName = $extName ?: MeasureTwigExtension::class;
}
public function compile(Compiler $compiler)
{
$compiler
->addDebugInfo($this)
->write('')
->subcompile($this->getNode('var'))
->raw(' = ')
->subcompile($this->getNode('name'))
->write(";\n")
->write("\$this->env->getExtension('".$this->extName."')->startMeasure(")
->subcompile($this->getNode('var'))
->raw(");\n")
->subcompile($this->getNode('body'))
->write("\$this->env->getExtension('".$this->extName."')->stopMeasure(")
->subcompile($this->getNode('var'))
->raw(");\n");
}
}

View File

@ -0,0 +1,78 @@
<?php
namespace DebugBar\Bridge\Twig;
use Twig\Node\Expression\AssignNameExpression;
use Twig\Token;
use Twig\TokenParser\AbstractTokenParser;
/**
* Token Parser for the measure tag.
* Based on Symfony\Bridge\Twig\TokenParser\StopwatchTokenParser;
*/
class MeasureTwigTokenParser extends AbstractTokenParser
{
/**
* @var string
*/
private $extName;
/**
* @var string
*/
private $tagName;
/**
* @var bool
*/
private $enabled;
/**
*
* @param string $tagName
*/
public function __construct($enabled, $tagName, $extName = null)
{
$this->enabled = $enabled;
$this->tagName = $tagName;
$this->extName = $extName;
}
public function parse(Token $token)
{
$lineno = $token->getLine();
$stream = $this->parser->getStream();
// {% measure 'bar' %}
$name = $this->parser->getExpressionParser()->parseExpression();
$stream->expect(Token::BLOCK_END_TYPE);
// {% endmeasure %}
$body = $this->parser->subparse([$this, 'decideMeasureEnd'], true);
$stream->expect(Token::BLOCK_END_TYPE);
if ($this->enabled) {
return new MeasureTwigNode(
$name,
$body,
new AssignNameExpression($this->parser->getVarName(), $token->getLine()),
$lineno,
$this->getTag(),
$this->extName
);
}
return $body;
}
public function getTag()
{
return $this->tagName;
}
public function decideMeasureEnd(Token $token)
{
return $token->test('end'.$this->getTag());
}
}

View File

@ -0,0 +1,61 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2017 Tim Riemenschneider
*
* 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\Extension\ProfilerExtension;
use Twig\Profiler\Profile;
/**
* Class TimeableTwigExtensionProfiler
*
* Extends ProfilerExtension to add rendering times to the TimeDataCollector
*
* @package DebugBar\Bridge\Twig
*/
class TimeableTwigExtensionProfiler extends ProfilerExtension
{
/**
* @var \DebugBar\DataCollector\TimeDataCollector
*/
private $timeDataCollector;
/**
* @param \DebugBar\DataCollector\TimeDataCollector $timeDataCollector
*/
public function setTimeDataCollector(TimeDataCollector $timeDataCollector)
{
$this->timeDataCollector = $timeDataCollector;
}
public function __construct(Profile $profile, TimeDataCollector $timeDataCollector = null)
{
parent::__construct($profile);
$this->timeDataCollector = $timeDataCollector;
}
public function enter(Profile $profile)
{
if ($this->timeDataCollector && $profile->isTemplate()) {
$this->timeDataCollector->startMeasure($profile->getName(), 'template ' . $profile->getName());
}
parent::enter($profile);
}
public function leave(Profile $profile)
{
parent::leave($profile);
if ($this->timeDataCollector && $profile->isTemplate()) {
$this->timeDataCollector->stopMeasure($profile->getName());
}
}
}

View File

@ -0,0 +1,419 @@
<?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
*
* @deprecated
*/
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,138 @@
<?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
*
* @deprecated
*/
class TraceableTwigTemplate extends Twig_Template 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 doDisplay(array $context, array $blocks = array())
{
return $this->template->doDisplay($context, $blocks);
}
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,89 @@
<?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>
*
* @deprecated use DebugBar\Bridge\TwigProfileCollector instead
*/
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' => json_encode(array('templates' => array())),
),
'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,199 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2017 Tim Riemenschneider
*
* 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;
/**
* Collects data about rendered templates
*
* http://twig.sensiolabs.org/
*
* A Twig_Extension_Profiler should be added to your Twig_Environment
* The root-Twig_Profiler_Profile-object should then be injected into this collector
*
* you can optionally provide the Twig_Environment or the Twig_Loader to also create
* debug-links.
*
* @see \Twig_Extension_Profiler, \Twig_Profiler_Profile
*
* <code>
* $env = new Twig_Environment($loader); // Or from a PSR11-container
* $profile = new Twig_Profiler_Profile();
* $env->addExtension(new Twig_Extension_Profile($profile));
* $debugbar->addCollector(new TwigProfileCollector($profile, $env));
* // or: $debugbar->addCollector(new TwigProfileCollector($profile, $loader));
* </code>
*
* @deprecated Use `\Debugbar\Bridge\NamespacedTwigProfileCollector` instead for Twig 2.x and 3.x
*/
class TwigProfileCollector extends DataCollector implements Renderable, AssetProvider
{
/**
* @var \Twig_Profiler_Profile
*/
private $profile;
/**
* @var \Twig_LoaderInterface
*/
private $loader;
/** @var int */
private $templateCount;
/** @var int */
private $blockCount;
/** @var int */
private $macroCount;
/**
* @var array[] {
* @var string $name
* @var int $render_time
* @var string $render_time_str
* @var string $memory_str
* @var string $xdebug_link
* }
*/
private $templates;
/**
* TwigProfileCollector constructor.
*
* @param \Twig_Profiler_Profile $profile
* @param \Twig_LoaderInterface|\Twig_Environment $loaderOrEnv
*/
public function __construct(\Twig_Profiler_Profile $profile, $loaderOrEnv = null)
{
$this->profile = $profile;
if ($loaderOrEnv instanceof \Twig_Environment) {
$loaderOrEnv = $loaderOrEnv->getLoader();
}
$this->loader = $loaderOrEnv;
}
/**
* Returns a hash where keys are control names and their values
* an array of options as defined in {@see DebugBar\JavascriptRenderer::addControl()}
*
* @return array
*/
public function getWidgets()
{
return array(
'twig' => array(
'icon' => 'leaf',
'widget' => 'PhpDebugBar.Widgets.TemplatesWidget',
'map' => 'twig',
'default' => json_encode(array('templates' => array())),
),
'twig:badge' => array(
'map' => 'twig.badge',
'default' => 0,
),
);
}
/**
* @return array
*/
public function getAssets()
{
return array(
'css' => 'widgets/templates/widget.css',
'js' => 'widgets/templates/widget.js',
);
}
/**
* Called by the DebugBar when data needs to be collected
*
* @return array Collected data
*/
public function collect()
{
$this->templateCount = $this->blockCount = $this->macroCount = 0;
$this->templates = array();
$this->computeData($this->profile);
return array(
'nb_templates' => $this->templateCount,
'nb_blocks' => $this->blockCount,
'nb_macros' => $this->macroCount,
'templates' => $this->templates,
'accumulated_render_time' => $this->profile->getDuration(),
'accumulated_render_time_str' => $this->getDataFormatter()->formatDuration($this->profile->getDuration()),
'memory_usage_str' => $this->getDataFormatter()->formatBytes($this->profile->getMemoryUsage()),
'callgraph' => $this->getHtmlCallGraph(),
'badge' => implode(
'/',
array(
$this->templateCount,
$this->blockCount,
$this->macroCount,
)
),
);
}
/**
* Returns the unique name of the collector
*
* @return string
*/
public function getName()
{
return 'twig';
}
public function getHtmlCallGraph()
{
$dumper = new \Twig_Profiler_Dumper_Html();
return $dumper->dump($this->profile);
}
/**
* Get an Xdebug Link to a file
*
* @return array {
* @var string url
* @var bool ajax
* }
*/
public function getXdebugLink($template, $line = 1)
{
if (is_null($this->loader)) {
return null;
}
$file = $this->loader->getSourceContext($template)->getPath();
return parent::getXdebugLink($file, $line);
}
private function computeData(\Twig_Profiler_Profile $profile)
{
$this->templateCount += ($profile->isTemplate() ? 1 : 0);
$this->blockCount += ($profile->isBlock() ? 1 : 0);
$this->macroCount += ($profile->isMacro() ? 1 : 0);
if ($profile->isTemplate()) {
$this->templates[] = array(
'name' => $profile->getName(),
'render_time' => $profile->getDuration(),
'render_time_str' => $this->getDataFormatter()->formatDuration($profile->getDuration()),
'memory_str' => $this->getDataFormatter()->formatBytes($profile->getMemoryUsage()),
'xdebug_link' => $this->getXdebugLink($profile->getTemplate()),
);
}
foreach ($profile as $p) {
$this->computeData($p);
}
}
}

View File

@ -0,0 +1,190 @@
<?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) : void
{
$this->collectors[$collector->getName()] = $collector;
}
/**
* @return array
*/
public function getCollectors() : array
{
return $this->collectors;
}
/**
* Merge data from one of the key/value pair of the collected data
*
* @param string $property
*/
public function setMergeProperty($property) : void
{
$this->mergeProperty = $property;
}
/**
* @return string
*/
public function getMergeProperty() : string
{
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) : void
{
$this->sort = $sort;
}
/**
* @return bool|string
*/
public function getSort()
{
return $this->sort;
}
/**
* @return array
*/
public function collect() : array
{
$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) : array
{
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() : string
{
return $this->name;
}
// --------------------------------------------
// ArrayAccess implementation
/**
* @param mixed $key
* @param mixed $value
* @throws DebugBarException
*/
public function offsetSet($key, $value): void
{
throw new DebugBarException("AggregatedCollector[] is read-only");
}
/**
* @param mixed $key
* @return mixed
*/
#[\ReturnTypeWillChange]
public function offsetGet($key)
{
return $this->collectors[$key];
}
/**
* @param mixed $key
* @return bool
*/
public function offsetExists($key): bool
{
return isset($this->collectors[$key]);
}
/**
* @param mixed $key
* @throws DebugBarException
*/
public function offsetUnset($key): void
{
throw new DebugBarException("AggregatedCollector[] is read-only");
}
}

View File

@ -0,0 +1,43 @@
<?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
* - inline_css: an array map of content ID to inline CSS content (not including <style> tag)
* - inline_js: an array map of content ID to inline JS content (not including <script> tag)
* - inline_head: an array map of content ID to arbitrary inline HTML content (typically
* <style>/<script> tags); it must be embedded within the <head> element
*
* All keys are optional.
*
* Ideally, you should store static assets in filenames that are returned via the normal css/js
* keys. However, the inline asset elements are useful when integrating with 3rd-party
* libraries that require static assets that are only available in an inline format.
*
* The inline content arrays require special string array keys: the caller of this function
* will use them to deduplicate content. This is particularly useful if multiple instances of
* the same asset provider are used. Inline assets from all collectors are merged together into
* the same array, so these content IDs effectively deduplicate the inline assets.
*
* @return array
*/
function getAssets();
}

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\DataCollector;
/**
* Collects array data
*/
class ConfigCollector extends DataCollector implements Renderable, AssetProvider
{
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 ($this->isHtmlVarDumperUsed()) {
$v = $this->getVarDumper()->renderVar($v);
} else 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 getAssets() {
return $this->isHtmlVarDumperUsed() ? $this->getVarDumper()->getAssets() : array();
}
/**
* @return array
*/
public function getWidgets()
{
$name = $this->getName();
$widget = $this->isHtmlVarDumperUsed()
? "PhpDebugBar.Widgets.HtmlVariableListWidget"
: "PhpDebugBar.Widgets.VariableListWidget";
return array(
"$name" => array(
"icon" => "gear",
"widget" => $widget,
"map" => "$name",
"default" => "{}"
)
);
}
}

View File

@ -0,0 +1,27 @@
<?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\HasDataFormatter;
use DebugBar\DataFormatter\HasXdebugLinks;
/**
* Abstract class for data collectors
*/
abstract class DataCollector implements DataCollectorInterface
{
use HasDataFormatter, HasXdebugLinks;
public static $defaultDataFormatter;
public static $defaultVarDumper;
}

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,191 @@
<?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;
use Symfony\Component\Debug\Exception\FatalThrowableError;
/**
* 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
* @deprecated in favor on addThrowable
*/
public function addException(Exception $e)
{
$this->addThrowable($e);
}
/**
* Adds a Throwable to be profiled in the debug bar
*
* @param \Throwable $e
*/
public function addThrowable($e)
{
$this->exceptions[] = $e;
if ($this->chainExceptions && $previous = $e->getPrevious()) {
$this->addThrowable($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[\Throwable]
*/
public function getExceptions()
{
return $this->exceptions;
}
public function collect()
{
return array(
'count' => count($this->exceptions),
'exceptions' => array_map(array($this, 'formatThrowableData'), $this->exceptions)
);
}
/**
* Returns exception data as an array
*
* @param Exception $e
* @return array
* @deprecated in favor on formatThrowableData
*/
public function formatExceptionData(Exception $e)
{
return $this->formatThrowableData($e);
}
/**
* Returns Throwable trace as an formated array
*
* @return array
*/
public function formatTrace(array $trace)
{
if (! empty($this->xdebugReplacements)) {
$trace = array_map(function ($track) {
if (isset($track['file'])) {
$track['file'] = $this->normalizeFilePath($track['file']);
}
return $track;
}, $trace);
}
return $trace;
}
/**
* Returns Throwable data as an string
*
* @param \Throwable $e
* @return string
*/
public function formatTraceAsString($e)
{
if (! empty($this->xdebugReplacements)) {
return implode("\n", array_map(function ($track) {
$track = explode(' ', $track);
if (isset($track[1])) {
$track[1] = $this->normalizeFilePath($track[1]);
}
return implode(' ', $track);
}, explode("\n", $e->getTraceAsString())));
}
return $e->getTraceAsString();
}
/**
* Returns Throwable data as an array
*
* @param \Throwable $e
* @return array
*/
public function formatThrowableData($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 ('.$this->normalizeFilePath($filePath).') in which the exception occurred');
}
$traceHtml = null;
if ($this->isHtmlVarDumperUsed()) {
$traceHtml = $this->getVarDumper()->renderVar($this->formatTrace($e->getTrace()));
}
return array(
'type' => get_class($e),
'message' => $e->getMessage(),
'code' => $e->getCode(),
'file' => $this->normalizeFilePath($filePath),
'line' => $e->getLine(),
'stack_trace' => $traceHtml ? null : $this->formatTraceAsString($e),
'stack_trace_html' => $traceHtml,
'surrounding_lines' => $lines,
'xdebug_link' => $this->getXdebugLink($filePath, $e->getLine())
);
}
/**
* @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,123 @@
<?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 $realUsage = false;
protected $memoryRealStart = 0;
protected $memoryStart = 0;
protected $peakUsage = 0;
protected $precision = 0;
/**
* Set the precision of the 'peak_usage_str' output.
*
* @param int $precision
*/
public function setPrecision($precision)
{
$this->precision = $precision;
}
/**
* Returns whether total allocated memory page size is used instead of actual used memory size
* by the application. See $real_usage parameter on memory_get_peak_usage for details.
*
* @return bool
*/
public function getRealUsage()
{
return $this->realUsage;
}
/**
* Sets whether total allocated memory page size is used instead of actual used memory size
* by the application. See $real_usage parameter on memory_get_peak_usage for details.
*
* @param bool $realUsage
*/
public function setRealUsage($realUsage)
{
$this->realUsage = $realUsage;
}
/**
* Reset memory baseline, to measure multiple requests in a long running process
*
* @return void
*/
public function resetMemoryBaseline()
{
$this->memoryStart = memory_get_usage(false);
$this->memoryRealStart = memory_get_usage(true);
}
/**
* Returns the peak memory usage
*
* @return integer
*/
public function getPeakUsage()
{
return $this->peakUsage - ($this->realUsage ? $this->memoryRealStart : $this->memoryStart);
}
/**
* Updates the peak memory usage value
*/
public function updatePeakUsage()
{
$this->peakUsage = memory_get_peak_usage($this->realUsage);
}
/**
* @return array
*/
public function collect()
{
$this->updatePeakUsage();
return array(
'peak_usage' => $this->getPeakUsage(),
'peak_usage_str' => $this->getDataFormatter()->formatBytes($this->getPeakUsage(), $this->precision)
);
}
/**
* @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,252 @@
<?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\HasXdebugLinks;
use Psr\Log\AbstractLogger;
use DebugBar\DataFormatter\HasDataFormatter;
/**
* Provides a way to log messages
*/
class MessagesCollector extends AbstractLogger implements DataCollectorInterface, MessagesAggregateInterface, Renderable, AssetProvider
{
use HasDataFormatter, HasXdebugLinks;
protected $name;
protected $messages = array();
protected $aggregates = array();
/** @var bool */
protected $collectFile = false;
/**
* @param string $name
*/
public function __construct($name = 'messages')
{
$this->name = $name;
}
/** @return void */
public function collectFileTrace($enabled = true)
{
$this->collectFile = $enabled;
}
/**
* @param string|null $messageHtml
* @param mixed $message
*
* @return string|null
*/
protected function customizeMessageHtml($messageHtml, $message)
{
$pos = strpos((string) $messageHtml, 'sf-dump-expanded');
if ($pos !== false) {
$messageHtml = substr_replace($messageHtml, 'sf-dump-compact', $pos, 16);
}
return $messageHtml;
}
/**
* 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)
{
$messageText = $message;
$messageHtml = null;
if (!is_string($message)) {
// Send both text and HTML representations; the text version is used for searches
$messageText = $this->getDataFormatter()->formatVar($message);
if ($this->isHtmlVarDumperUsed()) {
$messageHtml = $this->getVarDumper()->renderVar($message);
}
$isString = false;
}
$stackItem = [];
if ($this->collectFile) {
$stacktrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5);
$stackItem = $stacktrace[0];
foreach ($stacktrace as $trace) {
if (!isset($trace['file']) || strpos($trace['file'], '/vendor/') !== false) {
continue;
}
$stackItem = $trace;
break;
}
}
$this->messages[] = array(
'message' => $messageText,
'message_html' => $this->customizeMessageHtml($messageHtml, $message),
'is_string' => $isString,
'label' => $label,
'time' => microtime(true),
'xdebug_link' => $stackItem ? $this->getXdebugLink($stackItem['file'], $stackItem['line'] ?? null) : null,
);
}
/**
* Aggregates messages from other collectors
*
* @param MessagesAggregateInterface $messages
*/
public function aggregate(MessagesAggregateInterface $messages)
{
if ($this->collectFile && method_exists($messages, 'collectFileTrace')) {
$messages->collectFileTrace();
}
$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()): void
{
// For string messages, interpolate the context following PSR-3
if (is_string($message)) {
$message = $this->interpolate($message, $context);
}
$this->addMessage($message, $level);
}
/**
* Interpolates context values into the message placeholders.
*
* @param string $message
* @param array $context
* @return string
*/
function interpolate($message, array $context = array())
{
// build a replacement array with braces around the context keys
$replace = array();
foreach ($context as $key => $val) {
$placeholder = '{' . $key . '}';
if (strpos($message, $placeholder) === false) {
continue;
}
// check that the value can be cast to string
if (null === $val || is_scalar($val) || (is_object($val) && method_exists($val, "__toString"))) {
$replace[$placeholder] = $val;
} elseif ($val instanceof \DateTimeInterface) {
$replace[$placeholder] = $val->format("Y-m-d\TH:i:s.uP");
} elseif ($val instanceof \UnitEnum) {
$replace[$placeholder] = $val instanceof \BackedEnum ? $val->value : $val->name;
} elseif (is_object($val)) {
$replace[$placeholder] = '[object ' . $this->getDataFormatter()->formatClassName($val) . ']';
} elseif (is_array($val)) {
$json = @json_encode($val);
$replace[$placeholder] = false === $json ? 'null' : 'array' . $json;
} else {
$replace[$placeholder] = '['.gettype($val).']';
}
}
// interpolate replacement values into the message and return
return strtr($message, $replace);
}
/**
* 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 getAssets() {
return $this->isHtmlVarDumperUsed() ? $this->getVarDumper()->getAssets() : array();
}
/**
* @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,102 @@
<?php
namespace DebugBar\DataCollector;
use DebugBar\DataCollector\DataCollector;
use DebugBar\DataCollector\DataCollectorInterface;
use DebugBar\DataCollector\Renderable;
/**
* Collector for hit counts.
*/
class ObjectCountCollector extends DataCollector implements DataCollectorInterface, Renderable
{
/** @var string */
private $name;
/** @var string */
private $icon;
/** @var int */
protected $classCount = 0;
/** @var array */
protected $classList = [];
/**
* @param string $name
* @param string $icon
*/
public function __construct($name = 'counter', $icon = 'cubes')
{
$this->name = $name;
$this->icon = $icon;
}
/**
* @param string|mixed $class
* @param int $count
*/
public function countClass($class, $count = 1) {
if (! is_string($class)) {
$class = get_class($class);
}
$this->classList[$class] = ($this->classList[$class] ?? 0) + $count;
$this->classCount += $count;
}
/**
* {@inheritDoc}
*/
public function collect()
{
arsort($this->classList, SORT_NUMERIC);
if (! $this->getXdebugLinkTemplate()) {
return ['data' => $this->classList, 'count' => $this->classCount, 'is_counter' => true];
}
$data = [];
foreach ($this->classList as $class => $count) {
$reflector = class_exists($class) ? new \ReflectionClass($class) : null;
if ($reflector && $link = $this->getXdebugLink($reflector->getFileName())) {
$data[$class] = [
'value' => $count,
'xdebug_link' => $link,
];
} else {
$data[$class] = $count;
}
}
return ['data' => $data, 'count' => $this->classCount, 'is_counter' => true];
}
/**
* {@inheritDoc}
*/
public function getName()
{
return $this->name;
}
/**
* {@inheritDoc}
*/
public function getWidgets()
{
$name = $this->getName();
return [
"$name" => [
'icon' => $this->icon,
'widget' => 'PhpDebugBar.Widgets.HtmlVariableListWidget',
'map' => "$name.data",
'default' => '{}'
],
"$name:badge" => [
'map' => "$name.count",
'default' => 0
]
];
}
}

View File

@ -0,0 +1,239 @@
<?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 = '<>';
protected $durationBackground = false;
/**
* @param \PDO $pdo
* @param TimeDataCollector $timeCollector
*/
public function __construct(\PDO $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;
}
/**
* Enable/disable the shaded duration background on queries
*
* @param bool $enabled
*/
public function setDurationBackground($enabled)
{
$this->durationBackground = $enabled;
}
/**
* @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(\PDO $pdo, $name = null)
{
if ($name === null) {
$name = spl_object_hash($pdo);
}
if (!($pdo instanceof TraceablePDO)) {
$pdo = new TraceablePDO($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, $name);
$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
* @param string|null $connectionName the pdo connection (eg default | read | write)
* @return array
*/
protected function collectPDO(TraceablePDO $pdo, TimeDataCollector $timeCollector = null, $connectionName = null)
{
if (empty($connectionName) || $connectionName == 'default') {
$connectionName = 'pdo';
} else {
$connectionName = 'pdo ' . $connectionName;
}
$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(), array(), $connectionName);
}
}
$totalTime = $pdo->getAccumulatedStatementsDuration();
if ($this->durationBackground && $totalTime > 0) {
// For showing background measure on Queries tab
$start_percent = 0;
foreach ($stmts as $i => $stmt) {
if (!isset($stmt['duration'])) {
continue;
}
$width_percent = $stmt['duration'] / $totalTime * 100;
$stmts[$i] = array_merge($stmt, [
'start_percent' => round($start_percent, 3),
'width_percent' => round($width_percent, 3),
]);
$start_percent += $width_percent;
}
}
return array(
'nb_statements' => count($stmts),
'nb_failed_statements' => count($pdo->getFailedExecutedStatements()),
'accumulated_duration' => $totalTime,
'accumulated_duration_str' => $this->getDataFormatter()->formatDuration($totalTime),
'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" => "database",
"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,321 @@
<?php
namespace DebugBar\DataCollector\PDO;
use PDO;
use PDOException;
use DebugBar\DataCollector\PDO\TraceablePDOStatement;
/**
* A PDO proxy which traces statements
*/
class TraceablePDO extends PDO
{
/** @var PDO */
protected $pdo;
/** @var TracedStatement[] */
protected $executedStatements = [];
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
$this->pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS, [TraceablePDOStatement::class, [$this]]);
}
/**
* Initiates a transaction
*
* @link http://php.net/manual/en/pdo.begintransaction.php
* @return bool TRUE on success or FALSE on failure.
*/
public function beginTransaction() : bool
{
return $this->pdo->beginTransaction();
}
/**
* Commits a transaction
*
* @link http://php.net/manual/en/pdo.commit.php
* @return bool TRUE on success or FALSE on failure.
*/
public function commit() : bool
{
return $this->pdo->commit();
}
/**
* Fetch extended error information associated with the last operation on the database handle
*
* @link http://php.net/manual/en/pdo.errorinfo.php
* @return array PDO::errorInfo returns an array of error information
*/
#[\ReturnTypeWillChange]
public function errorCode()
{
return $this->pdo->errorCode();
}
/**
* Fetch extended error information associated with the last operation on the database handle
*
* @link http://php.net/manual/en/pdo.errorinfo.php
* @return array PDO::errorInfo returns an array of error information
*/
public function errorInfo() : array
{
return $this->pdo->errorInfo();
}
/**
* Execute an SQL statement and return the number of affected rows
*
* @link http://php.net/manual/en/pdo.exec.php
* @param string $statement
* @return int|bool PDO::exec returns the number of rows that were modified or deleted by the
* SQL statement you issued. If no rows were affected, PDO::exec returns 0. This function may
* return Boolean FALSE, but may also return a non-Boolean value which evaluates to FALSE.
* Please read the section on Booleans for more information
*/
#[\ReturnTypeWillChange]
public function exec($statement)
{
return $this->profileCall('exec', $statement, func_get_args());
}
/**
* Retrieve a database connection attribute
*
* @link http://php.net/manual/en/pdo.getattribute.php
* @param int $attribute One of the PDO::ATTR_* constants
* @return mixed A successful call returns the value of the requested PDO attribute.
* An unsuccessful call returns null.
*/
#[\ReturnTypeWillChange]
public function getAttribute($attribute)
{
return $this->pdo->getAttribute($attribute);
}
/**
* Checks if inside a transaction
*
* @link http://php.net/manual/en/pdo.intransaction.php
* @return bool TRUE if a transaction is currently active, and FALSE if not.
*/
public function inTransaction() : bool
{
return $this->pdo->inTransaction();
}
/**
* Returns the ID of the last inserted row or sequence value
*
* @link http://php.net/manual/en/pdo.lastinsertid.php
* @param string $name [optional]
* @return string If a sequence name was not specified for the name parameter, PDO::lastInsertId
* returns a string representing the row ID of the last row that was inserted into the database.
*/
#[\ReturnTypeWillChange]
public function lastInsertId($name = null)
{
return $this->pdo->lastInsertId($name);
}
/**
* Prepares a statement for execution and returns a statement object
*
* @link http://php.net/manual/en/pdo.prepare.php
* @param string $statement This must be a valid SQL statement template for the target DB server.
* @param array $driver_options [optional] This array holds one or more key=&gt;value pairs to
* set attribute values for the PDOStatement object that this method returns.
* @return TraceablePDOStatement|bool If the database server successfully prepares the statement,
* PDO::prepare returns a PDOStatement object. If the database server cannot successfully prepare
* the statement, PDO::prepare returns FALSE or emits PDOException (depending on error handling).
*/
#[\ReturnTypeWillChange]
public function prepare($statement, $driver_options = [])
{
return $this->pdo->prepare($statement, $driver_options);
}
/**
* Executes an SQL statement, returning a result set as a PDOStatement object
*
* @link http://php.net/manual/en/pdo.query.php
* @param string $statement
* @param int $fetchMode
* @param mixed ...$fetchModeArgs
* @return TraceablePDOStatement|bool PDO::query returns a PDOStatement object, or FALSE on
* failure.
*/
#[\ReturnTypeWillChange]
public function query($statement, $fetchMode = null, ...$fetchModeArgs)
{
return $this->profileCall('query', $statement, func_get_args());
}
/**
* Quotes a string for use in a query.
*
* @link http://php.net/manual/en/pdo.quote.php
* @param string $string The string to be quoted.
* @param int $parameter_type [optional] Provides a data type hint for drivers that have
* alternate quoting styles.
* @return string|bool A quoted string that is theoretically safe to pass into an SQL statement.
* Returns FALSE if the driver does not support quoting in this way.
*/
#[\ReturnTypeWillChange]
public function quote($string, $parameter_type = PDO::PARAM_STR)
{
return $this->pdo->quote($string, $parameter_type);
}
/**
* Rolls back a transaction
*
* @link http://php.net/manual/en/pdo.rollback.php
* @return bool TRUE on success or FALSE on failure.
*/
public function rollBack() : bool
{
return $this->pdo->rollBack();
}
/**
* Set an attribute
*
* @link http://php.net/manual/en/pdo.setattribute.php
* @param int $attribute
* @param mixed $value
* @return bool TRUE on success or FALSE on failure.
*/
public function setAttribute($attribute, $value) : bool
{
return $this->pdo->setAttribute($attribute, $value);
}
/**
* Profiles a call to a PDO method
*
* @param string $method
* @param string $sql
* @param array $args
* @return mixed The result of the call
*/
#[\ReturnTypeWillChange]
protected function profileCall($method, $sql, array $args)
{
$trace = new TracedStatement($sql);
$trace->start();
$ex = null;
try {
$result = $this->__call($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) : void
{
$this->executedStatements[] = $stmt;
}
/**
* Returns the accumulated execution time of statements
*
* @return float
*/
public function getAccumulatedStatementsDuration() : float
{
return array_reduce($this->executedStatements, function ($v, $s) { return $v + $s->getDuration(); }, 0.0);
}
/**
* Returns the peak memory usage while performing statements
*
* @return int
*/
public function getMemoryUsage() : int
{
return array_reduce($this->executedStatements, function ($v, $s) { return $v + $s->getMemoryUsage(); }, 0);
}
/**
* Returns the peak memory usage while performing statements
*
* @return int
*/
public function getPeakMemoryUsage() : int
{
return array_reduce($this->executedStatements, function ($v, $s) { $m = $s->getEndMemory(); return $m > $v ? $m : $v; }, 0);
}
/**
* Returns the list of executed statements as TracedStatement objects
*
* @return TracedStatement[]
*/
public function getExecutedStatements() : array
{
return $this->executedStatements;
}
/**
* Returns the list of failed statements
*
* @return TracedStatement[]
*/
public function getFailedExecutedStatements() : array
{
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([$this->pdo, $name], $args);
}
}

View File

@ -0,0 +1,131 @@
<?php
namespace DebugBar\DataCollector\PDO;
use PDO;
use PDOException;
use PDOStatement;
/**
* A traceable PDO statement to use with Traceablepdo
*/
class TraceablePDOStatement extends PDOStatement
{
/** @var PDO */
protected $pdo;
/** @var array */
protected $boundParameters = [];
/**
* TraceablePDOStatement constructor.
*
* @param TraceablePDO $pdo
*/
protected function __construct(TraceablePDO $pdo)
{
$this->pdo = $pdo;
}
/**
* Bind a column to a PHP variable
*
* @link http://php.net/manual/en/pdostatement.bindcolumn.php
* @param mixed $column Number of the column (1-indexed) or name of the column in the result set
* @param mixed $param Name of the PHP variable to which the column will be bound.
* @param int $type [optional] Data type of the parameter, specified by the PDO::PARAM_*
* constants.
* @param int $maxlen [optional] A hint for pre-allocation.
* @param mixed $driverdata [optional] Optional parameter(s) for the driver.
* @return bool TRUE on success or FALSE on failure.
*/
#[\ReturnTypeWillChange]
public function bindColumn($column, &$param, $type = null, $maxlen = null, $driverdata = null)
{
$this->boundParameters[$column] = $param;
$args = array_merge([$column, &$param], array_slice(func_get_args(), 2));
return parent::bindColumn(...$args);
}
/**
* Binds a parameter to the specified variable name
*
* @link http://php.net/manual/en/pdostatement.bindparam.php
* @param mixed $parameter Parameter identifier. For a prepared statement using named
* placeholders, this will be a parameter name of the form :name. For a prepared statement using
* question mark placeholders, this will be the 1-indexed position of the parameter.
* @param mixed $variable Name of the PHP variable to bind to the SQL statement parameter.
* @param int $data_type [optional] Explicit data type for the parameter using the PDO::PARAM_*
* constants.
* @param int $length [optional] Length of the data type. To indicate that a parameter is an OUT
* parameter from a stored procedure, you must explicitly set the length.
* @param mixed $driver_options [optional]
* @return bool TRUE on success or FALSE on failure.
*/
public function bindParam($parameter, &$variable, $data_type = PDO::PARAM_STR, $length = null, $driver_options = null) : bool
{
$this->boundParameters[$parameter] = $variable;
$args = array_merge([$parameter, &$variable], array_slice(func_get_args(), 2));
return parent::bindParam(...$args);
}
/**
* Binds a value to a parameter
*
* @link http://php.net/manual/en/pdostatement.bindvalue.php
* @param mixed $parameter Parameter identifier. For a prepared statement using named
* placeholders, this will be a parameter name of the form :name. For a prepared statement using
* question mark placeholders, this will be the 1-indexed position of the parameter.
* @param mixed $value The value to bind to the parameter.
* @param int $data_type [optional] Explicit data type for the parameter using the PDO::PARAM_*
* constants.
* @return bool TRUE on success or FALSE on failure.
*/
public function bindValue($parameter, $value, $data_type = PDO::PARAM_STR) : bool
{
$this->boundParameters[$parameter] = $value;
return parent::bindValue(...func_get_args());
}
/**
* Executes a prepared statement
*
* @link http://php.net/manual/en/pdostatement.execute.php
* @param array $input_parameters [optional] An array of values with as many elements as there
* are bound parameters in the SQL statement being executed. All values are treated as
* PDO::PARAM_STR.
* @throws PDOException
* @return bool TRUE on success or FALSE on failure.
*/
public function execute($input_parameters = null) : bool
{
$preparedId = spl_object_hash($this);
$boundParameters = $this->boundParameters;
if (is_array($input_parameters)) {
$boundParameters = array_merge($boundParameters, $input_parameters);
}
$trace = new TracedStatement($this->queryString, $boundParameters, $preparedId);
$trace->start();
$ex = null;
try {
$result = parent::execute($input_parameters);
} 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,277 @@
<?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;
protected $preparedId;
/**
* @param string $sql
* @param array $params
* @param null|string $preparedId
*/
public function __construct(string $sql, array $params = [], ?string $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) : void
{
$this->startTime = $startTime ?: microtime(true);
$this->startMemory = $startMemory ?: memory_get_usage(false);
}
/**
* @param \Exception|null $exception
* @param int $rowCount
* @param float $endTime
* @param int $endMemory
*/
public function end(\Exception $exception = null, int $rowCount = 0, float $endTime = null, int $endMemory = null) : void
{
$this->endTime = $endTime ?: microtime(true);
$this->duration = $this->endTime - $this->startTime;
$this->endMemory = $endMemory ?: memory_get_usage(false);
$this->memoryDelta = $this->endMemory - $this->startMemory;
$this->exception = $exception;
$this->rowCount = $rowCount;
}
/**
* Check parameters for illegal (non UTF-8) strings, like Binary data.
*
* @param array $params
* @return array
*/
public function checkParameters(array $params) : array
{
foreach ($params as &$param) {
if (!mb_check_encoding($param ?? '', 'UTF-8')) {
$param = '[BINARY DATA]';
}
}
return $params;
}
/**
* Returns the SQL string used for the query, without filled parameters
*
* @return string
*/
public function getSql() : string
{
return $this->sql;
}
/**
* Returns the SQL string with any parameters used embedded
*
* @param string $quotationChar
* @return string
*/
public function getSqlWithParams(string $quotationChar = '<>') : string
{
if (($l = strlen($quotationChar)) > 1) {
$quoteLeft = substr($quotationChar, 0, $l / 2);
$quoteRight = substr($quotationChar, $l / 2);
} else {
$quoteLeft = $quoteRight = $quotationChar;
}
$sql = $this->sql;
$cleanBackRefCharMap = ['%' => '%%', '$' => '$%', '\\' => '\\%'];
foreach ($this->parameters as $k => $v) {
$backRefSafeV = strtr($v, $cleanBackRefCharMap);
$v = "$quoteLeft$backRefSafeV$quoteRight";
if (is_numeric($k)) {
$marker = "\?";
} else {
$marker = (preg_match("/^:/", $k)) ? $k : ":" . $k;
}
$matchRule = "/({$marker}(?!\w))(?=(?:[^$quotationChar]|[$quotationChar][^$quotationChar]*[$quotationChar])*$)/";
$count = mb_substr_count($sql, $k);
if ($count < 1) {
$count = mb_substr_count($sql, $matchRule);
}
for ($i = 0; $i <= $count; $i++) {
$sql = preg_replace($matchRule, $v, $sql, 1);
}
}
$sql = strtr($sql, array_flip($cleanBackRefCharMap));
return $sql;
}
/**
* Returns the number of rows affected/returned
*
* @return int
*/
public function getRowCount() : int
{
return $this->rowCount;
}
/**
* Returns an array of parameters used with the query
*
* @return array
*/
public function getParameters() : array
{
$params = [];
foreach ($this->parameters as $name => $param) {
$params[$name] = htmlentities($param?:"", ENT_QUOTES, 'UTF-8', false);
}
return $params;
}
/**
* Returns the prepared statement id
*
* @return null|string
*/
public function getPreparedId() : ?string
{
return $this->preparedId;
}
/**
* Checks if this is a prepared statement
*
* @return boolean
*/
public function isPrepared() : bool
{
return $this->preparedId !== null;
}
/**
* @return float
*/
public function getStartTime() : float
{
return $this->startTime;
}
/**
* @return float
*/
public function getEndTime() : float
{
return $this->endTime;
}
/**
* Returns the duration in seconds + microseconds of the execution
*
* @return float
*/
public function getDuration() : float
{
return $this->duration;
}
/**
* @return int
*/
public function getStartMemory() : int
{
return $this->startMemory;
}
/**
* @return int
*/
public function getEndMemory() : int
{
return $this->endMemory;
}
/**
* Returns the memory usage during the execution
*
* @return int
*/
public function getMemoryUsage() : int
{
return $this->memoryDelta;
}
/**
* Checks if the statement was successful
*
* @return boolean
*/
public function isSuccess() : bool
{
return $this->exception === null;
}
/**
* Returns the exception triggered
*
* @return \Exception
*/
public function getException() : \Exception
{
return $this->exception;
}
/**
* Returns the exception's code
*
* @return int|string
*/
public function getErrorCode()
{
return $this->exception !== null ? $this->exception->getCode() : 0;
}
/**
* Returns the exception's message
*
* @return string
*/
public function getErrorMessage() : string
{
return $this->exception !== null ? $this->exception->getMessage() : '';
}
}

View File

@ -0,0 +1,51 @@
<?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 implements Renderable
{
/**
* @return string
*/
public function getName()
{
return 'php';
}
/**
* @return array
*/
public function collect()
{
return array(
'version' => implode('.', [PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION]),
'interface' => PHP_SAPI
);
}
/**
* {@inheritDoc}
*/
public function getWidgets()
{
return array(
"php_version" => array(
"icon" => "code",
"tooltip" => "PHP Version",
"map" => "php.version",
"default" => ""
),
);
}
}

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,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 the current request
*/
class RequestDataCollector extends DataCollector implements Renderable, AssetProvider
{
/**
* @return array
*/
public function collect()
{
$vars = array('_GET', '_POST', '_SESSION', '_COOKIE');
$data = array();
foreach ($vars as $var) {
if (isset($GLOBALS[$var])) {
$key = "$" . $var;
if ($this->isHtmlVarDumperUsed()) {
$data[$key] = $this->getVarDumper()->renderVar($GLOBALS[$var]);
} else {
$data[$key] = $this->getDataFormatter()->formatVar($GLOBALS[$var]);
}
}
}
return $data;
}
/**
* @return string
*/
public function getName()
{
return 'request';
}
/**
* @return array
*/
public function getAssets() {
return $this->isHtmlVarDumperUsed() ? $this->getVarDumper()->getAssets() : array();
}
/**
* @return array
*/
public function getWidgets()
{
$widget = $this->isHtmlVarDumperUsed()
? "PhpDebugBar.Widgets.HtmlVariableListWidget"
: "PhpDebugBar.Widgets.VariableListWidget";
return array(
"request" => array(
"icon" => "tags",
"widget" => $widget,
"map" => "request",
"default" => "{}"
)
);
}
}

View File

@ -0,0 +1,272 @@
<?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();
/**
* @var bool
*/
protected $memoryMeasure = false;
/**
* @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 = (float)$requestStartTime;
static::getDefaultDataFormatter(); // initializes formatter for lineal timeline
}
/**
* Starts memory measuring
*/
public function showMemoryUsage()
{
$this->memoryMeasure = true;
}
/**
* 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,
'memory' => $this->memoryMeasure ? memory_get_usage(false) : null,
'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");
}
if (! is_null($this->startedMeasures[$name]['memory'])) {
$params['memoryUsage'] = memory_get_usage(false) - $this->startedMeasures[$name]['memory'];
}
$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)
{
if (isset($params['memoryUsage'])) {
$memory = $this->memoryMeasure ? $params['memoryUsage'] : 0;
unset($params['memoryUsage']);
}
$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),
'memory' => $memory ?? 0,
'memory_str' => $this->getDataFormatter()->formatBytes($memory ?? 0),
'params' => $params,
'collector' => $collector
);
}
/**
* Utility function to measure the execution of a Closure
*
* @param string $label
* @param \Closure $closure
* @param string|null $collector
* @return mixed
*/
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);
return $result;
}
/**
* 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);
}
usort($this->measures, function($a, $b) {
if ($a['start'] == $b['start']) {
return 0;
}
return $a['start'] < $b['start'] ? -1 : 1;
});
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,106 @@
<?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
{
public $cloner;
public $dumper;
/**
* 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 < 0.1) {
return round($seconds * 1000, 2) . 'ms';
} elseif ($seconds < 1) {
return round($seconds * 1000) . '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[(int) floor($base)];
}
/**
* @param object $object
* @return string
*/
public function formatClassName($object)
{
$class = \get_class($object);
if (false === ($pos = \strpos($class, "@anonymous\0"))) {
return $class;
}
if (false === ($parent = \get_parent_class($class))) {
return \substr($class, 0, $pos + 10);
}
return $parent . '@anonymous';
}
}

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 $data
* @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,313 @@
<?php
namespace DebugBar\DataFormatter;
use DebugBar\DataCollector\AssetProvider;
use DebugBar\DataFormatter\VarDumper\DebugBarHtmlDumper;
use Symfony\Component\VarDumper\Cloner\Data\SeekingData;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Cloner\VarCloner;
/**
* Clones and renders variables in HTML format using the Symfony VarDumper component.
*
* Cloning is decoupled from rendering, so that dumper users can have the fastest possible cloning
* performance, while delaying rendering until it is actually needed.
*/
class DebugBarVarDumper implements AssetProvider
{
protected static $defaultClonerOptions = array();
protected static $defaultDumperOptions = array(
'expanded_depth' => 0,
'styles' => array(
// NOTE: 'default' CSS is also specified in debugbar.css
'default' => 'word-wrap: break-word; white-space: pre-wrap; word-break: normal',
'num' => 'font-weight:bold; color:#1299DA',
'const' => 'font-weight:bold',
'str' => 'font-weight:bold; color:#3A9B26',
'note' => 'color:#1299DA',
'ref' => 'color:#7B7B7B',
'public' => 'color:#000000',
'protected' => 'color:#000000',
'private' => 'color:#000000',
'meta' => 'color:#B729D9',
'key' => 'color:#3A9B26',
'index' => 'color:#1299DA',
'ellipsis' => 'color:#A0A000',
),
);
protected $clonerOptions;
protected $dumperOptions;
/** @var VarCloner */
protected $cloner;
/** @var DebugBarHtmlDumper */
protected $dumper;
/**
* Gets the VarCloner instance with configuration options set.
*
* @return VarCloner
*/
protected function getCloner()
{
if (!$this->cloner) {
$clonerOptions = $this->getClonerOptions();
if (isset($clonerOptions['casters'])) {
$this->cloner = new VarCloner($clonerOptions['casters']);
} else {
$this->cloner = new VarCloner();
}
if (isset($clonerOptions['additional_casters'])) {
$this->cloner->addCasters($clonerOptions['additional_casters']);
}
if (isset($clonerOptions['max_items'])) {
$this->cloner->setMaxItems($clonerOptions['max_items']);
}
if (isset($clonerOptions['max_string'])) {
$this->cloner->setMaxString($clonerOptions['max_string']);
}
// setMinDepth was added to Symfony 3.4:
if (isset($clonerOptions['min_depth']) && method_exists($this->cloner, 'setMinDepth')) {
$this->cloner->setMinDepth($clonerOptions['min_depth']);
}
}
return $this->cloner;
}
/**
* Gets the DebugBarHtmlDumper instance with configuration options set.
*
* @return DebugBarHtmlDumper
*/
protected function getDumper()
{
if (!$this->dumper) {
$this->dumper = new DebugBarHtmlDumper();
$dumperOptions = $this->getDumperOptions();
if (isset($dumperOptions['styles'])) {
$this->dumper->setStyles($dumperOptions['styles']);
}
}
return $this->dumper;
}
/**
* Gets the array of non-default VarCloner configuration options.
*
* @return array
*/
public function getClonerOptions()
{
if ($this->clonerOptions === null) {
$this->clonerOptions = self::$defaultClonerOptions;
}
return $this->clonerOptions;
}
/**
* Merges an array of non-default VarCloner configuration options with the existing non-default
* options.
*
* Configuration options are:
* - casters: a map of VarDumper Caster objects to use instead of the default casters.
* - additional_casters: a map of VarDumper Caster objects to use in addition to the default
* casters.
* - max_items: maximum number of items to clone beyond the minimum depth.
* - max_string: maximum string size
* - min_depth: minimum tree depth to clone before counting items against the max_items limit.
* (Requires Symfony 3.4; ignored on older versions.)
*
* @param array $options
*/
public function mergeClonerOptions($options)
{
$this->clonerOptions = $options + $this->getClonerOptions();
$this->cloner = null;
}
/**
* Resets the array of non-default VarCloner configuration options without retaining any of the
* existing non-default options.
*
* Configuration options are:
* - casters: a map of VarDumper Caster objects to use instead of the default casters.
* - additional_casters: a map of VarDumper Caster objects to use in addition to the default
* casters.
* - max_items: maximum number of items to clone beyond the minimum depth.
* - max_string: maximum string size
* - min_depth: minimum tree depth to clone before counting items against the max_items limit.
* (Requires Symfony 3.4; ignored on older versions.)
*
* @param array $options
*/
public function resetClonerOptions($options = null)
{
$this->clonerOptions = ($options ?: array()) + self::$defaultClonerOptions;
$this->cloner = null;
}
/**
* Gets the array of non-default HtmlDumper configuration options.
*
* @return array
*/
public function getDumperOptions()
{
if ($this->dumperOptions === null) {
$this->dumperOptions = self::$defaultDumperOptions;
}
return $this->dumperOptions;
}
/**
* Merges an array of non-default HtmlDumper configuration options with the existing non-default
* options.
*
* Configuration options are:
* - styles: a map of CSS styles to include in the assets, as documented in
* HtmlDumper::setStyles.
* - expanded_depth: the tree depth to initially expand.
* (Requires Symfony 3.2; ignored on older versions.)
* - max_string: maximum string size.
* (Requires Symfony 3.2; ignored on older versions.)
* - file_link_format: link format for files; %f expanded to file and %l expanded to line
* (Requires Symfony 3.2; ignored on older versions.)
*
* @param array $options
*/
public function mergeDumperOptions($options)
{
$this->dumperOptions = $options + $this->getDumperOptions();
$this->dumper = null;
}
/**
* Resets the array of non-default HtmlDumper configuration options without retaining any of the
* existing non-default options.
*
* Configuration options are:
* - styles: a map of CSS styles to include in the assets, as documented in
* HtmlDumper::setStyles.
* - expanded_depth: the tree depth to initially expand.
* (Requires Symfony 3.2; ignored on older versions.)
* - max_string: maximum string size.
* (Requires Symfony 3.2; ignored on older versions.)
* - file_link_format: link format for files; %f expanded to file and %l expanded to line
* (Requires Symfony 3.2; ignored on older versions.)
*
* @param array $options
*/
public function resetDumperOptions($options = null)
{
$this->dumperOptions = ($options ?: array()) + self::$defaultDumperOptions;
$this->dumper = null;
}
/**
* Captures the data from a variable and serializes it for later rendering.
*
* @param mixed $data The variable to capture.
* @return string Serialized variable data.
*/
public function captureVar($data)
{
return serialize($this->getCloner()->cloneVar($data));
}
/**
* Gets the display options for the HTML dumper.
*
* @return array
*/
protected function getDisplayOptions()
{
$displayOptions = array();
$dumperOptions = $this->getDumperOptions();
// Only used by Symfony 3.2 and newer:
if (isset($dumperOptions['expanded_depth'])) {
$displayOptions['maxDepth'] = $dumperOptions['expanded_depth'];
}
// Only used by Symfony 3.2 and newer:
if (isset($dumperOptions['max_string'])) {
$displayOptions['maxStringLength'] = $dumperOptions['max_string'];
}
// Only used by Symfony 3.2 and newer:
if (isset($dumperOptions['file_link_format'])) {
$displayOptions['fileLinkFormat'] = $dumperOptions['file_link_format'];
}
return $displayOptions;
}
/**
* Renders previously-captured data from captureVar to HTML and returns it as a string.
*
* @param string $capturedData Captured data from captureVar.
* @param array $seekPath Pass an array of keys to traverse if you only want to render a subset
* of the data.
* @return string HTML rendering of the variable.
*/
public function renderCapturedVar($capturedData, $seekPath = array())
{
$data = unserialize($capturedData);
if (!method_exists($data, 'seek')) {
$data = new SeekingData($data->getRawData());
}
foreach ($seekPath as $key) {
$data = $data->seek($key);
}
return $this->dump($data);
}
/**
* Captures and renders the data from a variable to HTML and returns it as a string.
*
* @param mixed $data The variable to capture and render.
* @return string HTML rendering of the variable.
*/
public function renderVar($data)
{
return $this->dump($this->getCloner()->cloneVar($data));
}
/**
* Returns assets required for rendering variables.
*
* @return array
*/
public function getAssets() {
$dumper = $this->getDumper();
$dumper->resetDumpHeader(); // this will cause the default dump header to regenerate
return array(
'inline_head' => array(
'html_var_dumper' => $dumper->getDumpHeaderByDebugBar(),
),
);
}
/**
* Helper function to dump a Data object to HTML.
*
* @param Data $data
* @return string
*/
protected function dump(Data $data)
{
$dumper = $this->getDumper();
$output = fopen('php://memory', 'r+b');
$dumper->setOutput($output);
$dumper->setDumpHeader(''); // we don't actually want a dump header
// NOTE: Symfony 3.2 added the third $extraDisplayOptions parameter. Older versions will
// safely ignore it.
$dumper->dump($data, null, $this->getDisplayOptions());
$result = stream_get_contents($output, -1, 0);
fclose($output);
return $result;
}
}

View File

@ -0,0 +1,164 @@
<?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 DebugBar\DataCollector\DataCollector;
trait HasDataFormatter
{
// The HTML var dumper requires debug bar users to support the new inline assets, which not all
// may support yet - so return false by default for now.
protected $useHtmlVarDumper = false;
protected $dataFormater;
protected $varDumper;
/**
* Sets a flag indicating whether the Symfony HtmlDumper will be used to dump variables for
* rich variable rendering.
*
* @param bool $value
* @return $this
*/
public function useHtmlVarDumper($value = true)
{
$this->useHtmlVarDumper = $value;
return $this;
}
/**
* Indicates whether the Symfony HtmlDumper will be used to dump variables for rich variable
* rendering.
*
* @return mixed
*/
public function isHtmlVarDumperUsed()
{
return $this->useHtmlVarDumper;
}
/**
* Sets the default data formater instance used by all collectors subclassing this class
*
* @param DataFormatterInterface $formater
*/
public static function setDefaultDataFormatter(DataFormatterInterface $formater)
{
DataCollector::$defaultDataFormatter = $formater;
}
/**
* Returns the default data formater
*
* @return DataFormatterInterface
*/
public static function getDefaultDataFormatter()
{
if (DataCollector::$defaultDataFormatter === null) {
DataCollector::$defaultDataFormatter = new DataFormatter();
}
return DataCollector::$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 = DataCollector::getDefaultDataFormatter();
}
return $this->dataFormater;
}
/**
* Sets the default variable dumper used by all collectors subclassing this class
*
* @param DebugBarVarDumper $varDumper
*/
public static function setDefaultVarDumper(DebugBarVarDumper $varDumper)
{
DataCollector::$defaultVarDumper = $varDumper;
}
/**
* Returns the default variable dumper
*
* @return DebugBarVarDumper
*/
public static function getDefaultVarDumper()
{
if (DataCollector::$defaultVarDumper === null) {
DataCollector::$defaultVarDumper = new DebugBarVarDumper();
}
return DataCollector::$defaultVarDumper;
}
/**
* Sets the variable dumper instance used by this collector
*
* @param DebugBarVarDumper $varDumper
* @return $this
*/
public function setVarDumper(DebugBarVarDumper $varDumper)
{
$this->varDumper = $varDumper;
return $this;
}
/**
* Gets the variable dumper instance used by this collector; note that collectors using this
* instance need to be sure to return the static assets provided by the variable dumper.
*
* @return DebugBarVarDumper
*/
public function getVarDumper()
{
if ($this->varDumper === null) {
$this->varDumper = DataCollector::getDefaultVarDumper();
}
return $this->varDumper;
}
/**
* @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,197 @@
<?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 DebugBar\DataCollector\DataCollector;
trait HasXdebugLinks
{
protected $xdebugLinkTemplate = '';
protected $xdebugShouldUseAjax = false;
protected $xdebugReplacements = array();
/**
* Shorten the file path by removing the xdebug path replacements
*
* @param string $file
* @return string
*/
public function normalizeFilePath($file)
{
if (empty($file)) {
return '';
}
if (@file_exists($file)) {
$file = realpath($file);
}
foreach (array_keys($this->xdebugReplacements) as $path) {
if (strpos($file, $path) === 0) {
$file = substr($file, strlen($path));
break;
}
}
return ltrim(str_replace('\\', '/', $file), '/');
}
/**
* Get an Xdebug Link to a file
*
* @param string $file
* @param int|null $line
*
* @return array {
* @var string $url
* @var bool $ajax should be used to open the url instead of a normal links
* }
*/
public function getXdebugLink($file, $line = null)
{
if (empty($file)) {
return null;
}
if (@file_exists($file)) {
$file = realpath($file);
}
foreach ($this->xdebugReplacements as $path => $replacement) {
if (strpos($file, $path) === 0) {
$file = $replacement . substr($file, strlen($path));
break;
}
}
$url = strtr($this->getXdebugLinkTemplate(), [
'%f' => rawurlencode(str_replace('\\', '/', $file)),
'%l' => rawurlencode((string) $line ?: 1),
]);
if ($url) {
return [
'url' => $url,
'ajax' => $this->getXdebugShouldUseAjax(),
'filename' => basename($file),
'line' => (string) $line ?: '?'
];
}
}
/**
* @return string
*/
public function getXdebugLinkTemplate()
{
if (empty($this->xdebugLinkTemplate) && !empty(ini_get('xdebug.file_link_format'))) {
$this->xdebugLinkTemplate = ini_get('xdebug.file_link_format');
}
return $this->xdebugLinkTemplate;
}
/**
* @param string $editor
*/
public function setEditorLinkTemplate($editor)
{
$editorLinkTemplates = array(
'sublime' => 'subl://open?url=file://%f&line=%l',
'textmate' => 'txmt://open?url=file://%f&line=%l',
'emacs' => 'emacs://open?url=file://%f&line=%l',
'macvim' => 'mvim://open/?url=file://%f&line=%l',
'phpstorm' => 'phpstorm://open?file=%f&line=%l',
'phpstorm-remote' => 'javascript:(()=>{let r=new XMLHttpRequest;' .
'r.open(\'get\',\'http://localhost:63342/api/file/%f:%l\');r.send();})()',
'idea' => 'idea://open?file=%f&line=%l',
'idea-remote' => 'javascript:(()=>{let r=new XMLHttpRequest;' .
'r.open(\'get\',\'http://localhost:63342/api/file/?file=%f&line=%l\');r.send();})()',
'vscode' => 'vscode://file/%f:%l',
'vscode-insiders' => 'vscode-insiders://file/%f:%l',
'vscode-remote' => 'vscode://vscode-remote/%f:%l',
'vscode-insiders-remote' => 'vscode-insiders://vscode-remote/%f:%l',
'vscodium' => 'vscodium://file/%f:%l',
'nova' => 'nova://core/open/file?filename=%f&line=%l',
'xdebug' => 'xdebug://%f@%l',
'atom' => 'atom://core/open/file?filename=%f&line=%l',
'espresso' => 'x-espresso://open?filepath=%f&lines=%l',
'netbeans' => 'netbeans://open/?f=%f:%l',
);
if (is_string($editor) && isset($editorLinkTemplates[$editor])) {
$this->setXdebugLinkTemplate($editorLinkTemplates[$editor]);
}
}
/**
* @param string $xdebugLinkTemplate
* @param bool $shouldUseAjax
*/
public function setXdebugLinkTemplate($xdebugLinkTemplate, $shouldUseAjax = false)
{
if ($xdebugLinkTemplate === 'idea') {
$this->xdebugLinkTemplate = 'http://localhost:63342/api/file/?file=%f&line=%l';
$this->xdebugShouldUseAjax = true;
} else {
$this->xdebugLinkTemplate = $xdebugLinkTemplate;
$this->xdebugShouldUseAjax = $shouldUseAjax;
}
}
/**
* @return bool
*/
public function getXdebugShouldUseAjax()
{
return $this->xdebugShouldUseAjax;
}
/**
* returns an array of filename-replacements
*
* this is useful f.e. when using vagrant or remote servers,
* where the path of the file is different between server and
* development environment
*
* @return array key-value-pairs of replacements, key = path on server, value = replacement
*/
public function getXdebugReplacements()
{
return $this->xdebugReplacements;
}
/**
* @param array $xdebugReplacements
*/
public function addXdebugReplacements($xdebugReplacements)
{
foreach ($xdebugReplacements as $serverPath => $replacement) {
$this->setXdebugReplacement($serverPath, $replacement);
}
}
/**
* @param array $xdebugReplacements
*/
public function setXdebugReplacements($xdebugReplacements)
{
$this->xdebugReplacements = $xdebugReplacements;
}
/**
* @param string $serverPath
* @param string $replacement
*/
public function setXdebugReplacement($serverPath, $replacement)
{
$this->xdebugReplacements[$serverPath] = $replacement;
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace DebugBar\DataFormatter\VarDumper;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
/**
* We have to extend the base HtmlDumper class in order to get access to the protected-only
* getDumpHeader function.
*/
class DebugBarHtmlDumper extends HtmlDumper
{
/**
* Resets an HTML header.
*/
public function resetDumpHeader()
{
$this->dumpHeader = null;
}
public function getDumpHeaderByDebugBar() {
// getDumpHeader is protected:
return str_replace('pre.sf-dump', '.phpdebugbar pre.sf-dump', $this->getDumpHeader());
}
}

View File

@ -0,0 +1,497 @@
<?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()
{
if (php_sapi_name() === 'cli') {
$ip = gethostname();
if ($ip) {
$ip = gethostbyname($ip);
} else {
$ip = '127.0.0.1';
}
$request_variables = array(
'method' => 'CLI',
'uri' => isset($_SERVER['SCRIPT_FILENAME']) ? realpath($_SERVER['SCRIPT_FILENAME']) : null,
'ip' => $ip
);
} else {
$request_variables = array(
'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
);
}
$this->data = array(
'__meta' => array_merge(
array(
'id' => $this->getCurrentRequestId(),
'datetime' => date('Y-m-d H:i:s'),
'utime' => microtime(true)
),
$request_variables
)
);
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
#[\ReturnTypeWillChange]
public function offsetSet($key, $value)
{
throw new DebugBarException("DebugBar[] is read-only");
}
#[\ReturnTypeWillChange]
public function offsetGet($key)
{
return $this->getCollector($key);
}
#[\ReturnTypeWillChange]
public function offsetExists($key)
{
return $this->hasCollector($key);
}
#[\ReturnTypeWillChange]
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,43 @@
<?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;
/**
* Basic request ID generator
*/
class RequestIdGenerator implements RequestIdGeneratorInterface
{
protected $index = 0;
/**
* @return string
*/
public function generate()
{
if (function_exists('random_bytes')) {
// PHP 7 only
return 'X' . bin2hex(random_bytes(16));
} else if (function_exists('openssl_random_pseudo_bytes')) {
// PHP >= 5.3.0, but OpenSSL may not always be available
return 'X' . bin2hex(openssl_random_pseudo_bytes(16));
} else {
// Fall back to a rudimentary ID generator:
// * $_SERVER array will make the ID unique to this request.
// * spl_object_hash($this) will make the ID unique to this object instance.
// (note that object hashes can be reused, but the other data here should prevent issues here).
// * uniqid('', true) will use the current microtime(), plus additional random data.
// * $this->index guarantees the uniqueness of IDs from the current object.
$this->index++;
$entropy = serialize($_SERVER) . uniqid('', true) . spl_object_hash($this) . $this->index;
return 'X' . md5($entropy);
}
}
}

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;
interface RequestIdGeneratorInterface
{
/**
* Generates a unique id for the current request. If called repeatedly, a new unique id must
* always be returned on each call to generate() - even across different object instances.
*
* To avoid any potential confusion in ID --> value maps, the returned value must be
* guaranteed to not be all-numeric.
*
* @return string
*/
function generate();
}

View File

@ -0,0 +1,381 @@
/* 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: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
background: #fff;
z-index: 10000;
font-size: 14px;
color: #000;
text-align: left;
line-height: 1;
letter-spacing: normal;
direction: ltr;
}
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: ns-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 ul li, div.phpdebugbar ol li, div.phpdebugbar dl li {
line-height: 1;
}
div.phpdebugbar table, .phpdebugbar-openhandler table {
border-collapse: collapse;
border-spacing: 0;
color: inherit;
}
div.phpdebugbar input[type='text'], div.phpdebugbar input[type='password'] {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
background: #fff;
font-size: 14px;
color: #000;
border: 0;
padding: 0;
margin: 0;
}
div.phpdebugbar code, div.phpdebugbar pre, div.phpdebugbar samp {
background: none;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 1em;
border: 0;
padding: 0;
margin: 0;
}
div.phpdebugbar code, div.phpdebugbar pre {
color: #000;
}
div.phpdebugbar pre.sf-dump {
color: #a0a000;
outline: 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: ns-resize;
}
div.phpdebugbar-closed, div.phpdebugbar-minimized{
border-top: 1px solid #ccc;
}
/* -------------------------------------- */
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 {
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;
line-height: 1em;
}
/* -------------------------------------- */
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: 0 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-visible {
display: inline;
}
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-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%20viewBox=%220%200%201792%201792%22%20fill=%22none%22%20xmlns=%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d=%22m1683%20653.5-742%20741c-12.667%2012.67-27.667%2019-45%2019s-32.333-6.33-45-19l-742-741c-12.667-12.667-19-27.833-19-45.5s6.333-32.833%2019-45.5l166-165c12.667-12.667%2027.667-19%2045-19s32.333%206.333%2045%2019l531%20531%20531-531c12.67-12.667%2027.67-19%2045-19s32.33%206.333%2045%2019l166%20165c12.67%2012.667%2019%2027.833%2019%2045.5s-6.33%2032.833-19%2045.5Z%22%20fill=%22%23555%22%2F%3E%3C%2Fsvg%3E) no-repeat center / 14px 14px;
}
a.phpdebugbar-maximize-btn {
background: url(data:image/svg+xml,%3Csvg%20viewBox=%220%200%201792%201792%22%20fill=%22none%22%20xmlns=%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d=%22m1683%201229.5-166%20165c-12.67%2012.67-27.67%2019-45%2019s-32.33-6.33-45-19l-531-531-531%20531c-12.667%2012.67-27.667%2019-45%2019s-32.333-6.33-45-19l-166-165c-12.667-12.67-19-27.83-19-45.5s6.333-32.83%2019-45.5l742-741c12.667-12.667%2027.667-19%2045-19s32.333%206.333%2045%2019l742%20741c12.67%2012.67%2019%2027.83%2019%2045.5s-6.33%2032.83-19%2045.5Z%22%20fill=%22%23555%22%2F%3E%3C%2Fsvg%3E) no-repeat center / 14px 14px;
}
a.phpdebugbar-close-btn {
background: url(data:image/svg+xml,%3Csvg%20viewBox=%220%200%201792%201792%22%20fill=%22none%22%20xmlns=%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d=%22M1490%201258c0%2026.67-9.33%2049.33-28%2068l-136%20136c-18.67%2018.67-41.33%2028-68%2028s-49.33-9.33-68-28l-294-294-294%20294c-18.667%2018.67-41.333%2028-68%2028s-49.333-9.33-68-28l-136-136c-18.667-18.67-28-41.33-28-68s9.333-49.33%2028-68l294-294-294-294c-18.667-18.667-28-41.333-28-68s9.333-49.333%2028-68l136-136c18.667-18.667%2041.333-28%2068-28s49.333%209.333%2068%2028l294%20294%20294-294c18.67-18.667%2041.33-28%2068-28s49.33%209.333%2068%2028l136%20136c18.67%2018.667%2028%2041.333%2028%2068s-9.33%2049.333-28%2068l-294%20294%20294%20294c18.67%2018.67%2028%2041.33%2028%2068Z%22%20fill=%22%23555%22%2F%3E%3C%2Fsvg%3E) no-repeat center / 14px 14px;
}
a.phpdebugbar-open-btn {
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 1792 1792'%3e%3cpath fill='%23555' d='M1646 991.796c0 16.494-8.25 34.064-24.75 52.684l-268.22 316.13c-22.89 27.14-54.95 50.16-96.2 69.05S1177.4 1458 1142.27 1458H273.728c-18.095 0-34.194-3.46-48.297-10.38-14.104-6.92-21.155-18.36-21.155-34.32 0-16.5 8.249-34.06 24.747-52.69l268.228-316.13c22.884-27.14 54.949-50.156 96.194-69.049 41.246-18.893 79.431-28.34 114.556-28.34h868.549c18.09 0 34.19 3.459 48.3 10.378 14.1 6.918 21.15 18.361 21.15 34.327Zm-273.82-274.615v127.728H708.001c-50.027 0-102.448 12.64-157.264 37.919-54.817 25.28-98.457 57.078-130.921 95.397L150.79 1294.35l-3.992 4.79c0-2.13-.133-5.46-.399-9.98-.266-4.52-.399-7.85-.399-9.98V512.817c0-48.962 17.563-91.005 52.688-126.13 35.125-35.126 77.168-52.688 126.131-52.688h255.455c48.962 0 91.005 17.562 126.13 52.688 35.126 35.125 52.688 77.168 52.688 126.13v25.546h434.278c48.96 0 91 17.563 126.13 52.688 35.12 35.125 52.68 77.168 52.68 126.13Z'/%3e%3c/svg%3e") no-repeat center / 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;
}
/* -------------------------------------- */
a.phpdebugbar-tab.phpdebugbar-tab-history {
min-width: 16px;
height: 16px;
display: flex;
justify-content: center;
align-items: center;
}
a.phpdebugbar-tab.phpdebugbar-tab-history .phpdebugbar-text {
display: none;
}
a.phpdebugbar-tab.phpdebugbar-tab-history i {
display:inline-block;
}
.phpdebugbar-widgets-dataset-history table {
width: 100%;
table-layout: fixed;
}
.phpdebugbar-widgets-dataset-history table th {
font-weight: bold;
}
.phpdebugbar-widgets-dataset-history table td, .phpdebugbar-widgets-dataset-history table th {
padding: 6px 3px;
border-bottom: 1px solid #ddd;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.phpdebugbar-widgets-dataset-history table td a{
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.phpdebugbar-widgets-dataset-history table tr.phpdebugbar-widgets-active {
background: #ccc;
color: #444;
}
.phpdebugbar-widgets-dataset-history span.phpdebugbar-badge {
margin: 0 5px 0 2px;
font-size: 11px;
line-height: 14px;
padding: 0 6px;
background: #ccc;
border-radius: 4px;
color: #555;
font-weight: normal;
text-shadow: none;
vertical-align: middle;
}
.phpdebugbar-widgets-dataset-history .phpdebugbar-widgets-dataset-actions {
text-align: center;
padding: 7px 0;
position: sticky;
top: 0;
background: #fff;
}
.phpdebugbar-widgets-dataset-history .phpdebugbar-widgets-dataset-actions a {
margin: 0 10px;
}
.phpdebugbar-widgets-dataset-history .phpdebugbar-widgets-dataset-actions input {
appearance: checkbox !important;
margin: 5px;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,70 @@
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;
color: #000;
border: 2px solid #888;
overflow: auto;
z-index: 20001;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
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,208 @@
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) {
var url = this.get('url');
if (data) {
url = url + '?' + new URLSearchParams(data);
}
fetch(url, {
method: "GET",
headers: {
"Accept": "application/json",
},
})
.then((data) => data.json())
.then(callback);
}
});
})(PhpDebugBar.$);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,82 @@
/*
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
*/
div.phpdebugbar pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em;
}
div.phpdebugbar code.hljs {
padding: 3px 5px;
}
div.phpdebugbar .hljs {
background: #f3f3f3;
color: #444;
}
div.phpdebugbar .hljs-comment {
color: #697070;
}
div.phpdebugbar .hljs-punctuation,
div.phpdebugbar .hljs-tag {
color: #444a;
}
div.phpdebugbar .hljs-tag .hljs-attr,
div.phpdebugbar .hljs-tag .hljs-name {
color: #444;
}
div.phpdebugbar .hljs-attribute,
div.phpdebugbar .hljs-doctag,
div.phpdebugbar .hljs-keyword,
div.phpdebugbar .hljs-meta .hljs-keyword,
div.phpdebugbar .hljs-name,
div.phpdebugbar .hljs-selector-tag {
font-weight: 700;
}
div.phpdebugbar .hljs-deletion,
div.phpdebugbar .hljs-number,
div.phpdebugbar .hljs-quote,
div.phpdebugbar .hljs-selector-class,
div.phpdebugbar .hljs-selector-id,
div.phpdebugbar .hljs-string,
div.phpdebugbar .hljs-template-tag,
div.phpdebugbar .hljs-type {
color: #800;
}
div.phpdebugbar .hljs-section,
div.phpdebugbar .hljs-title {
color: #800;
font-weight: 700;
}
div.phpdebugbar .hljs-link,
div.phpdebugbar .hljs-operator,
div.phpdebugbar .hljs-regexp,
div.phpdebugbar .hljs-selector-attr,
div.phpdebugbar .hljs-selector-pseudo,
div.phpdebugbar .hljs-symbol,
div.phpdebugbar .hljs-template-variable,
div.phpdebugbar .hljs-variable {
color: #ab5656;
}
div.phpdebugbar .hljs-literal {
color: #695;
}
div.phpdebugbar .hljs-addition,
div.phpdebugbar .hljs-built_in,
div.phpdebugbar .hljs-bullet,
div.phpdebugbar .hljs-code {
color: #397300;
}
div.phpdebugbar .hljs-meta {
color: #1f7199;
}
div.phpdebugbar .hljs-meta .hljs-string {
color: #38a;
}
div.phpdebugbar .hljs-emphasis {
font-style: italic;
}
div.phpdebugbar .hljs-strong {
font-weight: 700;
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,302 @@
pre.phpdebugbar-widgets-code-block {
white-space: pre;
word-wrap: normal;
overflow: hidden;
}
pre.phpdebugbar-widgets-code-block code {
display: block;
overflow-x: auto;
overflow-y: hidden;
}
pre.phpdebugbar-widgets-code-block code.phpdebugbar-widgets-numbered-code {
padding: 5px;
}
pre.phpdebugbar-widgets-code-block ul li.phpdebugbar-widgets-highlighted-line {
font-weight: bolder;
text-decoration: underline;
}
pre.phpdebugbar-widgets-code-block ul li.phpdebugbar-widgets-highlighted-line span {
position: absolute;
background: #800000;
min-width: calc(100% - 85px);
margin-left: 10px;
opacity: 0.15;
}
pre.phpdebugbar-widgets-code-block ul {
position: static;
float: left;
padding: 5px;
background: #cacaca;
border-right: 1px solid #aaa;
text-align: right;
}
.phpdebugbar-widgets-kvlist span.phpdebugbar-widgets-filename,
li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-filename {
display: block;
font-style: italic;
float: right;
margin-left: 8px;
color: #888;
}
a.phpdebugbar-widgets-editor-link:hover {
color: #aaaaaa;
}
a.phpdebugbar-widgets-editor-link:before {
font-family: PhpDebugbarFontAwesome;
margin-right: 4px;
font-size: 12px;
font-style: normal;
}
a.phpdebugbar-widgets-editor-link:before {
content: "\f08e";
margin-left: 4px;
}
/* -------------------------------------- */
ul.phpdebugbar-widgets-list {
margin: 0;
padding: 0;
list-style: none;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
}
ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item {
padding: 3px;
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 ul.phpdebugbar-widgets-list {
padding-bottom: 45px;
}
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value:before {
font-family: PhpDebugbarFontAwesome;
margin-right: 8px;
font-size: 11px;
}
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-alert:before {
content: "\f0f3";
color: #cbcf38;
}
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-debug:before {
content: "\f188";
color: #78d79a;
}
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-warning:before,
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-emergency:before,
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-notice:before {
content: "\f071";
color: #ecb03d;
}
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-error,
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-critical:before {
color: red;
}
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-error:before,
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-critical:before {
content: "\f057";
}
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item pre.sf-dump {
display: inline;
}
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 li .phpdebugbar-widgets-label-called-from {
float: right;
color: #666;
padding-left: 5px;
border-bottom: 1px dotted #666;
}
div.phpdebugbar-widgets-messages li .phpdebugbar-widgets-label-called-from:before {
content: "\f08d";
font-family: PhpDebugbarFontAwesome;
margin-right: 4px;
font-size: 12px;
}
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,
dl.phpdebugbar-widgets-htmlvarlist {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
}
dl.phpdebugbar-widgets-htmlvarlist dd {
cursor: initial;
}
/* -------------------------------------- */
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: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, 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: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, 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-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: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
}
div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-filename {
float: none;
}
ul.phpdebugbar-widgets-timeline table.phpdebugbar-widgets-params {
display: table;
border: 0;
width: 99%;
}

View File

@ -0,0 +1,853 @@
if (typeof(PhpDebugBar) == 'undefined') {
// namespace
var PhpDebugBar = {};
PhpDebugBar.$ = jQuery;
}
(function($) {
/**
* @namespace
*/
PhpDebugBar.Widgets = {};
var csscls = PhpDebugBar.utils.makecsscls('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(code, {language: lang}).value;
}
return hljs.highlightAuto(code).value;
}
if (typeof(hljs) === 'object') {
code.each(function(i, e) { hljs.highlightElement(e); });
}
return code;
};
/**
* Creates a <pre> element with a block of code
*
* @param {String} code
* @param {String} lang
* @param {Number} [firstLineNumber] If provided, shows line numbers beginning with the given value.
* @param {Number} [highlightedLine] If provided, the given line number will be highlighted.
* @return {String}
*/
var createCodeBlock = PhpDebugBar.Widgets.createCodeBlock = function(code, lang, firstLineNumber, highlightedLine) {
var pre = $('<pre />').addClass(csscls('code-block'));
// Add a newline to prevent <code> element from vertically collapsing too far if the last
// code line was empty: that creates problems with the horizontal scrollbar being
// incorrectly positioned - most noticeable when line numbers are shown.
var codeElement = $('<code />').text(code + '\n').appendTo(pre);
// Format the code
if (lang) {
codeElement.addClass("language-" + lang);
}
highlight(codeElement).removeClass('hljs');
// Show line numbers in a list
if (!isNaN(parseFloat(firstLineNumber))) {
var lineCount = code.split('\n').length;
var $lineNumbers = $('<ul />').prependTo(pre);
pre.children().addClass(csscls('numbered-code'));
for (var i = firstLineNumber; i < firstLineNumber + lineCount; i++) {
var li = $('<li />').text(i).appendTo($lineNumbers);
// Add a span with a special class if we are supposed to highlight a line.
if (highlightedLine === i) {
li.addClass(csscls('highlighted-line')).append('<span>&nbsp;</span>');
}
}
}
return pre;
};
var getDictValue = PhpDebugBar.utils.getDictValue = function(dict, key, default_value) {
var d = dict, parts = key.split('.');
for (var i = 0; i < parts.length; i++) {
if (!d[parts[i]]) {
return default_value;
}
d = d[parts[i]];
}
return d;
}
// ------------------------------------------------------------------
// 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 && value.value || 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);
}
});
}
});
// ------------------------------------------------------------------
/**
* An extension of KVListWidget where the data represents a list
* of variables whose contents are HTML; this is useful for showing
* variable output from VarDumper's HtmlDumper.
*
* Options:
* - data
*/
var HtmlVariableListWidget = PhpDebugBar.Widgets.HtmlVariableListWidget = KVListWidget.extend({
className: csscls('kvlist htmlvarlist'),
itemRenderer: function(dt, dd, key, value) {
$('<span />').attr('title', $('<i />').html(key || '').text()).html(key || '').appendTo(dt);
dd.html(value && value.value || value);
if (value && value.xdebug_link) {
var header = $('<span />').addClass(csscls('filename')).text(value.xdebug_link.filename + ( value.xdebug_link.line ? "#" + value.xdebug_link.line : ''));
if (value.xdebug_link) {
if (value.xdebug_link.ajax) {
$('<a title="' + value.xdebug_link.url + '"></a>').on('click', function () {
$.ajax(value.xdebug_link.url);
}).addClass(csscls('editor-link')).appendTo(header);
} else {
$('<a href="' + value.xdebug_link.url + '"></a>').addClass(csscls('editor-link')).appendTo(header);
}
}
header.appendTo(dd);
}
}
});
// ------------------------------------------------------------------
/**
* 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) {
if (value.message_html) {
var val = $('<span />').addClass(csscls('value')).html(value.message_html).appendTo(li);
} else {
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 (window.getSelection().type == "Range") {
return''
}
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.xdebug_link) {
var header = $('<span />').addClass(csscls('filename')).text(value.xdebug_link.filename + ( value.xdebug_link.line ? "#" + value.xdebug_link.line : ''));
if (value.xdebug_link) {
if (value.xdebug_link.ajax) {
$('<a title="' + value.xdebug_link.url + '"></a>').on('click', function () {
$.ajax(value.xdebug_link.url);
}).addClass(csscls('editor-link')).appendTo(header);
} else {
$('<a href="' + value.xdebug_link.url + '"></a>').addClass(csscls('editor-link')).appendTo(header);
}
}
header.appendTo(li);
}
if (value.collector) {
$('<span />').addClass(csscls('collector')).text(value.collector).prependTo(li);
}
if (value.label) {
val.addClass(csscls(value.label));
$('<span />').addClass(csscls('label')).text(value.label).prependTo(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" name="search" aria-label="Search" placeholder="Search" />')
.on('change', function() { self.set('search', this.value); })
.appendTo(this.$toolbar);
this.bindAttr('data', function(data) {
this.set({excludelabel: [], excludecollector: [], search: ''});
this.$toolbar.find(csscls('.filter')).remove();
var labels = [], collectors = [], self = this,
createFilterItem = function (type, value) {
$('<a />')
.addClass(csscls('filter')).addClass(csscls(type))
.text(value).attr('rel', value)
.on('click', function() { self.onFilterClick(this, type); })
.appendTo(self.$toolbar)
};
data.forEach(function (item) {
if (!labels.includes(item.label || 'none')) {
labels.push(item.label || 'none');
}
if (!collectors.includes(item.collector || 'none')) {
collectors.push(item.collector || 'none');
}
});
if (labels.length > 1) {
labels.forEach(label => createFilterItem('label', label));
}
if (collectors.length === 1) {
return;
}
$('<a />').addClass(csscls('filter')).css('visibility', 'hidden').appendTo(self.$toolbar);
collectors.forEach(collector => createFilterItem('collector', collector));
});
this.bindAttr(['excludelabel', 'excludecollector', 'search'], function() {
var excludelabel = this.get('excludelabel') || [],
excludecollector = this.get('excludecollector') || [],
search = this.get('search'),
caseless = false,
fdata = [];
if (search && search === search.toLowerCase()) {
caseless = true;
}
this.get('data').forEach(function (item) {
var message = caseless ? item.message.toLowerCase() : item.message;
if (
!excludelabel.includes(item.label || undefined) &&
!excludecollector.includes(item.collector || undefined) &&
(!search || message.indexOf(search) > -1)
) {
fdata.push(item);
}
});
this.$list.set('data', fdata);
});
},
onFilterClick: function(el, type) {
$(el).toggleClass(csscls('excluded'));
var excluded = [];
this.$toolbar.find(csscls('.filter') + csscls('.excluded') + csscls('.' + type)).each(function() {
excluded.push(this.rel === 'none' || !this.rel ? undefined : this.rel);
});
this.set('exclude' + type, excluded);
}
});
// ------------------------------------------------------------------
/**
* 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) {
// ported from php DataFormatter
var formatDuration = function(seconds) {
if (seconds < 0.001)
return (seconds * 1000000).toFixed() + 'μs';
else if (seconds < 0.1)
return (seconds * 1000).toFixed(2) + 'ms';
else if (seconds < 1)
return (seconds * 1000).toFixed() + 'ms';
return (seconds).toFixed(2) + 's';
};
// ported from php DataFormatter
var formatBytes = function formatBytes(size) {
if (size === 0 || size === null) {
return '0B';
}
var sign = size < 0 ? '-' : '',
size = Math.abs(size),
base = Math.log(size) / Math.log(1024),
suffixes = ['B', 'KB', 'MB', 'GB', 'TB'];
return sign + (Math.round(Math.pow(1024, base - Math.floor(base)) * 100) / 100) + suffixes[Math.floor(base)];
}
this.$el.empty();
if (data.measures) {
var aggregate = {};
for (var i = 0; i < data.measures.length; i++) {
var measure = data.measures[i];
if(!aggregate[measure.label])
aggregate[measure.label] = { count: 0, duration: 0, memory : 0 };
aggregate[measure.label]['count'] += 1;
aggregate[measure.label]['duration'] += measure.duration;
aggregate[measure.label]['memory'] += (measure.memory || 0);
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 +(measure.memory ? '/' + measure.memory_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>').hide().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() {
if (window.getSelection().type == "Range") {
return''
}
var table = $(this).find('table');
if (table.is(':visible')) {
table.hide();
} else {
table.show();
}
});
}
}
// convert to array and sort by duration
aggregate = $.map(aggregate, function(data, label) {
return {
label: label,
data: data
}
}).sort(function(a, b) {
return b.data.duration - a.data.duration
});
// build table and add
var aggregateTable = $('<table></table>').addClass(csscls('params'));
$.each(aggregate, function(i, aggregate) {
width = Math.min((aggregate.data.duration * 100 / data.duration).toFixed(2), 100);
aggregateTable.append('<tr><td class="' + csscls('name') + '">' +
aggregate.data.count + ' x ' + $('<i />').text(aggregate.label).html() + ' (' + width + '%)</td><td class="' + csscls('value') + '">' +
'<div class="' + csscls('measure') +'">' +
'<span class="' + csscls('value') + '"></span>' +
'<span class="' + csscls('label') + '">' + formatDuration(aggregate.data.duration) + (aggregate.data.memory ? '/' + formatBytes(aggregate.data.memory) : '') + '</span>' +
'</div></td></tr>');
aggregateTable.find('span.' + csscls('value') + ':last').css({width: width + "%" });
});
this.$el.append('<li/>').find('li:last').append(aggregateTable);
}
});
}
});
// ------------------------------------------------------------------
/**
* 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) {
var header = $('<span />').addClass(csscls('filename')).text(e.file + "#" + e.line);
if (e.xdebug_link) {
if (e.xdebug_link.ajax) {
$('<a title="' + e.xdebug_link.url + '"></a>').on('click', function () {
fetch(e.xdebug_link.url);
}).addClass(csscls('editor-link')).appendTo(header);
} else {
$('<a href="' + e.xdebug_link.url + '"></a>').addClass(csscls('editor-link')).appendTo(header);
}
}
header.appendTo(li);
}
if (e.type) {
$('<span />').addClass(csscls('type')).text(e.type).appendTo(li);
}
if (e.surrounding_lines) {
var startLine = (e.line - 3) <= 0 ? 1 : e.line - 3;
var pre = createCodeBlock(e.surrounding_lines.join(""), 'php', startLine, e.line).addClass(csscls('file')).appendTo(li);
if (!e.stack_trace_html) {
// This click event makes the var-dumper hard to use.
li.click(function () {
if (pre.is(':visible')) {
pre.hide();
} else {
pre.show();
}
});
}
}
if (e.stack_trace_html) {
var $trace = $('<span />').addClass(csscls('filename')).html(e.stack_trace_html);
$trace.appendTo(li);
} else if (e.stack_trace) {
e.stack_trace.split("\n").forEach(function (trace) {
var $traceLine = $('<div />');
$('<span />').addClass(csscls('filename')).text(trace).appendTo($traceLine);
$traceLine.appendTo(li);
});
}
}});
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();
}
});
}
});
/**
* Displays datasets in a table
*
*/
var DatasetWidget = PhpDebugBar.Widgets.DatasetWidget = PhpDebugBar.Widget.extend({
initialize: function(options) {
if (!options['itemRenderer']) {
options['itemRenderer'] = this.itemRenderer;
}
this.set(options);
this.set('autoshow', null);
this.set('id', null);
this.set('sort', localStorage.getItem('debugbar-history-sort') || 'asc');
this.$el.addClass(csscls('dataset-history'))
this.renderHead();
},
renderHead: function() {
this.$el.empty();
this.$actions = $('<div />').addClass(csscls('dataset-actions')).appendTo(this.$el);
var self = this;
this.$autoshow = $('<input type=checkbox>')
.on('click', function() {
if (self.get('debugbar').ajaxHandler) {
self.get('debugbar').ajaxHandler.setAutoShow($(this).is(':checked'));
}
});
$('<label>Autoshow</label>')
.append(this.$autoshow)
.appendTo(this.$actions)
this.$clearbtn = $('<a>Clear</a>')
.appendTo(this.$actions)
.on('click', function() {
self.$table.empty();
});
this.$showBtn = $('<a>Show all</a>')
.appendTo(this.$actions)
.on('click', function() {
self.searchInput.val(null);
self.methodInput.val(null);
self.set('search', null);
self.set('method', null);
});
this.methodInput = $('<select name="method" style="width:100px"><option>(method)</option><option>GET</option><option>POST</option><option>PUT</option><option>DELETE</option></select>')
.on('change', function() { self.set('method', this.value)})
.appendTo(this.$actions)
this.searchInput = $('<input type="text" name="search" aria-label="Search" placeholder="Search" />')
.on('input', function() { self.set('search', this.value); })
.appendTo(this.$actions);
this.$table = $('<tbody />');
$('<table/>')
.append($('<thead/>')
.append($('<tr/>')
.append($('<th></th>').css('width', '30px'))
.append($('<th>Date ↕</th>').css('width', '175px').click(function() {
self.set('sort', self.get('sort') === 'asc' ? 'desc' : 'asc')
localStorage.setItem('debugbar-history-sort', self.get('sort'))
}))
.append($('<th>Method</th>').css('width', '80px'))
.append($('<th>URL</th>'))
.append($('<th width="40%">Data</th>')))
)
.append(this.$table)
.appendTo(this.$el);
},
renderDatasets: function() {
this.$table.empty();
var self = this;
$.each(this.get('data'), function(key, data) {
if (!data.__meta) {
return;
}
self.get('itemRenderer')(self, data);
});
},
render: function() {
this.bindAttr('data', function() {
if (this.get('autoshow') === null && this.get('debugbar').ajaxHandler) {
this.set('autoshow', this.get('debugbar').ajaxHandler.autoShow);
}
if (!this.has('data')) {
return;
}
// Render the latest item
var datasets = this.get('data');
var data = datasets[Object.keys(datasets)[Object.keys(datasets).length - 1]]
if (!data.__meta) {
return;
}
this.get('itemRenderer')(this, data);
});
this.bindAttr(['itemRenderer', 'search', 'method', 'sort'], function() {
this.renderDatasets();
})
this.bindAttr('autoshow', function() {
var autoshow = this.get('autoshow');
this.$autoshow.prop('checked', autoshow);
})
this.bindAttr('id', function() {
var id = this.get('id');
this.$table.find('.' + csscls('active')).removeClass(csscls('active'));
this.$table.find('tr[data-id=' + id+']').addClass(csscls('active'));
})
},
/**
* Renders the content of a dataset item
*
* @param {Object} value An item from the data array
*/
itemRenderer: function(widget, data) {
var meta = data.__meta;
var $badges = $('<td />');
var tr = $('<tr />');
if (widget.get('sort') === 'asc') {
tr.appendTo(widget.$table);
} else {
tr.prependTo(widget.$table);
}
var clickHandler = function() {
var debugbar = widget.get('debugbar');
debugbar.showDataSet(meta.id, debugbar.datesetTitleFormater.format('', data, meta.suffix, meta.nb));
widget.$table.find('.' + csscls('active')).removeClass(csscls('active'));
tr.addClass(csscls('active'));
if ($(this).data('tab')) {
debugbar.showTab($(this).data('tab'));
}
}
tr.attr('data-id', meta['id'])
.append($('<td>#' + meta['nb'] + '</td>').click(clickHandler))
.append($('<td>' + meta['datetime'] + '</td>').click(clickHandler))
.append($('<td>' + meta['method'] + '</td>').click(clickHandler))
.append($('<td />').append(meta['uri'] + (meta['suffix'] ? ' ' + meta['suffix'] : '')).click(clickHandler))
.css('cursor', 'pointer')
.addClass(csscls('table-row'))
var debugbar = widget.get('debugbar');
$.each(debugbar.dataMap, function(key, def) {
var d = getDictValue(data, def[0], def[1]);
if (key.indexOf(':') != -1) {
key = key.split(':');
if (key[1] === 'badge' && d > 0) {
var control = debugbar.getControl(key[0]);
var $a = $('<a>').attr('title', control.get('title')).data('tab', key[0]);
if (control.$icon) {
$a.append(debugbar.getControl(key[0]).$icon.clone());
}
if (control.$badge) {
$a.append(debugbar.getControl(key[0]).$badge.clone().css('display', 'inline-block').text(d));
}
$a.appendTo($badges).click(clickHandler);
}
}
});
tr.append($badges);
if (debugbar.activeDatasetId === meta['id']) {
tr.addClass(csscls('active'));
}
var search = widget.get('search');
var method = widget.get('method');
if ((search && meta['uri'].indexOf(search) == -1) || (method && meta['method'] !== method)) {
tr.hide();
}
}
});
})(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: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
}

View File

@ -0,0 +1,63 @@
(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.body || mail.html) {
var header = $('<span />').addClass(csscls('filename')).text('');
$('<a title="Mail Preview">View Mail</a>').on('click', function () {
var popup = window.open('about:blank', 'Mail Preview', 'width=650,height=440,scrollbars=yes');
var documentToWriteTo = popup.document;
var headers = !mail.headers ? '' : $('<pre style="border: 1px solid #ddd; padding: 5px;" />')
.append($('<code />').text(mail.headers));
var body = $('<pre style="border: 1px solid #ddd; padding: 5px;" />').text(mail.body)
var html = null;
if (mail.html) {
body = $('<details />').append($('<summary>Text version</summary>')).append(body);
html = $('<iframe width="100%" height="400px" sandbox="" referrerpolicy="no-referrer"/>').attr("srcdoc", mail.html)
}
documentToWriteTo.open();
documentToWriteTo.write(headers.prop('outerHTML') + body.prop('outerHTML') + (html ? html.prop('outerHTML') : ''));
documentToWriteTo.close();
}).addClass(csscls('editor-link')).appendTo(header);
header.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,153 @@
div.phpdebugbar-widgets-sqlqueries .phpdebugbar-widgets-status {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, 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-copy-clipboard,
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-copy-clipboard,
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-copy-clipboard:before,
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-stmt-id:before,
div.phpdebugbar-widgets-sqlqueries a.phpdebugbar-widgets-editor-link: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 span.phpdebugbar-widgets-copy-clipboard:before {
content: "\f0c5";
}
div.phpdebugbar-widgets-sqlqueries a.phpdebugbar-widgets-editor-link:before {
content: "\f08e";
}
div.phpdebugbar-widgets-sqlqueries a.phpdebugbar-widgets-editor-link {
color: #888;
}
div.phpdebugbar-widgets-sqlqueries a.phpdebugbar-widgets-editor-link:hover {
color: #aaaaaa;
}
div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params {
display: none;
width: 70%;
margin: 10px;
border: 1px solid #ddd;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, 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: calc(100% - 30px);
margin-bottom: 5px;
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;
}
div.phpdebugbar-widgets-sqlqueries a.phpdebugbar-widgets-duplicates {
font-weight: bold;
text-decoration: underline;
}
div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item div.phpdebugbar-widgets-bg-measure {
position: absolute;
top: 0;
width: 100%;
height: 100%;
overflow: hidden;
pointer-events: none;
}
div.phpdebugbar-widgets-sqlqueries div.phpdebugbar-widgets-bg-measure div.phpdebugbar-widgets-value {
position: absolute;
height: 100%;
opacity: 0.2;
background: red;
}
div.phpdebugbar-widgets-sqlqueries td.phpdebugbar-widgets-value li.phpdebugbar-widgets-table-list-item {
text-align: left;
padding-left: 6px;
}
div.phpdebugbar-widgets-sqlqueries .phpdebugbar-text-muted {
color: #888;
}

View File

@ -0,0 +1,236 @@
(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'));
this.$list.$el.find("li[connection=" + $(el).attr("rel") + "]").toggle();
},
onCopyToClipboard: function (el) {
var code = $(el).parent('li').find('code').get(0);
var copy = function () {
try {
document.execCommand('copy');
alert('Query copied to the clipboard');
} catch (err) {
console.log('Oops, unable to copy');
}
};
var select = function (node) {
if (document.selection) {
var range = document.body.createTextRange();
range.moveToElementText(node);
range.select();
} else if (window.getSelection) {
var range = document.createRange();
range.selectNodeContents(node);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
}
copy();
window.getSelection().removeAllRanges();
};
select(code);
},
renderList: function (caption, icon, data) {
var $ul = $('<ul />').addClass(csscls('table-list')), $parts;
var $li = $('<li />').addClass(csscls('table-list-item'));
var $span = $('<span />').addClass('phpdebugbar-text-muted');
for (var key in data) {
var value = typeof data[key] === 'function' ? data[key].name + ' {}' : data[key];
$li.clone().append(typeof value === 'object' && value !== null
? [$span.clone().text(value.index || key).append('.'), '&nbsp;']
.concat(value.namespace ? [value.namespace + '::'] : [])
.concat([value.name || value.file])
.concat(value.line ? [$span.clone().text(':' + value.line)] : [])
: [$span.clone().text(key + ':'), '&nbsp;', value]
).appendTo($ul);
}
caption += icon ? ' <i class="phpdebugbar-fa phpdebugbar-fa-' + icon + ' phpdebugbar-text-muted"></i>' : '';
return $('<tr />').append(
$('<td />').addClass(csscls('name')).html(caption),
$('<td />').addClass(csscls('value')).append($ul)
);
},
render: function() {
this.$status = $('<div />').addClass(csscls('status')).appendTo(this.$el);
this.$toolbar = $('<div />').addClass(csscls('toolbar')).appendTo(this.$el);
var filters = [], self = this;
this.$list = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, stmt) {
if (stmt.type === 'transaction') {
$('<strong />').addClass(csscls('sql')).addClass(csscls('name')).text(stmt.sql).appendTo(li);
} else {
$('<code />').addClass(csscls('sql')).html(PhpDebugBar.Widgets.highlight(stmt.sql, 'sql')).appendTo(li);
}
if (stmt.width_percent) {
$('<div />').addClass(csscls('bg-measure')).append(
$('<div />').addClass(csscls('value')).css({
left: stmt.start_percent + '%',
width: Math.max(stmt.width_percent, 0.01) + '%',
})
).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.type || stmt.type === 'query') && stmt.show_copy !== false) {
$('<span title="Copy to clipboard" />')
.addClass(csscls('copy-clipboard'))
.css('cursor', 'pointer')
.on('click', function (event) {
self.onCopyToClipboard(this);
event.stopPropagation();
})
.appendTo(li);
}
if (typeof(stmt.xdebug_link) !== 'undefined' && stmt.xdebug_link) {
var header = $('<span title="Filename" />').addClass(csscls('filename')).text(stmt.xdebug_link.filename + ( stmt.xdebug_link.line ? "#" + stmt.xdebug_link.line : ''));
if (stmt.xdebug_link.ajax) {
$('<a title="' + stmt.xdebug_link.url + '"></a>').on('click', function () {
fetch(stmt.xdebug_link.url);
}).addClass(csscls('editor-link')).appendTo(header);
} else {
$('<a href="' + stmt.xdebug_link.url + '"></a>').addClass(csscls('editor-link')).appendTo(header);
}
header.appendTo(li);
}
var table = $('<table></table>').addClass(csscls('params'));
if (stmt.params && !$.isEmptyObject(stmt.params)) {
self.renderList('Params', 'thumb-tack', stmt.params).appendTo(table);
}
if (stmt.bindings && !$.isEmptyObject(stmt.bindings)) {
self.renderList('Bindings', 'thumb-tack', stmt.bindings).appendTo(table);
}
if (stmt.hints && !$.isEmptyObject(stmt.hints)) {
self.renderList('Hints', 'question-circle', stmt.hints).appendTo(table);
}
if (stmt.backtrace && !$.isEmptyObject(stmt.backtrace)) {
self.renderList('Backtrace', 'list-ul', stmt.backtrace).appendTo(table);
}
if (table.find('tr').length) {
table.appendTo(li);
li.css('cursor', 'pointer').click(function() {
if (window.getSelection().type == "Range") {
return''
}
if (table.is(':visible')) {
table.hide();
} else {
table.show();
}
});
}
}});
this.$list.$el.appendTo(this.$el);
this.bindAttr('data', function(data) {
// the PDO collector maybe is empty
if (data.length <= 0 || !data.statements) {
return false;
}
filters = [];
this.$toolbar.hide().find(csscls('.filter')).remove();
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++) {
if (data.statements[i].type && data.statements[i].type !== 'query') {
continue;
}
var stmt = data.statements[i].sql;
if (data.statements[i].params && !$.isEmptyObject(data.statements[i].params)) {
stmt += JSON.stringify(data.statements[i].params);
}
if (data.statements[i].bindings && !$.isEmptyObject(data.statements[i].bindings)) {
stmt += JSON.stringify(data.statements[i].bindings);
}
if (data.statements[i].connection) {
stmt += '@' + data.statements[i].connection;
}
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 += sql[stmt].keys.length;
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'));
}
}
}
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 duplicates");
t.append(", " + (data.nb_statements - duplicate) + " unique. ");
// add toggler for displaying only duplicated queries
var duplicatedText = 'Show only duplicated';
$('<a />').addClass(csscls('duplicates')).click(function () {
$(this).toggleClass('shown-duplicated')
.text($(this).hasClass('shown-duplicated') ? 'Show All' : duplicatedText);
$('.' + self.className + ' .' + csscls('list-item'))
.not('.' + csscls('sql-duplicate')).toggle();
}).text(duplicatedText).appendTo(t);
}
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,73 @@
div.phpdebugbar-widgets-templates div.phpdebugbar-widgets-status {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, 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 a.phpdebugbar-widgets-editor-link,
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 a.phpdebugbar-widgets-editor-link,
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,
div.phpdebugbar-widgets-templates a.phpdebugbar-widgets-editor-link:before,
div.phpdebugbar-widgets-templates a.phpdebugbar-widgets-editor-link:before
{
font-family: PhpDebugbarFontAwesome;
margin-right: 4px;
font-size: 12px;
}
div.phpdebugbar-widgets-templates a.phpdebugbar-widgets-editor-link:hover
{
color: #aaaaaa;
}
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 a.phpdebugbar-widgets-editor-link:before {
content: "\f08e";
margin-left: 4px;
}
div.phpdebugbar-widgets-templates table.phpdebugbar-widgets-params {
display: none;
width: 70%;
margin: 10px;
border: 1px solid #ddd;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, 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,103 @@
(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 (typeof tpl.xdebug_link !== 'undefined' && tpl.xdebug_link !== null) {
var header = $('<span />').addClass(csscls('filename')).text(tpl.xdebug_link.filename + ( tpl.xdebug_link.line ? "#" + tpl.xdebug_link.line : ''));
if (tpl.xdebug_link) {
if (tpl.xdebug_link.ajax) {
$('<a title="' + tpl.xdebug_link.url + '"></a>').on('click', function () {
fetch(tpl.xdebug_link.url);
}).addClass(csscls('editor-link')).appendTo(header);
} else {
$('<a href="' + tpl.xdebug_link.url + '"></a>').addClass(csscls('editor-link')).appendTo(header);
}
}
header.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 (typeof(tpl.editorLink) != 'undefined' && tpl.editorLink) {
$('<a href="'+ tpl.editorLink +'" />').on('click', function (event) {
event.stopPropagation();
}).addClass(csscls('editor-link')).text('file').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 (window.getSelection().type == "Range") {
return''
}
if (table.is(':visible')) {
table.hide();
} else {
table.show();
}
});
}
}});
this.$list.$el.appendTo(this.$el);
this.$callgraph = $('<div />').addClass(csscls('callgraph')).appendTo(this.$el);
this.bindAttr('data', function(data) {
this.$list.set('data', data.templates);
this.$status.empty();
this.$callgraph.empty();
var sentence = data.sentence || "templates were rendered";
$('<span />').text(data.nb_templates + " " + 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));
}
if (data.nb_blocks > 0) {
$('<div />').text(data.nb_blocks + " blocks were rendered").appendTo(this.$status);
}
if (data.nb_macros > 0) {
$('<div />').text(data.nb_macros + " macros were rendered").appendTo(this.$status);
}
if (typeof data.callgraph !== 'undefined') {
this.$callgraph.html(data.callgraph);
}
});
}
});
})(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'] ? 1 : -1;
});
//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,158 @@
<?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;
use ReflectionMethod;
/**
* Stores collected data into Memcache using the Memcached extension
*/
class MemcachedStorage implements StorageInterface
{
protected $memcached;
protected $keyNamespace;
protected $expiration;
protected $newGetMultiSignature;
/**
* @param Memcached $memcached
* @param string $keyNamespace Namespace for Memcached key names (to avoid conflict with other Memcached users).
* @param int $expiration Expiration for Memcached entries (see Expiration Times in Memcached documentation).
*/
public function __construct(Memcached $memcached, $keyNamespace = 'phpdebugbar', $expiration = 0)
{
$this->memcached = $memcached;
$this->keyNamespace = $keyNamespace;
$this->expiration = $expiration;
}
/**
* {@inheritdoc}
*/
public function save($id, $data)
{
$key = $this->createKey($id);
$this->memcached->set($key, $data, $this->expiration);
if (!$this->memcached->append($this->keyNamespace, "|$key")) {
$this->memcached->set($this->keyNamespace, $key, $this->expiration);
} else if ($this->expiration) {
// append doesn't support updating expiration, so do it here:
$this->memcached->touch($this->keyNamespace, $this->expiration);
}
}
/**
* {@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();
$keys = array_reverse(explode('|', $keys)); // Reverse so newest comes first
$keyPosition = 0; // Index in $keys to try to get next items from
$remainingItems = $max + $offset; // Try to obtain this many remaining items
// Loop until we've found $remainingItems matching items or no more items may exist.
while ($remainingItems > 0 && $keyPosition < count($keys)) {
// Consume some keys from $keys:
$itemsToGet = array_slice($keys, $keyPosition, $remainingItems);
$keyPosition += $remainingItems;
// Try to get them, and filter them:
$newItems = $this->memcachedGetMulti($itemsToGet, Memcached::GET_PRESERVE_ORDER);
if ($newItems) {
foreach ($newItems as $data) {
$meta = $data['__meta'];
if ($this->filter($meta, $filters)) {
$remainingItems--;
// Keep the result only if we've discarded $offset items first
if ($offset <= 0) {
$results[] = $meta;
} else {
$offset--;
}
}
}
}
}
return $results;
}
/**
* 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");
}
/**
* The memcached getMulti function changed in version 3.0.0 to only have two parameters.
*
* @param array $keys
* @param int $flags
*/
protected function memcachedGetMulti($keys, $flags)
{
if ($this->newGetMultiSignature === null) {
$this->newGetMultiSignature = (new ReflectionMethod('Memcached', 'getMulti'))->getNumberOfParameters() === 2;
}
if ($this->newGetMultiSignature) {
return $this->memcached->getMulti($keys, $flags);
} else {
$null = null;
return $this->memcached->getMulti($keys, $null, $flags);
}
}
}

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,106 @@
<?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 Redis
*/
class RedisStorage implements StorageInterface
{
/** @var \Predis\Client|\Redis */
protected $redis;
/** @var string */
protected $hash;
/**
* @param \Predis\Client|\Redis $redis Redis Client
* @param string $hash
*/
public function __construct($redis, $hash = 'phpdebugbar')
{
$this->redis = $redis;
$this->hash = $hash;
}
/**
* {@inheritdoc}
*/
public function save($id, $data)
{
$this->redis->hSet("$this->hash:meta", $id, serialize($data['__meta']));
unset($data['__meta']);
$this->redis->hSet("$this->hash:data", $id, serialize($data));
}
/**
* {@inheritdoc}
*/
public function get($id)
{
return array_merge(unserialize($this->redis->hGet("$this->hash:data", $id)),
array('__meta' => unserialize($this->redis->hGet("$this->hash:meta", $id))));
}
/**
* {@inheritdoc}
*/
public function find(array $filters = [], $max = 20, $offset = 0)
{
$results = [];
$cursor = "0";
$isPhpRedis = get_class($this->redis) === 'Redis' || get_class($this->redis) === 'RedisCluster';
do {
if ($isPhpRedis) {
$data = $this->redis->hScan("$this->hash:meta", $cursor);
} else {
[$cursor, $data] = $this->redis->hScan("$this->hash:meta", $cursor);
}
foreach ($data as $meta) {
if ($meta = unserialize($meta)) {
if ($this->filter($meta, $filters)) {
$results[] = $meta;
}
}
}
} while($cursor);
usort($results, static function ($a, $b) {
return $b['utime'] <=> $a['utime'];
});
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:data");
$this->redis->del("$this->hash:meta");
}
}

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);