refactored Profiler and DataCollector classes (the WDT has been removed and will be re-added in the upcoming WebProfilerBundle)

This commit is contained in:
Fabien Potencier 2010-08-28 15:23:21 +02:00
parent eb66e0dc00
commit 72db4c7342
30 changed files with 1065 additions and 592 deletions

View File

@ -2,8 +2,10 @@
namespace Symfony\Bundle\DoctrineBundle\DataCollector;
use Symfony\Component\HttpKernel\Profiler\DataCollector\DataCollector;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\DoctrineBundle\Logger\DbalLogger;
/*
* This file is part of the Symfony framework.
@ -21,21 +23,21 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*/
class DoctrineDataCollector extends DataCollector
{
protected $container;
protected $logger;
public function __construct(ContainerInterface $container)
public function __construct(DbalLogger $logger = null)
{
$this->container = $container;
$this->logger = $logger;
}
public function collect()
/**
* {@inheritdoc}
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
$this->data = array();
if ($this->container->has('doctrine.dbal.logger')) {
$this->data = array(
'queries' => $this->container->getDoctrine_Dbal_LoggerService()->queries,
);
}
$this->data = array(
'queries' => null !== $this->logger ? $this->logger->queries : array(),
);
}
public function getQueryCount()
@ -48,16 +50,9 @@ class DoctrineDataCollector extends DataCollector
return $this->data['queries'];
}
public function getSummary()
{
$queries = count($this->data['queries']);
$queriesColor = $queries < 10 ? '#2d2' : '#d22';
return sprintf('<img style="margin-left: 10px; vertical-align: middle" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAKlJREFUeNrsk0EOgyAQRT9KiLog7D0Ql+B4HsULeAHXQFwaiCGBCm1Nmi4a69a/IDNk5g+8ZEhKCcMwYFfCORGlFOgrSVJKNE0DxhgofV6HELBtG5xz8N6XuK7rUjOOYx5I3gbQWoNzDiEEuq5DjLE0LcsCYwystVjXFW3bou/74xkVLuqywfGFaZp+T6uqwmGe52+DPyB+GtwQb4h5q3aI6SREko+HAAMADJ+V5b1xqucAAAAASUVORK5CYII=" />
<span style="color: %s">%d</span>
', $queriesColor, $queries);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'db';

View File

@ -21,7 +21,7 @@
<service id="doctrine.data_collector" class="%doctrine.data_collector.class%">
<tag name="data_collector" />
<argument type="service" id="service_container" />
<argument type="service" id="doctrine.dbal.logger" />
</service>
</services>
</container>

View File

@ -2,8 +2,10 @@
namespace Symfony\Bundle\DoctrineMongoDBBundle\DataCollector;
use Symfony\Component\HttpKernel\Profiler\DataCollector\DataCollector;
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
use Symfony\Bundle\DoctrineMongoDBBundle\Logger\DoctrineMongoDBLogger;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Data collector for the Doctrine MongoDB ODM.
@ -19,20 +21,22 @@ class DoctrineMongoDBDataCollector extends DataCollector
$this->logger = $logger;
}
public function collect()
/**
* {@inheritdoc}
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
$this->data['nb_queries'] = $this->logger->getNbQueries();
}
public function getSummary()
public function getQueryCount()
{
$color = $this->data['nb_queries'] < 10 ? '#2d2' : '#d22';
return sprintf('<img style="margin-left: 10px; vertical-align: middle" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAeJJREFUeNqM009r1EAUAPD3kkm22bapthSlq/EPUg9FUMSLoCAVvPUm4smr4Ico+BW89SCe6tVDj2oVLwVRS6HqTcUqwe7W7SbZbGbmzTiTvei2ML6QDAwzP968l0GtNfwdS/eu1KMSBHEytbhf9HbLdrGFiPX8i9VP/6z3YCTKvITidwETyeTN5NbZNTzFHhVZGWc/MsjTfHT5QUBKCcFMOD97be6JRhgLj0fXxy9PrXi+h6jRDfCcQ3xx+iE0/ZbgAogrCE5Gd8JzzftaKjfgHw0uBUm0JCoOpAmUUqBIgwEe4LgfO4HwdLSoQoykJJBEYGuspQaMgwU227jhBHQDFwjMZmW+Wg0BPVyJR9hVJwAT3rQkCVJbxpzZ1M0ew9ZPVWrG3YVMDKTZSDYDZY+gzYuWAs2VdHfhW3/bFq/OoEYMYB8zUkd8dgMf8zWxV+XkKRA1YNL3EGhf9uSX/ksnQCl/X7zqPCZmAVkDyDzgH3qrtCe2nACGCMV6Z7n3pv2chT6wgEG1nW1UG91lDP7jT0TfA3NxurtPv96dfOu/Pp/Oves++36buPxl2zoabHSiGlTDS9Uv24PNbOXMheRE61hrpxE14LA4APjMr8eoOQY/03RTaLVTDkrggh8K/BFgAGj/AWtED/mBAAAAAElFTkSuQmCC" />
<span style="color: %s">%d</span>
', $color, $this->data['nb_queries']);
return $this->data['nb_queries'];
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'mongodb';

View File

@ -1,64 +0,0 @@
<?php
namespace Symfony\Bundle\FrameworkBundle\DataCollector;
use Symfony\Component\HttpKernel\Profiler\DataCollector\DataCollector;
use Symfony\Component\DependencyInjection\ContainerInterface;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* AppDataCollector.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class AppDataCollector extends DataCollector
{
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function collect()
{
$request = $this->container->getRequestService();
$this->data = array(
'route' => $request->attributes->get('_route') ? $request->attributes->get('_route') : '<span style="color: #a33">NONE</span>',
'format' => $request->getRequestFormat(),
'content_type' => $this->profiler->getResponse()->headers->get('Content-Type') ? $this->profiler->getResponse()->headers->get('Content-Type') : 'text/html',
'code' => $this->profiler->getResponse()->getStatusCode(),
);
}
public function getRoute()
{
return $this->data['route'];
}
public function getFormat()
{
return $this->data['format'];
}
public function getSummary()
{
return sprintf('<img style="margin-left: 10px; vertical-align: middle" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAABKElEQVR42sSTPa6CQBSFv3lRrCRYACvQxMLCBhsTK5fAGtiAPaGfDbgGNmBCS0PFAqhsoSGSWE1yrTDvxd+E4t1qkrnn3HPPnFEiwpD6YWCNPjUcj0fquhYAz/MUQBRFzwniOH6cMBrJfD4HoKoq6UleKkiS5H7WWstut+N0OmHbNrZt92rURw+01rJerzmfzyyXSy6XC77vf2ei1lpWqxVN02CMwRhDEARUVcXHFfrJbdsyHo8BcByHsiwxxqjfBj4omE6nstlsuF6vWJaFZVl4nkdZlhwOB/U2B1mWSRiGFEWB67pMJhNc16Uoipfgpx6EYUie58xmM/I8fwsGUH2UsywTgMViAUCapnRdp9498x+COI5lu93eL/b7vfomyurfP9NggtsAfaVzbTWryOIAAAAASUVORK5CYII=" />
%s<span style="margin: 0; padding: 0; color: #aaa">/</span>%s<span style="margin: 0; padding: 0; color: #aaa">/</span><span style="color: %s">%s</span><span style="margin: 0; padding: 0; color: #aaa">/</span>%s
', $this->data['route'], $this->data['format'], 200 == $this->data['code'] ? '#3a3' : '#a33', $this->data['code'], $this->data['content_type']);
}
public function getName()
{
return 'app';
}
}

View File

@ -3,8 +3,10 @@
namespace Symfony\Bundle\FrameworkBundle\DataCollector;
use Symfony\Framework\Kernel;
use Symfony\Component\HttpKernel\Profiler\DataCollector\DataCollector;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
/*
* This file is part of the Symfony framework.
@ -22,23 +24,32 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*/
class ConfigDataCollector extends DataCollector
{
protected $container;
protected $kernel;
protected $router;
public function __construct(ContainerInterface $container)
/**
* Constructor.
*
* @param Kernel $kernel A Kernel instance
* @param RouterInterface $router A Router instance
*/
public function __construct(Kernel $kernel, RouterInterface $router = null)
{
$this->container = $container;
$this->kernel = $kernel;
$this->router = $router;
}
public function collect()
/**
* {@inheritdoc}
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
$kernel = $this->container->getKernelService();
$this->data = array(
'token' => $this->profiler->getProfilerStorage()->getToken(),
'token' => $response->headers->get('X-Debug-Token'),
'symfony_version' => Kernel::VERSION,
'name' => $kernel->getName(),
'env' => $kernel->getEnvironment(),
'debug' => $kernel->isDebug(),
'name' => $this->kernel->getName(),
'env' => $this->kernel->getEnvironment(),
'debug' => $this->kernel->isDebug(),
'php_version' => PHP_VERSION,
'xdebug' => extension_loaded('xdebug'),
'accel' => (
@ -51,17 +62,107 @@ class ConfigDataCollector extends DataCollector
);
}
public function getSummary()
/**
* Gets the URL.
*
* @return string The URL
*/
public function getUrl()
{
return sprintf('<img style="vertical-align: middle" alt="Symfony" src="data:image/png;base64,R0lGODlhOwARAMQdAIZ1X9rVz0owD/Lx78K6r2JML3pnT+bj37asn6qej1Y+H+jk4JKDb0oxEJOEcId2YGJMMPTy8KufkLetoJ+RgNvW0M7Iv56Rf25aP8/JwG5aQP///z4jAP///wAAAAAAACH5BAEAAB0ALAAAAAA7ABEAAAX/YLc8WmmeaKqu7OosYsPNdG3fuCDgfM81C4dvyAsEiEgORZNMGjaI5rBEEzAugBmWBshiOICtAbu7QL+Ky8UwExQw6i8HTqPPqJzCYMMPCDYWNBYWBRsHfIaIgWYbWIiAHAF7iAkcZjSXd0wchwQYCBZ/gTODHAQbngEbARincBsYGAMDAK2NAKsGBodpG5i+mjMGewg7HJCki7AcT18YG1e+DBtZMweBjTO4r780eHmqAQXHoxylZs7Lz2q+6IIDxxczz6/GmRzfBQKnAxgWG+MiKUuXjh0zVDMKEYg3DxYueRwSAMO3SSKBfwMEPBtA4NDAOeoaZQp3alk2kBgEVJDk421TgX+AAu5SZClkwUwCEPA5wIYhSg4KYO5pieRZFClIrcELNkTAHgVJkUqEyOGBBCRdoiItUInGhAgQtIpNoqGD2Qxo06pdy7at27dvK5gNAQA7" />
%s
<img style="margin-left: 10px; vertical-align: middle" alt="PHP" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAARCAYAAAC8XK78AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABfdJREFUeNqMVWlsFGUYfubas+y2hZZlWxahFlBKQbSg/ECUhCgQIsqRcMQfgHJoJAgRMcQ/KP5CJHgkCMQoERLBCyGIUGsIQlIOW44etGV7sle7x8zszOzMrO+MBU1AwiTfzux3vO/zPu/zvh+Tz+fxoKcr3DeMYZgpkqROSibl8fGENDqZzAbSolIoy5o7m81BVjRFltVkRlIihq50cIzSxDJKA/LKpb17P4g8yD5zPwB1Zy55fT7PC3nwC/qT6qxIVArQQDKlIqebMM08fD4fWJZFKpWiE3ds5GHZM3SNhgQ9l4xp2WitnI0dzcqpE98dPZR+IICvDpzkCgu9yw2D3xhJaNW3IzLkrA7dMMFxDDlkyAeDggIB8eh5yFIGleNmIzGgwVoaNAnG/mZtQKapQZGjGOhvborevr4rnogeqK+v1+4BsOvj7ys8HvfulMjOifdr0HUGgiDYxigFdmSmadjvEcN5bN0yF8FgCKvX7iMWcjRv2MN2zQr2Gdzlhc6bOaQG2nD92vHTbTevvNnd3XvDWuOtn23vH3zc4/L+FEsaFZqmw+EQUOARkcmEYZgmWcjTnAvFRWXIKl7097dBU3UMKwmQ0SZYW/yFpZQePznXIIsd0HI6HTPpP4fColKwXAmNx1AzLTBLVff/Smy+RKm8yG/ctH+ow+k6lJaECkotbWLg8bhwpf4gjh37Fh63CzzPQhRllJQGsWbdDnRHe2HtbfirHu1tjVCyKpwuL5YuexvFwwL4fM8Ge93tckJRFQqAx7z5y1A1aQkyeR9mPLe2vK+v+zCxNIOrfmLuZqejcAnD8oO5Y+B0Mjh39jD6+vqwddtneKpmAcrKylH3+0moSowMM2houIK167bi2ZmvIhAI4Pz5OmLIS1qgs+f+wKJFqzB/wVuYUPU0Wpov03otpk9/nhgtgMPpREnJI8VXG8+YrGFyizlOuKvkvI3cRHt7CyoqKsELY5CRSlA9+UVixo1wuAPNzY1wu50YNfoZ6OYolA6vtM8O8RWhP9Frf4+pnIpUZiiGlkxFdfUUkHwgSSkKkqXqUBEYUYnQqInzqZLYEP4jGI6YSCV7aaQIwAQixEVo/ejpvgpZzmLcuPHo7OxEKDQGuRxPoBhiqtU+GwyOQrjzJoqK/CgtrYCLUuAQJGLrIgq8btJMCIah20GyHEe6GR7kYeYiNDHkTvRerwtdt27afGSzIiJ95+yoThz/Bn6/HzNnzkZt7RlUTayhSiENMzlEbrfbAEaOLEdzUxPt86Gj7U8SrIZffjyBnp4eLF+xhnRRRLtEu0KsihIziRiv58QfdF3ZJAhuW7Uc2YxFw7bBCxfqcPnyWWhaDmPHVmHh4g10KG6norx8LIUhEGNZpFNRWyNW7atqFtGojP37thNAncAU47XXN+PRsXNAXdQmm+cdiMfC6Aw3niR3uU/ETGRxYVEoZOXH0FVC3GZv3PLuHqJqKPUDnvqPhxihJuQrxpb3viZnbqTSadIChwWvbEXBEB+artcRWA3Ll6/D5CfnERiVUuBFVhUGneepoTlIDwZOn9obVRRxJ7v/y3e6c1p6aTrVEzOpoCk43LrVRHQG4S4YjZTopk7HUe0rBEAiYQHpjOXctA1ad8FAygFRctpaoO6MspET0D/goj1OxBI5KmHJpt3h8JBuVBz7eWe6teXCCmpqN+92wpWrPqrheM+n5SPLa6KRZhLSMLB8iKLQ8TCPIHCUgG4kElGMKJsE64qwwFiOraitd2e4AbW/7Wvo6rq2nvyevecuWLlqh4/q4A2fP7De4fQHwXB2J/tHoP9eOPd7rFmeHPG8QGlQwJB31io5Smk81obGhlPRS/Unv9D13G6ylXjgbbhy5YdBhmVe5gT3QoejYKogeN08iZRlLNWzg31+cNyp4HzeFrE1DEOBJEapejqUrq7Gi92dN47E47eP0JXd+VDX8V0gq7eTr/w46m7TSKDVLMtXspwQYhlq7GC8Fg6TLiCTapucypomxRR5oEsUE62ZTH9jOp24kJGkptaWVuP/fPwtwAAItQDxq4a1ggAAAABJRU5ErkJggg==" />
%s<span style="margin: 0; padding: 0; color: #aaa">/</span><span style="color: %s">xdebug</span><span style="margin: 0; padding: 0; color: #aaa">/</span><span style="color: %s">accel</span>
<img style="margin-left: 10px; vertical-align: middle" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAATtJREFUeNqcU0Gqg0AMjZ/eQbEH0IUewIV4BXEjeD9dCvYGUlB056IU3Rf0Ci6c/hc6g/rr7+cHwkySl8xLZkZL05QORKz22jtAkiR0OkigIAjofD7T4/GgsizFLlcV/JLJAKOi4zjsME1zs8KPOHDrw7RXCwLBv0qWZZyLHMmAuq4jIcRHBW4taga3241c1+U9+r5erwrk+76kzri1gIEAII5jdcorWZMKW8aAA17OgRkYhkHLsvza9zoO/OYWQHmeZwYdFZIxKPDrGWhVVTGdKIrY6Xke1XWtrgo26EPyPN+8BTVEy7LU6bquUxiGPxigCHB939P+IZFt2x+vUOL2t8DSNA2D7vc7FUWxSYQNP/bAvXsH2jiO4nK5qMC3za1M08T2MAys+79wOvpxbdt+/I0ckL39V54CDAChFuDJX64gowAAAABJRU5ErkJggg==" />
%s<span style="margin: 0; padding: 0; color: #aaa">/</span>%s<span style="margin: 0; padding: 0; color: #aaa">/</span>%s<span style="margin: 0; padding: 0; color: #aaa">/</span><a style="color: #000" href="#%s">%s</a>
', $this->data['symfony_version'], $this->data['php_version'], $this->data['xdebug'] ? '#3a3' : '#a33', $this->data['accel'] ? '#3a3' : '#a33', $this->data['name'], $this->data['env'], $this->data['debug'] ? 'debug' : 'no-debug', $this->data['token'], $this->data['token']);
if (null !== $this->router) {
try {
return $this->router->generate('_profiler', array('token' => $this->data['token']));
} catch (\Exception $e) {
// the route is not registered
}
}
return false;
}
/**
* Gets the token.
*
* @return string The token
*/
public function getToken()
{
return $this->data['token'];
}
/**
* Gets the Symfony version.
*
* @return string The Symfony version
*/
public function getSymfonyVersion()
{
return $this->data['symfony_version'];
}
/**
* Gets the PHP version.
*
* @return string The PHP version
*/
public function getPhpVersion()
{
return $this->data['php_version'];
}
/**
* Gets the application name.
*
* @return string The application name
*/
public function getAppName()
{
return $this->data['name'];
}
/**
* Gets the environment.
*
* @return string The environment
*/
public function getEnv()
{
return $this->data['env'];
}
/**
* Returns true if the debug is enabled.
*
* @return Boolean true if debug is enabled, false otherwise
*/
public function isDebug()
{
return $this->data['debug'];
}
/**
* Returns true if the XDebug is enabled.
*
* @return Boolean true if XDebug is enabled, false otherwise
*/
public function hasXDebug()
{
return $this->data['xdebug'];
}
/**
* Returns true if an accelerator is enabled.
*
* @return Boolean true if an accelerator is enabled, false otherwise
*/
public function hasAccelerator()
{
return $this->data['accel'];
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'config';

View File

@ -0,0 +1,44 @@
<?php
namespace Symfony\Bundle\FrameworkBundle\DataCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector as BaseRequestDataCollector;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* RequestDataCollector.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class RequestDataCollector extends BaseRequestDataCollector
{
/**
* {@inheritdoc}
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
parent::collect($request, $response, $exception);
$this->data['route'] = $request->attributes->get('_route');
}
/**
* Gets the route.
*
* @return string The route
*/
public function getRoute()
{
return $this->data['route'];
}
}

View File

@ -2,8 +2,10 @@
namespace Symfony\Bundle\FrameworkBundle\DataCollector;
use Symfony\Component\HttpKernel\Profiler\DataCollector\DataCollector;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
use Symfony\Framework\Kernel;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/*
* This file is part of the Symfony framework.
@ -21,32 +23,36 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*/
class TimerDataCollector extends DataCollector
{
protected $container;
protected $kernel;
public function __construct(ContainerInterface $container)
public function __construct(Kernel $kernel)
{
$this->container = $container;
$this->kernel = $kernel;
}
public function collect()
/**
* {@inheritdoc}
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
$this->data = array(
'time' => microtime(true) - $this->container->getKernelService()->getStartTime(),
'time' => microtime(true) - $this->kernel->getStartTime(),
);
}
/**
* Gets the request time.
*
* @return integer The time
*/
public function getTime()
{
return $this->data['time'];
}
public function getSummary()
{
return sprintf('<img style="margin-left: 10px; vertical-align: middle" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAABSElEQVR42pyTwZHCMAxFXxxKUAXuIA3gFnJxERxohAt9QAM5EhpwB6rAJdjJXmKThIWdWc1oJvbo/y8p383tdmMfMcYEtKurLCKHfZ33nsMOmAFjraXrunofQmhVdQYmEVkTvwhijPMaOI5jLXLO0XUdIQSjqrOINBuCGGMu4AL03leC+/1eiQBUNZdOzFJjCth7X8GXy6WSee8Zx7F0WHCYGGOy1la1aZpqrs/zPNcaa21ZNAZoi3rf9+ScawL1O6VE3/frLtrNEgFSSm+/9Hq9AnA+n/ktvhKcTqev5GWEHELAOccwDKSUPuYwDDjnCCEAZAAjIgdV3Sh9yhKqSnFmGWEKIRjnHI/HA4Dj8VgBz+ez+mBRnzY7EJFi1WqWvRMXSxf19m2JItKoalZVY62toDXw61sonSwzJlX98zUCNGuH/Sd+BgBGROvHb4RJ6gAAAABJRU5ErkJggg==" />
%.0f ms
', $this->data['time'] * 1000);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'timer';

View File

@ -74,10 +74,6 @@ class WebExtension extends Extension
));
}
if (isset($config['toolbar']) && $config['toolbar']) {
$config['profiler'] = true;
}
if (isset($config['profiler'])) {
if ($config['profiler']) {
if (!$container->hasDefinition('profiler')) {
@ -90,18 +86,6 @@ class WebExtension extends Extension
}
}
// toolbar need to be registered after the profiler
if (isset($config['toolbar'])) {
if ($config['toolbar']) {
if (!$container->hasDefinition('debug.toolbar')) {
$loader = new XmlFileLoader($container, __DIR__.'/../Resources/config');
$loader->load('toolbar.xml');
}
} elseif ($container->hasDefinition('debug.toolbar')) {
$container->getDefinition('debug.toolbar')->clearTags();
}
}
if (isset($config['validation']['enabled'])) {
$this->registerValidationConfiguration($config, $container);
}

View File

@ -4,7 +4,7 @@ namespace Symfony\Bundle\FrameworkBundle;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Profiler\Profiler as BaseProfiler;
use Symfony\Component\HttpKernel\Profiler\ProfilerStorage;
use Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
/*
@ -23,34 +23,12 @@ use Symfony\Component\HttpKernel\Log\LoggerInterface;
*/
class Profiler extends BaseProfiler
{
protected $container;
public function __construct(ContainerInterface $container, ProfilerStorage $profilerStorage, LoggerInterface $logger = null)
public function __construct(ContainerInterface $container, ProfilerStorageInterface $storage, LoggerInterface $logger = null)
{
parent::__construct($profilerStorage, $logger);
parent::__construct($storage, $logger);
$this->container = $container;
$this->initCollectors();
$this->loadCollectorData();
}
protected function initCollectors()
{
$config = $this->container->findTaggedServiceIds('data_collector');
$ids = array();
$coreCollectors = array();
$userCollectors = array();
foreach ($config as $id => $attributes) {
$collector = $this->container->get($id);
$collector->setProfiler($this);
if (isset($attributes[0]['core']) && $attributes[0]['core']) {
$coreCollectors[$collector->getName()] = $collector;
} else {
$userCollectors[$collector->getName()] = $collector;
}
foreach ($container->findTaggedServiceIds('data_collector') as $id => $attributes) {
$this->addCollector($container->get($id));
}
$this->setCollectors(array_merge($coreCollectors, $userCollectors));
}
}

View File

@ -6,29 +6,48 @@
<parameters>
<parameter key="data_collector.config.class">Symfony\Bundle\FrameworkBundle\DataCollector\ConfigDataCollector</parameter>
<parameter key="data_collector.app.class">Symfony\Bundle\FrameworkBundle\DataCollector\AppDataCollector</parameter>
<parameter key="data_collector.request.class">Symfony\Bundle\FrameworkBundle\DataCollector\RequestDataCollector</parameter>
<parameter key="data_collector.timer.class">Symfony\Bundle\FrameworkBundle\DataCollector\TimerDataCollector</parameter>
<parameter key="data_collector.memory.class">Symfony\Component\HttpKernel\Profiler\DataCollector\MemoryDataCollector</parameter>
<parameter key="data_collector.memory.class">Symfony\Component\HttpKernel\DataCollector\MemoryDataCollector</parameter>
<parameter key="data_collector.logger.class">Symfony\Component\HttpKernel\DataCollector\LoggerDataCollector</parameter>
<parameter key="data_collector.exception.class">Symfony\Component\HttpKernel\DataCollector\ExceptionDataCollector</parameter>
<parameter key="data_collector.events.class">Symfony\Component\HttpKernel\DataCollector\EventDataCollector</parameter>
</parameters>
<services>
<service id="data_collector.config" class="%data_collector.config.class%">
<tag name="data_collector" core="true" />
<argument type="service" id="service_container" />
<tag name="data_collector" />
<argument type="service" id="kernel" />
<argument type="service" id="router" on-invalid="ignore" />
</service>
<service id="data_collector.app" class="%data_collector.app.class%">
<tag name="data_collector" core="true" />
<argument type="service" id="service_container" />
<service id="data_collector.request" class="%data_collector.request.class%">
<tag name="data_collector" />
</service>
<service id="data_collector.timer" class="%data_collector.timer.class%">
<tag name="data_collector" core="true" />
<argument type="service" id="service_container" />
<tag name="data_collector" />
<argument type="service" id="kernel" />
</service>
<service id="data_collector.memory" class="%data_collector.memory.class%">
<tag name="data_collector" core="true" />
<tag name="data_collector" />
</service>
<service id="data_collector.logger" class="%data_collector.logger.class%">
<tag name="data_collector" />
<argument type="service" id="logger" on-invalid="ignore" />
</service>
<service id="data_collector.exception" class="%data_collector.exception.class%">
<tag name="data_collector" />
</service>
<service id="data_collector.events" class="%data_collector.events.class%">
<tag name="data_collector" />
<call method="setEventDispatcher">
<argument type="service" id="event_dispatcher" />
</call>
</service>
</services>
</container>

View File

@ -6,7 +6,7 @@
<parameters>
<parameter key="profiler.class">Symfony\Bundle\FrameworkBundle\Profiler</parameter>
<parameter key="profiler.storage.class">Symfony\Component\HttpKernel\Profiler\ProfilerStorage</parameter>
<parameter key="profiler.storage.class">Symfony\Component\HttpKernel\Profiler\SQLiteProfilerStorage</parameter>
<parameter key="profiler.storage.file">%kernel.cache_dir%/profiler.db</parameter>
<parameter key="profiler.storage.lifetime">86400</parameter>
<parameter key="profiler_listener.class">Symfony\Component\HttpKernel\Profiler\ProfilerListener</parameter>
@ -21,7 +21,6 @@
<service id="profiler.storage" class="%profiler.storage.class%">
<argument>%profiler.storage.file%</argument>
<argument>null</argument>
<argument>%profiler.storage.lifetime%</argument>
</service>

View File

@ -1,17 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://www.symfony-project.org/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="debug.toolbar.class">Symfony\Component\HttpKernel\Profiler\WebDebugToolbarListener</parameter>
</parameters>
<services>
<service id="debug.toolbar" class="%debug.toolbar.class%">
<tag name="kernel.listener" />
<argument type="service" id="profiler" />
</service>
</services>
</container>

View File

@ -31,10 +31,6 @@ class WebExtensionTest extends TestCase
$loader->configLoad(array('profiler' => true), $container);
$this->assertEquals('Symfony\\Bundle\\FrameworkBundle\\Profiler', $container->getParameter('profiler.class'), '->configLoad() loads the collectors.xml file if not already loaded');
$this->assertFalse($container->getParameterBag()->has('debug.toolbar.class'), '->configLoad() does not load the toolbar.xml file');
$loader->configLoad(array('toolbar' => true), $container);
$this->assertEquals('Symfony\\Component\\HttpKernel\\Profiler\\WebDebugToolbarListener', $container->getParameter('debug.toolbar.class'), '->configLoad() loads the collectors.xml file if the toolbar option is given');
}
public function testTemplatingLoad()

View File

@ -4,6 +4,7 @@ namespace Symfony\Bundle\ZendBundle\Logger;
use Zend\Log\Logger as BaseLogger;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
/*
* This file is part of the Symfony framework.
@ -21,6 +22,22 @@ use Symfony\Component\HttpKernel\Log\LoggerInterface;
*/
class Logger extends BaseLogger implements LoggerInterface
{
/**
* Returns a DebugLoggerInterface instance if one is registered with this logger.
*
* @return DebugLoggerInterface A DebugLoggerInterface instance or null if none is registered
*/
public function getDebugLogger()
{
foreach ($this->_writers as $writer) {
if ($writer instanceof DebugLoggerInterface) {
return $writer;
}
}
return null;
}
public function emerg($message)
{
return parent::log($message, 0);

View File

@ -1,6 +1,6 @@
<?php
namespace Symfony\Component\HttpKernel\Profiler\DataCollector;
namespace Symfony\Component\HttpKernel\DataCollector;
use Symfony\Component\HttpKernel\Profiler\Profiler;
@ -16,25 +16,21 @@ use Symfony\Component\HttpKernel\Profiler\Profiler;
/**
* DataCollector.
*
* Children of this class must store the collected data in the data property.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
abstract class DataCollector implements DataCollectorInterface
abstract class DataCollector implements DataCollectorInterface, \Serializable
{
protected $profiler;
protected $data;
public function getData()
public function serialize()
{
return $this->data;
return serialize($this->data);
}
public function setData($data)
public function unserialize($data)
{
$this->data = $data;
}
public function setProfiler(Profiler $profiler)
{
$this->profiler = $profiler;
$this->data = unserialize($data);
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace Symfony\Component\HttpKernel\DataCollector;
use Symfony\Component\HttpKernel\Profiler\Profiler;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* DataCollectorInterface.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
interface DataCollectorInterface
{
/**
* Collects data for the given Request and Response.
*
* @param Request $request A Request instance
* @param Response $response A Response instance
* @param \Exception $exception An Exception instance
*/
function collect(Request $request, Response $response, \Exception $exception = null);
/**
* Returns the name of the collector.
*
* @return string The collector name
*/
function getName();
}

View File

@ -0,0 +1,77 @@
<?php
namespace Symfony\Component\HttpKernel\DataCollector;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Framework\Debug\EventDispatcherTraceableInterface;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* EventDataCollector.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class EventDataCollector extends DataCollector
{
protected $dispatcher;
public function setEventDispatcher(EventDispatcher $dispatcher)
{
if ($dispatcher instanceof EventDispatcherTraceableInterface) {
$this->dispatcher = $dispatcher;
}
}
/**
* {@inheritdoc}
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
$this->data = array(
'called_events' => null !== $this->dispatcher ? $this->dispatcher->getCalledEvents() : array(),
'not_called_events' => null !== $this->dispatcher ? $this->dispatcher->getNotCalledEvents() : array(),
);
}
/**
* Gets the called events.
*
* @return array An array of called events
*
* @see EventDispatcherTraceableInterface
*/
public function getCalledEvents()
{
return $this->data['called_events'];
}
/**
* Gets the not called events.
*
* @return array An array of not called events
*
* @see EventDispatcherTraceableInterface
*/
public function getNotCalledEvents()
{
return $this->data['not_called_events'];
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'events';
}
}

View File

@ -0,0 +1,93 @@
<?php
namespace Symfony\Component\HttpKernel\DataCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* ExceptionDataCollector.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class ExceptionDataCollector extends DataCollector
{
/**
* {@inheritdoc}
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
if (null !== $exception) {
$this->data = array(
'exception' => $exception,
);
}
}
/**
* Checks if the exception is not null.
*
* @return Boolean true if the exception is not null, false otherwise
*/
public function hasException()
{
return isset($this->data['exception']);
}
/**
* Gets the exception.
*
* @return \Exception The exception
*/
public function getException()
{
return $this->data['exception'];
}
/**
* Gets the exception message.
*
* @return string The exception message
*/
public function getMessage()
{
return $this->data['exception']->getMessage();
}
/**
* Gets the exception code.
*
* @return integer The exception code
*/
public function getCode()
{
return $this->data['exception']->getCode();
}
/**
* Gets the exception trace.
*
* @return array The exception trace
*/
public function getTrace()
{
return $this->data['exception']->getTrace();
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'exception';
}
}

View File

@ -0,0 +1,76 @@
<?php
namespace Symfony\Component\HttpKernel\DataCollector;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* LogDataCollector.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class LoggerDataCollector extends DataCollector
{
protected $logger;
public function __construct($logger = null)
{
if (null !== $logger) {
$this->logger = $logger->getDebugLogger();
}
}
/**
* {@inheritdoc}
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
if (null !== $this->logger) {
$this->data = array(
'error_count' => $this->logger->countErrors(),
'logs' => $this->logger->getLogs(),
);
}
}
/**
* Gets the called events.
*
* @return array An array of called events
*
* @see EventDispatcherTraceableInterface
*/
public function countErrors()
{
return isset($this->data['error_count']) ? $this->data['error_count'] : 0;
}
/**
* Gets the logs.
*
* @return array An array of logs
*/
public function getLogs()
{
return isset($this->data['logs']) ? $this->data['logs'] : array();
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'logger';
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace Symfony\Component\HttpKernel\DataCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* MemoryDataCollector.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class MemoryDataCollector extends DataCollector
{
/**
* {@inheritdoc}
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
$this->data = array(
'memory' => memory_get_peak_usage(true),
);
}
/**
* Gets the memory.
*
* @return integer The memory
*/
public function getMemory()
{
return $this->data['memory'];
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'memory';
}
}

View File

@ -0,0 +1,102 @@
<?php
namespace Symfony\Component\HttpKernel\DataCollector;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\HeaderBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* RequestDataCollector.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class RequestDataCollector extends DataCollector
{
/**
* {@inheritdoc}
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
$this->data = array(
'format' => $request->getRequestFormat(),
'content_type' => $response->headers->get('Content-Type') ? $response->headers->get('Content-Type') : 'text/html',
'status_code' => $response->getStatusCode(),
'request_query' => $request->query->all(),
'request_request' => $request->request->all(),
'request_headers' => $request->headers->all(),
'request_server' => $request->server->all(),
'request_cookies' => $request->cookies->all(),
'response_headers' => $response->headers->all(),
'session_attributes' => $request->getSession()->getAttributes(),
);
}
public function getRequestRequest()
{
return new ParameterBag($this->data['request_request']);
}
public function getRequestQuery()
{
return new ParameterBag($this->data['request_query']);
}
public function getRequestHeaders()
{
return new HeaderBag($this->data['request_headers']);
}
public function getRequestServer()
{
return new ParameterBag($this->data['request_server']);
}
public function getRequestCookies()
{
return new ParameterBag($this->data['request_cookies']);
}
public function getResponseHeaders()
{
return new HeaderBag($this->data['response_headers']);
}
public function getSessionAttributes()
{
return new HeaderBag($this->data['session_attributes']);
}
public function getContentType()
{
return $this->data['content_type'];
}
public function getStatusCode()
{
return $this->data['status_code'];
}
public function getFormat()
{
return $this->data['format'];
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'request';
}
}

View File

@ -1,34 +0,0 @@
<?php
namespace Symfony\Component\HttpKernel\Profiler\DataCollector;
use Symfony\Component\HttpKernel\Profiler\Profiler;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* DataCollectorInterface.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
interface DataCollectorInterface
{
function getData();
function setData($data);
function getName();
function collect();
function setProfiler(Profiler $profiler);
function getSummary();
}

View File

@ -1,44 +0,0 @@
<?php
namespace Symfony\Component\HttpKernel\Profiler\DataCollector;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* MemoryDataCollector.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class MemoryDataCollector extends DataCollector
{
public function collect()
{
$this->data = array(
'memory' => memory_get_peak_usage(true),
);
}
public function getMemory()
{
return $this->data['memory'];
}
public function getSummary()
{
return sprintf('<img style="margin-left: 10px; vertical-align: middle" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAAmElEQVR42sSTuwoEIQxFr7JtOgf0n+z9Rhsr/0nB0jKiW00xs7Myj4VNFQj3cAKJGGPgSUk8rNfaeO8vqTjnxAYAAMuynAqXUj4NAKDWen8FKSWstadCIYRjg9YaUkrTsDHm2GAFtNamgP18A2BmKKWmAGaeG+ScpwCt9XdA7x299ylgP//dJQIAEYGI7gNijJcNxN+/8T0A1+E5NmcLfJkAAAAASUVORK5CYII=" />
%.0f KB
', $this->data['memory'] / 1024);
}
public function getName()
{
return 'memory';
}
}

View File

@ -2,9 +2,10 @@
namespace Symfony\Component\HttpKernel\Profiler;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Profiler\ProfilerStorage;
use Symfony\Component\HttpKernel\Profiler\DataCollector\DataCollectorInterface;
use Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface;
use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
/*
@ -21,134 +22,195 @@ use Symfony\Component\HttpKernel\Log\LoggerInterface;
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Profiler implements \ArrayAccess
class Profiler
{
protected $profilerStorage;
protected $storage;
protected $collectors;
protected $response;
protected $logger;
protected $enabled;
protected $token;
protected $data;
protected $ip;
protected $url;
protected $time;
protected $empty;
public function __construct(ProfilerStorage $profilerStorage, LoggerInterface $logger = null)
/**
* Constructor.
*
* @param ProfilerStorageInterface $storage A ProfilerStorageInterface instance
* @param LoggerInterface $logger A LoggerInterface instance
*/
public function __construct(ProfilerStorageInterface $storage, LoggerInterface $logger = null)
{
$this->profilerStorage = $profilerStorage;
$this->storage = $storage;
$this->logger = $logger;
$this->collectors = array();
$this->enabled = true;
$this->empty = true;
}
/**
* Clones the Profiler instance.
* Disables the profiler.
*/
public function __clone()
{
$this->profilerStorage = clone $this->profilerStorage;
}
/**
* Returns a new Profiler for the given Response.
*
* @param Response $response A Response instance
*
* @return Profiler A new Profiler instance
*/
public function load(Response $response)
{
if (!$token = $response->headers->get('X-Debug-Token')) {
return null;
}
return $this->getProfilerForToken($token);
}
/**
* Returns a new Profiler for the given token.
*
* @param string $token A token
*
* @return Profiler A new Profiler instance
*/
public function getProfilerForToken($token)
{
$profiler = clone $this;
$profiler->profilerStorage->setToken($token);
$profiler->loadCollectorData();
return $profiler;
}
public function disable()
{
$this->enabled = false;
}
/**
* Collects data for the given Response.
* Loads a Profiler for the given Response.
*
* @param Response $response A Response instance
*
* @return Profiler A new Profiler instance
*/
public function collect(Response $response)
public function loadFromResponse(Response $response)
{
if (!$token = $response->headers->get('X-Debug-Token')) {
return null;
}
return $this->loadFromToken($token);
}
/**
* Loads a Profiler for the given token.
*
* @param string $token A token
*
* @return Profiler A new Profiler instance
*/
public function loadFromToken($token)
{
$profiler = new self($this->storage, $this->logger);
$profiler->setToken($token);
return $profiler;
}
/**
* Sets the token.
*
* @param string $token The token
*/
public function setToken($token)
{
$this->token = $token;
if (false !== $items = $this->storage->read($token)) {
list($collectors, $this->ip, $this->url, $this->time) = $items;
$this->setCollectors($collectors);
$this->empty = false;
} else {
$this->empty = true;
}
}
/**
* Gets the token.
*
* @return string The token
*/
public function getToken()
{
if (null === $this->token) {
$this->token = uniqid();
}
return $this->token;
}
/**
* Checks if the profiler is empty.
*
* @return Boolean Whether the profiler is empty or not
*/
public function isEmpty()
{
return $this->empty;
}
/**
* Returns the IP.
*
* @return string The IP
*/
public function getIp()
{
return $this->ip;
}
/**
* Returns the URL.
*
* @return string The URL
*/
public function getUrl()
{
return $this->url;
}
/**
* Returns the time.
*
* @return string The time
*/
public function getTime()
{
return $this->time;
}
/**
* Finds profiler tokens for the given criteria.
*
* @param string $ip The IP
* @param string $url The URL
* @param string $limit The maximum number of tokens to return
*
* @return array An array of tokens
*/
public function find($ip, $url, $limit)
{
return $this->storage->find($ip, $url, $limit);
}
/**
* Collects data for the given Response.
*
* @param Request $request A Request instance
* @param Response $response A Response instance
* @param \Exception $exception An exception instance if the request threw one
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
if (false === $this->enabled) {
return;
}
$this->response = $response;
$this->response->headers->set('X-Debug-Token', $this->profilerStorage->getToken());
$response = $response;
$response->headers->set('X-Debug-Token', $this->getToken());
$data = array();
foreach ($this->collectors as $name => $collector) {
$collector->collect();
$data[$name] = $collector->getData();
foreach ($this->collectors as $collector) {
$collector->collect($request, $response, $exception);
}
$this->ip = $request->server->get('REMOTE_ADDR');
$this->url = $request->getUri();
$this->time = time();
try {
$this->profilerStorage->write($data);
$this->profilerStorage->purge();
$this->storage->write($this->token, $this->collectors, $this->ip, $this->url, $this->time);
$this->empty = false;
} catch (\Exception $e) {
if (null !== $this->logger) {
$this->logger->err('Unable to store the profiler information.');
$this->logger->err(sprintf('Unable to store the profiler information (%s).', $e->getMessage()));
}
}
}
/**
* Loads the data stored in the storage for all collectors.
*/
public function loadCollectorData()
{
try {
foreach ($this->collectors as $name => $collector) {
$collector->setData($this->profilerStorage->getData($name));
}
} catch (\Exception $e) {
if (null !== $this->logger) {
$this->logger->err('Unable to read the profiler information.');
}
}
}
/**
* Gets the profiler storage.
*
* @return ProfilerStorage A ProfilerStorage instance
*/
public function getProfilerStorage()
{
return $this->profilerStorage;
}
/**
* Gets the Response.
*
* @return Response A Response instance
*/
public function getResponse()
{
return $this->response;
}
/**
* Gets the Collectors associated with this profiler.
*
@ -209,53 +271,4 @@ class Profiler implements \ArrayAccess
return $this->collectors[$name];
}
/**
* Returns true if the named collector exists.
*
* @param string $name The collector name
*
* @return Boolean true if the collector exists, false otherwise
*/
public function offsetExists($name)
{
return $this->hasCollector($name);
}
/**
* Gets a collector.
*
* @param string $name The collector name
*
* @throws \InvalidArgumentException if the collector does not exist
*/
public function offsetGet($name)
{
return $this->getCollector($name);
}
/**
* Unimplemented.
*
* @param string $name The collector name
* @param string|array $value The collector
*
* @throws \LogicException
*/
public function offsetSet($name, $value)
{
throw new \LogicException('A Collector cannot be set.');
}
/**
* Unimplemented.
*
* @param string $name The collector name
*
* @throws \LogicException
*/
public function offsetUnset($name)
{
throw new \LogicException('A Collector cannot be removed.');
}
}

View File

@ -24,30 +24,61 @@ use Symfony\Component\HttpKernel\HttpKernelInterface;
class ProfilerListener
{
protected $profiler;
protected $exception;
/**
* Constructor.
*
* @param Profiler $profiler A Profiler instance
*/
public function __construct(Profiler $profiler)
{
$this->profiler = $profiler;
}
/**
* Registers a core.response listener.
* Registers a core.response and core.exception listeners.
*
* @param EventDispatcher $dispatcher An EventDispatcher instance
* @param integer $priority The priority
*/
public function register(EventDispatcher $dispatcher, $priority = 0)
{
$dispatcher->connect('core.response', array($this, 'handle'), $priority);
$dispatcher->connect('core.exception', array($this, 'handleException'), $priority);
$dispatcher->connect('core.response', array($this, 'handleResponse'), $priority);
}
public function handle(Event $event, Response $response)
/**
* Handles the core.exception event.
*
* @param Event $event An Event instance
*/
public function handleException(Event $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getParameter('request_type')) {
return false;
}
$this->exception = $event->getParameter('exception');
return false;
}
/**
* Handles the core.response event.
*
* @param Event $event An Event instance
*
* @return Response $response A Response instance
*/
public function handleResponse(Event $event, Response $response)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getParameter('request_type')) {
return $response;
}
$this->profiler->collect($response);
$this->profiler->collect($event->getParameter('request'), $response, $this->exception);
$this->exception = null;
return $response;
}

View File

@ -0,0 +1,55 @@
<?php
namespace Symfony\Component\HttpKernel\Profiler;
use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* ProfilerStorageInterface.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
interface ProfilerStorageInterface
{
/**
* Finds profiler tokens for the given criteria.
*
* @param string $ip The IP
* @param string $url The URL
* @param string $limit The maximum number of tokens to return
*
* @return array An array of tokens
*/
function find($ip, $url, $limit);
/**
* Reads data associated with the given token.
*
* The method returns false if the token does not exists in the storage.
*
* @param string $token A token
*
* @return DataCollectorInterface[] An array of DataCollectorInterface instance
*/
function read($token);
/**
* Reads data associated with the given token.
*
* @param string $token A token
* @param DataCollectorInterface[] $collectors An array of DataCollectorInterface instances
* @param string $ip An IP
* @param string $url An URL
* @param integer $time The time of the data
*/
function write($token, $collectors, $ip, $url, $time);
}

View File

@ -2,6 +2,8 @@
namespace Symfony\Component\HttpKernel\Profiler;
use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface;
/*
* This file is part of the Symfony framework.
*
@ -12,77 +14,99 @@ namespace Symfony\Component\HttpKernel\Profiler;
*/
/**
* ProfilerStorage.
* SQLiteProfilerStorage stores profiling information in a SQLite database.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class ProfilerStorage
class SQLiteProfilerStorage implements ProfilerStorageInterface
{
protected $token;
protected $data;
protected $store;
protected $lifetime;
public function __construct($store, $token = null, $lifetime = 86400)
/**
* Constructor.
*
* @param string $store The path to the SQLite DB
* @param integer $lifetime The lifetime to use for the purge
*/
public function __construct($store, $lifetime = 86400)
{
$this->store = $store;
$this->token = null === $token ? uniqid() : $token;
$this->data = null;
$this->lifetime = (int) $lifetime;
}
public function hasData()
/**
* {@inheritdoc}
*/
public function find($ip, $url, $limit)
{
return null !== $this->data;
}
$criteria = array();
public function getData($name = null)
{
if (null === $this->data) {
$this->data = $this->read();
if ($ip = preg_replace('/[^\d\.]/', '', $ip)) {
$criteria[] = ' ip LIKE "%'.$ip.'%"';
}
if (null === $name) {
return $this->data;
if ($url) {
$criteria[] = ' url LIKE "%'.$url.'%"';
}
return isset($this->data[$name]) ? $this->data[$name] : array();
$criteria = $criteria ? 'WHERE '.implode(' AND ', $criteria) : '';
$db = $this->initDb();
$tokens = $this->fetch($db, 'SELECT token, ip, url, time FROM data '.$criteria.' ORDER BY time DESC LIMIT '.((integer) $limit));
$this->close($db);
return $tokens;
}
public function setToken($token)
{
$this->token = $token;
$this->data = null;
}
public function getToken()
{
return $this->token;
}
protected function read()
/**
* {@inheritdoc}
*/
public function read($token)
{
$db = $this->initDb();
$args = array(':token' => $this->token);
$data = $this->fetch($db, 'SELECT data FROM data WHERE token = :token ORDER BY created_at DESC LIMIT 1', $args);
$args = array(':token' => $token);
$data = $this->fetch($db, 'SELECT data, ip, url, time FROM data WHERE token = :token ORDER BY time DESC LIMIT 1', $args);
$this->close($db);
if (isset($data[0]['data'])) {
return unserialize(pack('H*', $data[0]['data']));
return array(unserialize(pack('H*', $data[0]['data'])), $data[0]['ip'], $data[0]['url'], $data[0]['time']);
} else {
return false;
}
}
public function write($data)
/**
* {@inheritdoc}
*/
public function write($token, $collectors, $ip, $url, $time)
{
$unpack = unpack('H*', serialize($data));
$unpack = unpack('H*', serialize($collectors));
$data = $unpack[1];
$db = $this->initDb();
$args = array(
':token' => $this->token,
':data' => (string) $data,
':time' => time()
':token' => $token,
':data' => $data,
':ip' => $ip,
':url' => $url,
':time' => $time,
);
$this->exec($db, 'INSERT INTO data (token, data, created_at) VALUES (:token, :data, :time)', $args);
$this->exec($db, 'INSERT INTO data (token, data, ip, url, time) VALUES (:token, :data, :ip, :url, :time)', $args);
$this->purge();
$this->close($db);
}
public function purge($all = false)
{
$db = $this->initDb();
if (true === $all) {
$this->exec($db, 'DELETE FROM data');
} else {
$args = array(':time' => time() - $this->lifetime);
$this->exec($db, 'DELETE FROM data WHERE time < :time', $args);
}
$this->close($db);
}
@ -99,8 +123,8 @@ class ProfilerStorage
throw new \RuntimeException('You need to enable either the SQLite or PDO_SQLite extension for the profiler to run properly.');
}
$db->exec('CREATE TABLE IF NOT EXISTS data (token STRING, data STRING, created_at INTEGER)');
$db->exec('CREATE INDEX IF NOT EXISTS data_data ON data (created_at)');
$db->exec('CREATE TABLE IF NOT EXISTS data (token STRING, data STRING, ip STRING, url STRING, time INTEGER)');
$db->exec('CREATE INDEX IF NOT EXISTS data_data ON data (time)');
return $db;
}
@ -155,12 +179,4 @@ class ProfilerStorage
$db->close();
}
}
public function purge()
{
$db = $this->initDb();
$args = array(':time' => time() - $this->lifetime);
$this->exec($db, 'DELETE FROM data WHERE created_at < :time', $args);
$this->close($db);
}
}

View File

@ -1,102 +0,0 @@
<?php
namespace Symfony\Component\HttpKernel\Profiler;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* WebDebugToolbarListener injects the Web Debug Toolbar.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class WebDebugToolbarListener
{
protected $profiler;
public function __construct(Profiler $profiler)
{
$this->profiler = $profiler;
}
/**
* Registers a core.response listener.
*
* @param EventDispatcher $dispatcher An EventDispatcher instance
* @param integer $priority The priority
*/
public function register(EventDispatcher $dispatcher, $priority = 0)
{
$dispatcher->connect('core.response', array($this, 'handle'), $priority);
}
public function handle(Event $event, Response $response)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getParameter('request_type')) {
return $response;
}
$request = $event->getParameter('request');
if (!$response->headers->has('X-Debug-Token')
|| '3' === substr($response->getStatusCode(), 0, 1)
|| ($response->headers->has('Content-Type') && false === strpos($response->headers->get('Content-Type'), 'html'))
|| 'html' !== $request->getRequestFormat()
|| $request->isXmlHttpRequest()
) {
return $response;
}
$response->setContent($this->injectToolbar($request, $response));
return $response;
}
/**
* Injects the web debug toolbar into a given HTML string.
*
* @param string $content The HTML content
*
* @return Response A Response instance
*/
protected function injectToolbar(Request $request, Response $response)
{
$data = '';
foreach ($this->profiler->getCollectors() as $name => $collector) {
$data .= $collector->getSummary();
}
$position = false === strpos($request->headers->get('user-agent'), 'Mobile') ? 'fixed' : 'absolute';
$toolbar = <<<EOF
<!-- START of Symfony 2 Web Debug Toolbar -->
<div style="clear: both; height: 40px;"></div>
<div style="position: $position; bottom: 0px; left:0; z-index: 6000000; width: 100%; background: #dde4eb; border-top: 1px solid #bbb; padding: 5px; margin: 0; font: 11px Verdana, Arial, sans-serif; color: #222;">
$data
</div>
<!-- END of Symfony 2 Web Debug Toolbar -->
EOF;
$toolbar = "\n".str_replace("\n", '', $toolbar)."\n";
$count = 0;
$content = str_ireplace('</body>', $toolbar.'</body>', $response->getContent(), $count);
if (!$count) {
$content .= $toolbar;
}
return $content;
}
}

View File

@ -75,7 +75,7 @@ class Client extends BaseClient
return false;
}
return $this->container->getProfilerService()->load($this->response);
return $this->container->getProfilerService()->loadFromResponse($this->response);
}
/**

View File

@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Component\HttpKernel\Profiler;
use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector;
use Symfony\Component\HttpKernel\Profiler\SQLiteProfilerStorage;
use Symfony\Component\HttpKernel\Profiler\Profiler;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class ProfilerTest extends \PHPUnit_Framework_TestCase
{
public function testCollect()
{
$request = new Request();
$request->query->set('foo', 'bar');
$response = new Response();
$collector = new RequestDataCollector();
$storage = new SQLiteProfilerStorage(sys_get_temp_dir().'/sf2_profiler.db');
$storage->purge(true);
$profiler = new Profiler($storage);
$profiler->addCollector($collector);
$profiler->setToken('foobar');
$profiler->collect($request, $response);
$profiler = new Profiler($storage);
$profiler->setToken('foobar');
$this->assertEquals(array('foo' => 'bar'), $profiler->getCollector('request')->getRequestQuery()->all());
}
}