diff --git a/src/Symfony/Bundle/DoctrineBundle/DataCollector/DoctrineDataCollector.php b/src/Symfony/Bundle/DoctrineBundle/DataCollector/DoctrineDataCollector.php index f7caa762845d..8ff36ff17aed 100644 --- a/src/Symfony/Bundle/DoctrineBundle/DataCollector/DoctrineDataCollector.php +++ b/src/Symfony/Bundle/DoctrineBundle/DataCollector/DoctrineDataCollector.php @@ -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 @@ */ 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 @@ public function getQueries() return $this->data['queries']; } - public function getSummary() - { - $queries = count($this->data['queries']); - $queriesColor = $queries < 10 ? '#2d2' : '#d22'; - - return sprintf(' - %d - ', $queriesColor, $queries); - } - + /** + * {@inheritdoc} + */ public function getName() { return 'db'; diff --git a/src/Symfony/Bundle/DoctrineBundle/Resources/config/dbal.xml b/src/Symfony/Bundle/DoctrineBundle/Resources/config/dbal.xml index b326e2456e82..fd7cbe7ba0bf 100644 --- a/src/Symfony/Bundle/DoctrineBundle/Resources/config/dbal.xml +++ b/src/Symfony/Bundle/DoctrineBundle/Resources/config/dbal.xml @@ -21,7 +21,7 @@ - + diff --git a/src/Symfony/Bundle/DoctrineMongoDBBundle/DataCollector/DoctrineMongoDBDataCollector.php b/src/Symfony/Bundle/DoctrineMongoDBBundle/DataCollector/DoctrineMongoDBDataCollector.php index 620efb0ea99e..0574ccf2cd26 100644 --- a/src/Symfony/Bundle/DoctrineMongoDBBundle/DataCollector/DoctrineMongoDBDataCollector.php +++ b/src/Symfony/Bundle/DoctrineMongoDBBundle/DataCollector/DoctrineMongoDBDataCollector.php @@ -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 @@ public function __construct(DoctrineMongoDBLogger $logger) $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(' - %d - ', $color, $this->data['nb_queries']); + return $this->data['nb_queries']; } + /** + * {@inheritdoc} + */ public function getName() { return 'mongodb'; diff --git a/src/Symfony/Bundle/FrameworkBundle/DataCollector/AppDataCollector.php b/src/Symfony/Bundle/FrameworkBundle/DataCollector/AppDataCollector.php deleted file mode 100644 index fdc853d2e9e9..000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/DataCollector/AppDataCollector.php +++ /dev/null @@ -1,64 +0,0 @@ - - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -/** - * AppDataCollector. - * - * @author Fabien Potencier - */ -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') : 'NONE', - '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(' - %s/%s/%s/%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'; - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/DataCollector/ConfigDataCollector.php b/src/Symfony/Bundle/FrameworkBundle/DataCollector/ConfigDataCollector.php index e2ea32c838b2..eb1d90ecf3fe 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DataCollector/ConfigDataCollector.php +++ b/src/Symfony/Bundle/FrameworkBundle/DataCollector/ConfigDataCollector.php @@ -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 @@ */ 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 @@ public function collect() ); } - public function getSummary() + /** + * Gets the URL. + * + * @return string The URL + */ + public function getUrl() + { + 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 sprintf('Symfony - %s - PHP - %s/xdebug/accel - - %s/%s/%s/%s - ', $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']); + return $this->data['accel']; } + /** + * {@inheritdoc} + */ public function getName() { return 'config'; diff --git a/src/Symfony/Bundle/FrameworkBundle/DataCollector/RequestDataCollector.php b/src/Symfony/Bundle/FrameworkBundle/DataCollector/RequestDataCollector.php new file mode 100644 index 000000000000..160b79e82310 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/DataCollector/RequestDataCollector.php @@ -0,0 +1,44 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * RequestDataCollector. + * + * @author Fabien Potencier + */ +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']; + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/DataCollector/TimerDataCollector.php b/src/Symfony/Bundle/FrameworkBundle/DataCollector/TimerDataCollector.php index f418a4569b80..96be2608890d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DataCollector/TimerDataCollector.php +++ b/src/Symfony/Bundle/FrameworkBundle/DataCollector/TimerDataCollector.php @@ -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 @@ */ 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(' - %.0f ms - ', $this->data['time'] * 1000); - } - + /** + * {@inheritdoc} + */ public function getName() { return 'timer'; diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/WebExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/WebExtension.php index 7cbb089d8a94..d2da5e87ee16 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/WebExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/WebExtension.php @@ -74,10 +74,6 @@ public function configLoad($config, ContainerBuilder $container) )); } - if (isset($config['toolbar']) && $config['toolbar']) { - $config['profiler'] = true; - } - if (isset($config['profiler'])) { if ($config['profiler']) { if (!$container->hasDefinition('profiler')) { @@ -90,18 +86,6 @@ public function configLoad($config, ContainerBuilder $container) } } - // 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); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Profiler.php b/src/Symfony/Bundle/FrameworkBundle/Profiler.php index e6cbe7e48230..2d9f564d6a28 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Profiler.php +++ b/src/Symfony/Bundle/FrameworkBundle/Profiler.php @@ -4,7 +4,7 @@ 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 @@ */ class Profiler extends BaseProfiler { - protected $container; - - public function __construct(ContainerInterface $container, ProfilerStorage $profilerStorage, LoggerInterface $logger = null) - { - parent::__construct($profilerStorage, $logger); - - $this->container = $container; - $this->initCollectors(); - $this->loadCollectorData(); - } - - protected function initCollectors() + public function __construct(ContainerInterface $container, ProfilerStorageInterface $storage, LoggerInterface $logger = null) { - $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); + parent::__construct($storage, $logger); - 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)); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml index 8772a76254c3..dddfd1ff10ec 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml @@ -6,29 +6,48 @@ Symfony\Bundle\FrameworkBundle\DataCollector\ConfigDataCollector - Symfony\Bundle\FrameworkBundle\DataCollector\AppDataCollector + Symfony\Bundle\FrameworkBundle\DataCollector\RequestDataCollector Symfony\Bundle\FrameworkBundle\DataCollector\TimerDataCollector - Symfony\Component\HttpKernel\Profiler\DataCollector\MemoryDataCollector + Symfony\Component\HttpKernel\DataCollector\MemoryDataCollector + Symfony\Component\HttpKernel\DataCollector\LoggerDataCollector + Symfony\Component\HttpKernel\DataCollector\ExceptionDataCollector + Symfony\Component\HttpKernel\DataCollector\EventDataCollector - - + + + - - - + + - - + + - + + + + + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml index 04d193e5edfd..c6bd63b3907f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml @@ -6,7 +6,7 @@ Symfony\Bundle\FrameworkBundle\Profiler - Symfony\Component\HttpKernel\Profiler\ProfilerStorage + Symfony\Component\HttpKernel\Profiler\SQLiteProfilerStorage %kernel.cache_dir%/profiler.db 86400 Symfony\Component\HttpKernel\Profiler\ProfilerListener @@ -21,7 +21,6 @@ %profiler.storage.file% - null %profiler.storage.lifetime% diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/toolbar.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/toolbar.xml deleted file mode 100644 index 29027edeff7a..000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/toolbar.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - Symfony\Component\HttpKernel\Profiler\WebDebugToolbarListener - - - - - - - - - diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/WebExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/WebExtensionTest.php index ea6d2d18745a..8e283cb7b895 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/WebExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/WebExtensionTest.php @@ -31,10 +31,6 @@ public function testConfigLoad() $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() diff --git a/src/Symfony/Bundle/ZendBundle/Logger/Logger.php b/src/Symfony/Bundle/ZendBundle/Logger/Logger.php index f842addb1da0..e6ac3242f7a1 100644 --- a/src/Symfony/Bundle/ZendBundle/Logger/Logger.php +++ b/src/Symfony/Bundle/ZendBundle/Logger/Logger.php @@ -4,6 +4,7 @@ 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 @@ */ 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); diff --git a/src/Symfony/Component/HttpKernel/Profiler/DataCollector/DataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php similarity index 54% rename from src/Symfony/Component/HttpKernel/Profiler/DataCollector/DataCollector.php rename to src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php index 808c07862a56..b80533fb1ffb 100644 --- a/src/Symfony/Component/HttpKernel/Profiler/DataCollector/DataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php @@ -1,6 +1,6 @@ */ -abstract class DataCollector implements DataCollectorInterface +abstract class DataCollector implements DataCollectorInterface, \Serializable { - protected $profiler; protected $data; - public function getData() - { - return $this->data; - } - - public function setData($data) + public function serialize() { - $this->data = $data; + return serialize($this->data); } - public function setProfiler(Profiler $profiler) + public function unserialize($data) { - $this->profiler = $profiler; + $this->data = unserialize($data); } } diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php b/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php new file mode 100644 index 000000000000..1888067dd557 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php @@ -0,0 +1,40 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * DataCollectorInterface. + * + * @author Fabien Potencier + */ +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(); +} diff --git a/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php new file mode 100644 index 000000000000..978131c94d7d --- /dev/null +++ b/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php @@ -0,0 +1,77 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * EventDataCollector. + * + * @author Fabien Potencier + */ +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'; + } +} diff --git a/src/Symfony/Component/HttpKernel/DataCollector/ExceptionDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/ExceptionDataCollector.php new file mode 100644 index 000000000000..446d7c66793a --- /dev/null +++ b/src/Symfony/Component/HttpKernel/DataCollector/ExceptionDataCollector.php @@ -0,0 +1,93 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * ExceptionDataCollector. + * + * @author Fabien Potencier + */ +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'; + } +} diff --git a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php new file mode 100644 index 000000000000..aed0b356d578 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php @@ -0,0 +1,76 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * LogDataCollector. + * + * @author Fabien Potencier + */ +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'; + } +} diff --git a/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php new file mode 100644 index 000000000000..e8bfa81a051a --- /dev/null +++ b/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php @@ -0,0 +1,51 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * MemoryDataCollector. + * + * @author Fabien Potencier + */ +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'; + } +} diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php new file mode 100644 index 000000000000..2d93970e4da4 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php @@ -0,0 +1,102 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * RequestDataCollector. + * + * @author Fabien Potencier + */ +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'; + } +} diff --git a/src/Symfony/Component/HttpKernel/Profiler/DataCollector/DataCollectorInterface.php b/src/Symfony/Component/HttpKernel/Profiler/DataCollector/DataCollectorInterface.php deleted file mode 100644 index 8a0b91785f10..000000000000 --- a/src/Symfony/Component/HttpKernel/Profiler/DataCollector/DataCollectorInterface.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -/** - * DataCollectorInterface. - * - * @author Fabien Potencier - */ -interface DataCollectorInterface -{ - function getData(); - - function setData($data); - - function getName(); - - function collect(); - - function setProfiler(Profiler $profiler); - - function getSummary(); -} diff --git a/src/Symfony/Component/HttpKernel/Profiler/DataCollector/MemoryDataCollector.php b/src/Symfony/Component/HttpKernel/Profiler/DataCollector/MemoryDataCollector.php deleted file mode 100644 index b11ccf02261a..000000000000 --- a/src/Symfony/Component/HttpKernel/Profiler/DataCollector/MemoryDataCollector.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -/** - * MemoryDataCollector. - * - * @author Fabien Potencier - */ -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(' - %.0f KB - ', $this->data['memory'] / 1024); - } - - public function getName() - { - return 'memory'; - } -} diff --git a/src/Symfony/Component/HttpKernel/Profiler/Profiler.php b/src/Symfony/Component/HttpKernel/Profiler/Profiler.php index 9e4f98e52dfb..35b950af6f7e 100644 --- a/src/Symfony/Component/HttpKernel/Profiler/Profiler.php +++ b/src/Symfony/Component/HttpKernel/Profiler/Profiler.php @@ -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,132 +22,193 @@ * * @author Fabien Potencier */ -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() + public function disable() { - $this->profilerStorage = clone $this->profilerStorage; + $this->enabled = false; } /** - * Returns a new Profiler 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 load(Response $response) + public function loadFromResponse(Response $response) { if (!$token = $response->headers->get('X-Debug-Token')) { return null; } - return $this->getProfilerForToken($token); + return $this->loadFromToken($token); } /** - * Returns a new Profiler for the given token. + * Loads a Profiler for the given token. * * @param string $token A token * * @return Profiler A new Profiler instance */ - public function getProfilerForToken($token) + public function loadFromToken($token) { - $profiler = clone $this; - $profiler->profilerStorage->setToken($token); - $profiler->loadCollectorData(); + $profiler = new self($this->storage, $this->logger); + $profiler->setToken($token); return $profiler; } - public function disable() + /** + * Sets the token. + * + * @param string $token The token + */ + public function setToken($token) { - $this->enabled = false; + $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; + } } /** - * Collects data for the given Response. + * Gets the token. * - * @param Response $response A Response instance + * @return string The token */ - public function collect(Response $response) + public function getToken() { - if (false === $this->enabled) { - return; + if (null === $this->token) { + $this->token = uniqid(); } - $this->response = $response; - $this->response->headers->set('X-Debug-Token', $this->profilerStorage->getToken()); + return $this->token; + } - $data = array(); - foreach ($this->collectors as $name => $collector) { - $collector->collect(); + /** + * Checks if the profiler is empty. + * + * @return Boolean Whether the profiler is empty or not + */ + public function isEmpty() + { + return $this->empty; + } - $data[$name] = $collector->getData(); - } + /** + * Returns the IP. + * + * @return string The IP + */ + public function getIp() + { + return $this->ip; + } - try { - $this->profilerStorage->write($data); - $this->profilerStorage->purge(); - } catch (\Exception $e) { - if (null !== $this->logger) { - $this->logger->err('Unable to store the profiler information.'); - } - } + /** + * Returns the URL. + * + * @return string The URL + */ + public function getUrl() + { + return $this->url; } /** - * Loads the data stored in the storage for all collectors. + * Returns the time. + * + * @return string The time */ - public function loadCollectorData() + public function getTime() { - 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.'); - } - } + return $this->time; } /** - * Gets the profiler storage. + * Finds profiler tokens for the given criteria. * - * @return ProfilerStorage A ProfilerStorage instance + * @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 getProfilerStorage() + public function find($ip, $url, $limit) { - return $this->profilerStorage; + return $this->storage->find($ip, $url, $limit); } /** - * Gets the Response. + * Collects data for the given Response. * - * @return Response A Response instance + * @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 getResponse() + public function collect(Request $request, Response $response, \Exception $exception = null) { - return $this->response; + if (false === $this->enabled) { + return; + } + + $response = $response; + $response->headers->set('X-Debug-Token', $this->getToken()); + + 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->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(sprintf('Unable to store the profiler information (%s).', $e->getMessage())); + } + } } /** @@ -209,53 +271,4 @@ public function getCollector($name) 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.'); - } } diff --git a/src/Symfony/Component/HttpKernel/Profiler/ProfilerListener.php b/src/Symfony/Component/HttpKernel/Profiler/ProfilerListener.php index 850d0563ca6e..6f61e215ee45 100644 --- a/src/Symfony/Component/HttpKernel/Profiler/ProfilerListener.php +++ b/src/Symfony/Component/HttpKernel/Profiler/ProfilerListener.php @@ -24,30 +24,61 @@ 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; } diff --git a/src/Symfony/Component/HttpKernel/Profiler/ProfilerStorageInterface.php b/src/Symfony/Component/HttpKernel/Profiler/ProfilerStorageInterface.php new file mode 100644 index 000000000000..ea08094297e0 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Profiler/ProfilerStorageInterface.php @@ -0,0 +1,55 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * ProfilerStorageInterface. + * + * @author Fabien Potencier + */ +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); +} diff --git a/src/Symfony/Component/HttpKernel/Profiler/ProfilerStorage.php b/src/Symfony/Component/HttpKernel/Profiler/SQLiteProfilerStorage.php similarity index 58% rename from src/Symfony/Component/HttpKernel/Profiler/ProfilerStorage.php rename to src/Symfony/Component/HttpKernel/Profiler/SQLiteProfilerStorage.php index 943f254cf9ae..e9871fe384d6 100644 --- a/src/Symfony/Component/HttpKernel/Profiler/ProfilerStorage.php +++ b/src/Symfony/Component/HttpKernel/Profiler/SQLiteProfilerStorage.php @@ -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 @@ */ /** - * ProfilerStorage. + * SQLiteProfilerStorage stores profiling information in a SQLite database. * * @author Fabien Potencier */ -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) : ''; - public function setToken($token) - { - $this->token = $token; - $this->data = null; - } + $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); - public function getToken() - { - return $this->token; + return $tokens; } - 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 @@ protected function initDb() 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 @@ protected function close($db) $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); - } } diff --git a/src/Symfony/Component/HttpKernel/Profiler/WebDebugToolbarListener.php b/src/Symfony/Component/HttpKernel/Profiler/WebDebugToolbarListener.php deleted file mode 100644 index a94e00873535..000000000000 --- a/src/Symfony/Component/HttpKernel/Profiler/WebDebugToolbarListener.php +++ /dev/null @@ -1,102 +0,0 @@ - - * - * 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 - */ -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 = << -
-
- $data -
- - -EOF; - - $toolbar = "\n".str_replace("\n", '', $toolbar)."\n"; - $count = 0; - $content = str_ireplace('', $toolbar.'', $response->getContent(), $count); - if (!$count) { - $content .= $toolbar; - } - - return $content; - } -} diff --git a/src/Symfony/Framework/Client.php b/src/Symfony/Framework/Client.php index 6769dedfec6f..0f8d9072dee4 100644 --- a/src/Symfony/Framework/Client.php +++ b/src/Symfony/Framework/Client.php @@ -75,7 +75,7 @@ public function getProfiler() return false; } - return $this->container->getProfilerService()->load($this->response); + return $this->container->getProfilerService()->loadFromResponse($this->response); } /** diff --git a/tests/Symfony/Tests/Component/HttpKernel/Profiler/ProfilerTest.php b/tests/Symfony/Tests/Component/HttpKernel/Profiler/ProfilerTest.php new file mode 100644 index 000000000000..6980d5cbcae1 --- /dev/null +++ b/tests/Symfony/Tests/Component/HttpKernel/Profiler/ProfilerTest.php @@ -0,0 +1,41 @@ + + * + * 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()); + } +}