diff --git a/src/Symfony/Framework/DoctrineBundle/DataCollector/DoctrineDataCollector.php b/src/Symfony/Framework/DoctrineBundle/DataCollector/DoctrineDataCollector.php index 7b92cc968b..2d0639d23a 100644 --- a/src/Symfony/Framework/DoctrineBundle/DataCollector/DoctrineDataCollector.php +++ b/src/Symfony/Framework/DoctrineBundle/DataCollector/DoctrineDataCollector.php @@ -22,16 +22,24 @@ use Symfony\Framework\ProfilerBundle\DataCollector\DataCollector; */ class DoctrineDataCollector extends DataCollector { - protected function collect() + public function collect() { - $data = array(); + $this->data = array(); if ($this->container->hasService('doctrine.dbal.logger')) { - $data = array( + $this->data = array( 'queries' => $this->container->getDoctrine_Dbal_LoggerService()->queries, ); } + } - return $data; + public function getQueryCount() + { + return count($this->data['queries']); + } + + public function getQueries() + { + return $this->data['queries']; } public function getSummary() diff --git a/src/Symfony/Framework/ProfilerBundle/DataCollector/AppDataCollector.php b/src/Symfony/Framework/ProfilerBundle/DataCollector/AppDataCollector.php index 0013dca4ac..579595edfe 100644 --- a/src/Symfony/Framework/ProfilerBundle/DataCollector/AppDataCollector.php +++ b/src/Symfony/Framework/ProfilerBundle/DataCollector/AppDataCollector.php @@ -20,18 +20,28 @@ namespace Symfony\Framework\ProfilerBundle\DataCollector; */ class AppDataCollector extends DataCollector { - protected function collect() + public function collect() { $request = $this->container->getRequestService(); - return array( + $this->data = array( 'route' => $request->path->get('_route') ? $request->path->get('_route') : 'NONE', 'format' => $request->getRequestFormat(), - 'content_type' => $this->manager->getResponse()->headers->get('Content-Type') ? $this->manager->getResponse()->headers->get('Content-Type') : 'text/html', - 'code' => $this->manager->getResponse()->getStatusCode(), + '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(' diff --git a/src/Symfony/Framework/ProfilerBundle/DataCollector/ConfigDataCollector.php b/src/Symfony/Framework/ProfilerBundle/DataCollector/ConfigDataCollector.php index a35132b6ad..e757750650 100644 --- a/src/Symfony/Framework/ProfilerBundle/DataCollector/ConfigDataCollector.php +++ b/src/Symfony/Framework/ProfilerBundle/DataCollector/ConfigDataCollector.php @@ -22,12 +22,12 @@ use Symfony\Foundation\Kernel; */ class ConfigDataCollector extends DataCollector { - protected function collect() + public function collect() { $kernel = $this->container->getKernelService(); - return array( - 'token' => $this->manager->getProfilerStorage()->getToken(), + $this->data = array( + 'token' => $this->profiler->getProfilerStorage()->getToken(), 'symfony_version' => Kernel::VERSION, 'name' => $kernel->getName(), 'env' => $kernel->getEnvironment(), diff --git a/src/Symfony/Framework/ProfilerBundle/DataCollector/DataCollector.php b/src/Symfony/Framework/ProfilerBundle/DataCollector/DataCollector.php index dccee57573..8771857c15 100644 --- a/src/Symfony/Framework/ProfilerBundle/DataCollector/DataCollector.php +++ b/src/Symfony/Framework/ProfilerBundle/DataCollector/DataCollector.php @@ -3,6 +3,7 @@ namespace Symfony\Framework\ProfilerBundle\DataCollector; use Symfony\Components\DependencyInjection\ContainerInterface; +use Symfony\Framework\ProfilerBundle\Profiler; /* * This file is part of the Symfony framework. @@ -22,7 +23,7 @@ use Symfony\Components\DependencyInjection\ContainerInterface; */ abstract class DataCollector implements DataCollectorInterface { - protected $manager; + protected $profiler; protected $container; protected $data; @@ -33,17 +34,18 @@ abstract class DataCollector implements DataCollectorInterface public function getData() { - if (null === $this->data) { - $this->data = $this->collect(); - } - return $this->data; } - abstract protected function collect(); - - public function setCollectorManager(DataCollectorManager $manager) + public function setData($data) { - $this->manager = $manager; + $this->data = $data; + } + + abstract public function collect(); + + public function setProfiler(Profiler $profiler) + { + $this->profiler = $profiler; } } diff --git a/src/Symfony/Framework/ProfilerBundle/DataCollector/DataCollectorInterface.php b/src/Symfony/Framework/ProfilerBundle/DataCollector/DataCollectorInterface.php index 88b631b734..d3aa228258 100644 --- a/src/Symfony/Framework/ProfilerBundle/DataCollector/DataCollectorInterface.php +++ b/src/Symfony/Framework/ProfilerBundle/DataCollector/DataCollectorInterface.php @@ -2,6 +2,8 @@ namespace Symfony\Framework\ProfilerBundle\DataCollector; +use Symfony\Framework\ProfilerBundle\Profiler; + /* * This file is part of the Symfony framework. * @@ -20,7 +22,7 @@ namespace Symfony\Framework\ProfilerBundle\DataCollector; */ interface DataCollectorInterface { - public function setCollectorManager(DataCollectorManager $manager); + public function setProfiler(Profiler $profiler); public function getData(); diff --git a/src/Symfony/Framework/ProfilerBundle/DataCollector/DataCollectorManager.php b/src/Symfony/Framework/ProfilerBundle/DataCollector/DataCollectorManager.php deleted file mode 100644 index 0c7270d139..0000000000 --- a/src/Symfony/Framework/ProfilerBundle/DataCollector/DataCollectorManager.php +++ /dev/null @@ -1,115 +0,0 @@ - - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -/** - * DataCollectorManager. - * - * @package Symfony - * @subpackage Framework_ProfilerBundle - * @author Fabien Potencier - */ -class DataCollectorManager -{ - protected $container; - protected $profilerStorage; - protected $collectors; - protected $response; - protected $lifetime; - protected $logger; - - public function __construct(ContainerInterface $container, LoggerInterface $logger, ProfilerStorage $profilerStorage, $lifetime = 86400) - { - $this->container = $container; - $this->logger = $logger; - $this->lifetime = $lifetime; - $this->profilerStorage = $profilerStorage; - $this->collectors = $this->initCollectors(); - } - - /** - * Registers a core.response listener. - * - * @param Symfony\Components\EventDispatcher\EventDispatcher $dispatcher An EventDispatcher instance - */ - public function register(EventDispatcher $dispatcher) - { - $dispatcher->connect('core.response', array($this, 'handle')); - } - - public function handle(Event $event, Response $response) - { - if (HttpKernelInterface::MASTER_REQUEST !== $event->getParameter('request_type')) { - return $response; - } - - $this->response = $response; - $this->response->headers->set('X-Debug-Token', $this->profilerStorage->getToken()); - - $data = array(); - foreach ($this->collectors as $name => $collector) { - $data[$name] = $collector->getData(); - } - - try { - $this->profilerStorage->write($data); - $this->profilerStorage->purge($this->lifetime); - } catch (\Exception $e) { - $this->logger->err('Unable to store the profiler information.'); - } - - return $response; - } - - public function getProfilerStorage() - { - return $this->profilerStorage; - } - - public function getResponse() - { - return $this->response; - } - - public function getCollectors() - { - return $this->collectors; - } - - public function initCollectors() - { - $config = $this->container->findAnnotatedServiceIds('data_collector'); - $ids = array(); - $coreCollectors = array(); - $userCollectors = array(); - foreach ($config as $id => $attributes) { - $collector = $this->container->getService($id); - $collector->setCollectorManager($this); - - if (isset($attributes[0]['core']) && $attributes[0]['core']) { - $coreCollectors[$collector->getName()] = $collector; - } else { - $userCollectors[$collector->getName()] = $collector; - } - } - - return $this->collectors = array_merge($coreCollectors, $userCollectors); - } -} diff --git a/src/Symfony/Framework/ProfilerBundle/DataCollector/MemoryDataCollector.php b/src/Symfony/Framework/ProfilerBundle/DataCollector/MemoryDataCollector.php index 16c6755f07..3e64183b21 100644 --- a/src/Symfony/Framework/ProfilerBundle/DataCollector/MemoryDataCollector.php +++ b/src/Symfony/Framework/ProfilerBundle/DataCollector/MemoryDataCollector.php @@ -20,13 +20,18 @@ namespace Symfony\Framework\ProfilerBundle\DataCollector; */ class MemoryDataCollector extends DataCollector { - protected function collect() + public function collect() { - return array( + $this->data = array( 'memory' => memory_get_peak_usage(true), ); } + public function getMemory() + { + return $this->data['memory']; + } + public function getSummary() { return sprintf(' diff --git a/src/Symfony/Framework/ProfilerBundle/DataCollector/TimerDataCollector.php b/src/Symfony/Framework/ProfilerBundle/DataCollector/TimerDataCollector.php index 67cb7c5172..74a7c11ad1 100644 --- a/src/Symfony/Framework/ProfilerBundle/DataCollector/TimerDataCollector.php +++ b/src/Symfony/Framework/ProfilerBundle/DataCollector/TimerDataCollector.php @@ -20,13 +20,18 @@ namespace Symfony\Framework\ProfilerBundle\DataCollector; */ class TimerDataCollector extends DataCollector { - protected function collect() + public function collect() { - return array( + $this->data = array( 'time' => microtime(true) - $this->container->getKernelService()->getStartTime(), ); } + public function getTime() + { + return $this->data['time']; + } + public function getSummary() { return sprintf(' diff --git a/src/Symfony/Framework/ProfilerBundle/DependencyInjection/ProfilerExtension.php b/src/Symfony/Framework/ProfilerBundle/DependencyInjection/ProfilerExtension.php index c47295adc5..6e05139d0c 100644 --- a/src/Symfony/Framework/ProfilerBundle/DependencyInjection/ProfilerExtension.php +++ b/src/Symfony/Framework/ProfilerBundle/DependencyInjection/ProfilerExtension.php @@ -26,7 +26,7 @@ class ProfilerExtension extends LoaderExtension { public function configLoad($config, BuilderConfiguration $configuration) { - if (!$configuration->hasDefinition('data_collector_manager')) { + if (!$configuration->hasDefinition('profiler')) { $loader = new XmlFileLoader(__DIR__.'/../Resources/config'); $configuration->merge($loader->load('collectors.xml')); } diff --git a/src/Symfony/Framework/ProfilerBundle/Listener/DataCollector.php b/src/Symfony/Framework/ProfilerBundle/Listener/DataCollector.php new file mode 100644 index 0000000000..e9f5e9af15 --- /dev/null +++ b/src/Symfony/Framework/ProfilerBundle/Listener/DataCollector.php @@ -0,0 +1,56 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * DataCollector collects data for the current request by listening to the core.response event. + * + * @package Symfony + * @subpackage Framework_ProfilerBundle + * @author Fabien Potencier + */ +class DataCollector +{ + protected $profiler; + + public function __construct(Profiler $profiler) + { + $this->profiler = $profiler; + } + + /** + * Registers a core.response listener. + * + * @param Symfony\Components\EventDispatcher\EventDispatcher $dispatcher An EventDispatcher instance + */ + public function register(EventDispatcher $dispatcher) + { + $dispatcher->connect('core.response', array($this, 'handle')); + } + + public function handle(Event $event, Response $response) + { + if (HttpKernelInterface::MASTER_REQUEST !== $event->getParameter('request_type')) { + return $response; + } + + $this->profiler->collect($response); + + return $response; + } +} diff --git a/src/Symfony/Framework/ProfilerBundle/Listener/WebDebugToolbar.php b/src/Symfony/Framework/ProfilerBundle/Listener/WebDebugToolbar.php index b894f09581..ecb52d1469 100644 --- a/src/Symfony/Framework/ProfilerBundle/Listener/WebDebugToolbar.php +++ b/src/Symfony/Framework/ProfilerBundle/Listener/WebDebugToolbar.php @@ -7,7 +7,7 @@ use Symfony\Components\EventDispatcher\EventDispatcher; use Symfony\Components\EventDispatcher\Event; use Symfony\Components\HttpKernel\Response; use Symfony\Components\HttpKernel\HttpKernelInterface; -use Symfony\Framework\ProfilerBundle\DataCollector\DataCollectorManager; +use Symfony\Framework\ProfilerBundle\Profiler; /* * This file is part of the Symfony framework. @@ -28,12 +28,12 @@ use Symfony\Framework\ProfilerBundle\DataCollector\DataCollectorManager; class WebDebugToolbar { protected $container; - protected $collectorManager; + protected $profiler; - public function __construct(ContainerInterface $container, DataCollectorManager $collectorManager) + public function __construct(ContainerInterface $container, Profiler $profiler) { $this->container = $container; - $this->collectorManager = $collectorManager; + $this->profiler = $profiler; } /** @@ -77,7 +77,7 @@ class WebDebugToolbar protected function injectToolbar(Response $response) { $data = ''; - foreach ($this->collectorManager->getCollectors() as $name => $collector) { + foreach ($this->profiler->getCollectors() as $name => $collector) { $data .= $collector->getSummary(); } diff --git a/src/Symfony/Framework/ProfilerBundle/Profiler.php b/src/Symfony/Framework/ProfilerBundle/Profiler.php new file mode 100644 index 0000000000..689bf5a917 --- /dev/null +++ b/src/Symfony/Framework/ProfilerBundle/Profiler.php @@ -0,0 +1,192 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * Profiler. + * + * @package Symfony + * @subpackage Framework_ProfilerBundle + * @author Fabien Potencier + */ +class Profiler implements \ArrayAccess +{ + protected $container; + protected $profilerStorage; + protected $collectors; + protected $response; + protected $logger; + + public function __construct(ContainerInterface $container, ProfilerStorage $profilerStorage, LoggerInterface $logger = null) + { + $this->container = $container; + $this->profilerStorage = $profilerStorage; + $this->logger = $logger; + $this->initCollectors(); + $this->loadCollectorData(); + } + + public function __clone() + { + $this->profilerStorage = clone $this->profilerStorage; + } + + public function load(Response $response) + { + return $this->getProfilerForToken($response->headers->get('X-Debug-Token')); + } + + public function getProfilerForToken($token) + { + $profiler = clone $this; + $profiler->profilerStorage->setToken($token); + $profiler->loadCollectorData(); + + return $profiler; + } + + public function collect(Response $response) + { + $this->response = $response; + $this->response->headers->set('X-Debug-Token', $this->profilerStorage->getToken()); + + $data = array(); + foreach ($this->collectors as $name => $collector) { + $collector->collect(); + + $data[$name] = $collector->getData(); + } + + try { + $this->profilerStorage->write($data); + $this->profilerStorage->purge(); + } catch (\Exception $e) { + if (null !== $this->logger) { + $this->logger->err('Unable to store the profiler information.'); + } + } + } + + public function getProfilerStorage() + { + return $this->profilerStorage; + } + + public function getResponse() + { + return $this->response; + } + + 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.'); + } + } + } + + public function getCollectors() + { + return $this->collectors; + } + + public function hasCollector($name) + { + return isset($this->collectors[$name]); + } + + public function getCollector($name) + { + if (!isset($this->collectors[$name])) { + throw new \InvalidArgumentException(sprintf('Collector "%s" does not exist.', $name)); + } + + return $this->collectors[$name]; + } + + /** + * Returns true if the named field exists. + * + * @param string $name The field name + * + * @param Boolean true if the field exists, false otherwise + */ + public function offsetExists($name) + { + return $this->hasCollector($name); + } + + /** + * Gets the value of a field. + * + * @param string $name The field name + * + * @throws \InvalidArgumentException if the field does not exist + */ + public function offsetGet($name) + { + return $this->getCollector($name); + } + + /** + * Sets the value of a field. + * + * @param string $name The field name + * @param string|array $value The value of the field + * + * @throws \InvalidArgumentException if the field does not exist + */ + public function offsetSet($name, $value) + { + throw new \LogicException('The Collectors cannot be set.'); + } + + /** + * Unimplemented. + * + * @param string $name The field name + */ + public function offsetUnset($name) + { + throw new \LogicException('The Collectors cannot be removed.'); + } + + protected function initCollectors() + { + $config = $this->container->findAnnotatedServiceIds('data_collector'); + $ids = array(); + $coreCollectors = array(); + $userCollectors = array(); + foreach ($config as $id => $attributes) { + $collector = $this->container->getService($id); + $collector->setProfiler($this); + + if (isset($attributes[0]['core']) && $attributes[0]['core']) { + $coreCollectors[$collector->getName()] = $collector; + } else { + $userCollectors[$collector->getName()] = $collector; + } + } + + $this->collectors = array_merge($coreCollectors, $userCollectors); + } +} diff --git a/src/Symfony/Framework/ProfilerBundle/ProfilerStorage.php b/src/Symfony/Framework/ProfilerBundle/ProfilerStorage.php index efc986b313..6ef9f2c69b 100644 --- a/src/Symfony/Framework/ProfilerBundle/ProfilerStorage.php +++ b/src/Symfony/Framework/ProfilerBundle/ProfilerStorage.php @@ -23,12 +23,14 @@ class ProfilerStorage protected $token; protected $data; protected $store; + protected $lifetime; - public function __construct($store, $token = null) + public function __construct($store, $token = null, $lifetime = 86400) { $this->store = $store; $this->token = null === $token ? uniqid() : $token; $this->data = null; + $this->lifetime = (int) $lifetime; } public function hasData() @@ -46,7 +48,13 @@ class ProfilerStorage return $this->data; } - return isset($this->data[$name]) ? $this->data[$name] : null; + return isset($this->data[$name]) ? $this->data[$name] : array(); + } + + public function setToken($token) + { + $this->token = $token; + $this->data = null; } public function getToken() @@ -134,10 +142,10 @@ class ProfilerStorage } } - public function purge($lifetime) + public function purge() { $db = $this->initDb(false); - $args = array(':time' => time() - (int) $lifetime); + $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/Framework/ProfilerBundle/Resources/config/collectors.xml b/src/Symfony/Framework/ProfilerBundle/Resources/config/collectors.xml index fff25d9996..fc5fca3c64 100644 --- a/src/Symfony/Framework/ProfilerBundle/Resources/config/collectors.xml +++ b/src/Symfony/Framework/ProfilerBundle/Resources/config/collectors.xml @@ -5,10 +5,11 @@ xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd"> - Symfony\Framework\ProfilerBundle\DataCollector\DataCollectorManager - Symfony\Framework\ProfilerBundle\ProfilerStorage - %kernel.cache_dir%/profiler.db - 86400 + Symfony\Framework\ProfilerBundle\Profiler + Symfony\Framework\ProfilerBundle\ProfilerStorage + %kernel.cache_dir%/profiler.db + 86400 + Symfony\Framework\ProfilerBundle\Listener\DataCollector Symfony\Framework\ProfilerBundle\DataCollector\ConfigDataCollector Symfony\Framework\ProfilerBundle\DataCollector\AppDataCollector Symfony\Framework\ProfilerBundle\DataCollector\TimerDataCollector @@ -16,16 +17,21 @@ - - + - - - %data_collector_manager.lifetime% + + - - %data_collector_manager.storage.file% + + %profiler.storage.file% + null + %profiler.storage.lifetime% + + + + + diff --git a/src/Symfony/Framework/ProfilerBundle/Resources/config/toolbar.xml b/src/Symfony/Framework/ProfilerBundle/Resources/config/toolbar.xml index 64ae8a5019..1f50621ae1 100644 --- a/src/Symfony/Framework/ProfilerBundle/Resources/config/toolbar.xml +++ b/src/Symfony/Framework/ProfilerBundle/Resources/config/toolbar.xml @@ -12,7 +12,7 @@ - + diff --git a/src/Symfony/Framework/ProfilerBundle/Tests/DependencyInjection/ProfilerExtensionTest.php b/src/Symfony/Framework/ProfilerBundle/Tests/DependencyInjection/ProfilerExtensionTest.php index 5fe77d9e1b..ff3f3d77f9 100644 --- a/src/Symfony/Framework/ProfilerBundle/Tests/DependencyInjection/ProfilerExtensionTest.php +++ b/src/Symfony/Framework/ProfilerBundle/Tests/DependencyInjection/ProfilerExtensionTest.php @@ -23,7 +23,7 @@ class ProfilerExtensionTest extends TestCase $loader = new ProfilerExtension(); $configuration = $loader->configLoad(array(), $configuration); - $this->assertEquals('Symfony\\Framework\\ProfilerBundle\\DataCollector\\DataCollectorManager', $configuration->getParameter('data_collector_manager.class'), '->configLoad() loads the collectors.xml file if not already loaded'); + $this->assertEquals('Symfony\\Framework\\ProfilerBundle\\Profiler', $configuration->getParameter('profiler.class'), '->configLoad() loads the collectors.xml file if not already loaded'); $this->assertFalse($configuration->hasParameter('debug.toolbar.class'), '->configLoad() does not load the toolbar.xml file'); $configuration = $loader->configLoad(array('toolbar' => true), $configuration);