diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml
index 7eb472fd973bb..def9aea920699 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml
@@ -34,6 +34,7 @@
+
@@ -45,6 +46,7 @@
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml
index e7d1c3c7d47b5..e4ba4f9792c90 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml
@@ -19,7 +19,6 @@
-
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml
index a8666606e150f..cee86b3b72cfa 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml
@@ -29,6 +29,7 @@
%profiler_listener.only_exceptions%
%profiler_listener.only_master_requests%
+
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php
index cd7f7870177c2..0c35cf1701512 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php
@@ -11,8 +11,10 @@
namespace Symfony\Component\HttpKernel\DataCollector;
+use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface;
/**
@@ -20,8 +22,15 @@
*
* @author Fabien Potencier
*/
-class EventDataCollector extends DataCollector
+class EventDataCollector extends DataCollector implements LateDataCollectorInterface
{
+ protected $dispatcher;
+
+ public function __construct(EventDispatcherInterface $dispatcher = null)
+ {
+ $this->dispatcher = $dispatcher;
+ }
+
/**
* {@inheritdoc}
*/
@@ -33,6 +42,14 @@ public function collect(Request $request, Response $response, \Exception $except
);
}
+ public function lateCollect()
+ {
+ if ($this->dispatcher instanceof TraceableEventDispatcherInterface) {
+ $this->setCalledListeners($this->dispatcher->getCalledListeners());
+ $this->setNotCalledListeners($this->dispatcher->getNotCalledListeners());
+ }
+ }
+
/**
* Sets the called listeners.
*
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/LateDataCollectorInterface.php b/src/Symfony/Component/HttpKernel/DataCollector/LateDataCollectorInterface.php
new file mode 100644
index 0000000000000..2c4ccb6850ed9
--- /dev/null
+++ b/src/Symfony/Component/HttpKernel/DataCollector/LateDataCollectorInterface.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpKernel\DataCollector;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * LateDataCollectorInterface.
+ *
+ * @author Fabien Potencier
+ */
+interface LateDataCollectorInterface
+{
+ /**
+ * Collects data as late as possible.
+ */
+ public function lateCollect();
+}
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php
index f08720e8073bb..7a94fa859546e 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php
@@ -32,6 +32,14 @@ public function __construct($logger = null)
}
}
+ /**
+ * {@inheritdoc}
+ */
+ public function collect(Request $request, Response $response, \Exception $exception = null)
+ {
+ // everything is done as late as possible
+ }
+
/**
* {@inheritdoc}
*/
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php
index 115101f7ccc98..8db93621537b9 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\HttpKernel\DataCollector;
+use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -19,7 +20,7 @@
*
* @author Fabien Potencier
*/
-class MemoryDataCollector extends DataCollector
+class MemoryDataCollector extends DataCollector implements LateDataCollectorInterface
{
public function __construct()
{
@@ -37,6 +38,14 @@ public function collect(Request $request, Response $response, \Exception $except
$this->updateMemoryUsage();
}
+ /**
+ * {@inheritdoc}
+ */
+ public function lateCollect()
+ {
+ $this->updateMemoryUsage();
+ }
+
/**
* Gets the memory.
*
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php
index 74d7616bf9cfc..3dd59465df979 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php
@@ -12,22 +12,26 @@
namespace Symfony\Component\HttpKernel\DataCollector;
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
+use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Stopwatch\Stopwatch;
/**
* TimeDataCollector.
*
* @author Fabien Potencier
*/
-class TimeDataCollector extends DataCollector
+class TimeDataCollector extends DataCollector implements LateDataCollectorInterface
{
protected $kernel;
+ protected $stopwatch;
- public function __construct(KernelInterface $kernel = null)
+ public function __construct(KernelInterface $kernel = null, $stopwatch = null)
{
$this->kernel = $kernel;
+ $this->stopwatch = $stopwatch;
}
/**
@@ -42,11 +46,23 @@ public function collect(Request $request, Response $response, \Exception $except
}
$this->data = array(
+ 'token' => $response->headers->get('X-Debug-Token'),
'start_time' => $startTime * 1000,
'events' => array(),
);
}
+ /**
+ * {@inheritdoc}
+ */
+ public function lateCollect()
+ {
+ if (null !== $this->stopwatch && isset($this->data['token'])) {
+ $this->setEvents($this->stopwatch->getSectionEvents($this->data['token']));
+ }
+ unset($this->data['token']);
+ }
+
/**
* Sets the request events.
*
diff --git a/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php b/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php
index 85f27850e6b2f..449e39ad47ea5 100644
--- a/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php
+++ b/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php
@@ -14,8 +14,6 @@
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\HttpKernel\KernelEvents;
use Psr\Log\LoggerInterface;
-use Symfony\Component\HttpKernel\Profiler\Profile;
-use Symfony\Component\HttpKernel\Profiler\Profiler;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
@@ -33,7 +31,6 @@ class TraceableEventDispatcher implements EventDispatcherInterface, TraceableEve
private $logger;
private $called;
private $stopwatch;
- private $profiler;
private $dispatcher;
private $wrappedListeners;
private $firstCalledEvent;
@@ -59,11 +56,16 @@ public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $sto
/**
* Sets the profiler.
*
+ * The traceable event dispatcher does not use the profiler anymore.
+ * The job is now done directly by the Profiler listener and the
+ * data collectors themselves.
+ *
* @param Profiler|null $profiler A Profiler instance
+ *
+ * @deprecated Deprecated since version 2.4, to be removed in 3.0.
*/
public function setProfiler(Profiler $profiler = null)
{
- $this->profiler = $profiler;
}
/**
@@ -142,7 +144,9 @@ public function dispatch($eventName, Event $event = null)
unset($this->firstCalledEvent[$eventName]);
- $e->stop();
+ if ($e->isStarted()) {
+ $e->stop();
+ }
$this->postDispatch($eventName, $event);
@@ -312,57 +316,6 @@ private function getListenerInfo($listener, $eventName)
return $info;
}
- /**
- * Updates the stopwatch data in the profile hierarchy.
- *
- * @param string $token Profile token
- * @param Boolean $updateChildren Whether to update the children altogether
- */
- private function updateProfiles($token, $updateChildren)
- {
- if (!$this->profiler || !$profile = $this->profiler->loadProfile($token)) {
- return;
- }
-
- $this->saveInfoInProfile($profile, $updateChildren);
- }
-
- /**
- * Update the profiles with the timing and events information and saves them.
- *
- * @param Profile $profile The root profile
- * @param Boolean $updateChildren Whether to update the children altogether
- */
- private function saveInfoInProfile(Profile $profile, $updateChildren)
- {
- try {
- $collector = $profile->getCollector('memory');
- $collector->updateMemoryUsage();
- } catch (\InvalidArgumentException $e) {
- }
-
- try {
- $collector = $profile->getCollector('time');
- $collector->setEvents($this->stopwatch->getSectionEvents($profile->getToken()));
- } catch (\InvalidArgumentException $e) {
- }
-
- try {
- $collector = $profile->getCollector('events');
- $collector->setCalledListeners($this->getCalledListeners());
- $collector->setNotCalledListeners($this->getNotCalledListeners());
- } catch (\InvalidArgumentException $e) {
- }
-
- $this->profiler->saveProfile($profile);
-
- if ($updateChildren) {
- foreach ($profile->getChildren() as $child) {
- $this->saveInfoInProfile($child, true);
- }
- }
- }
-
private function preDispatch($eventName, Event $event)
{
// wrap all listeners before they are called
@@ -411,23 +364,14 @@ private function postDispatch($eventName, Event $event)
case KernelEvents::RESPONSE:
$token = $event->getResponse()->headers->get('X-Debug-Token');
$this->stopwatch->stopSection($token);
- if ($event->isMasterRequest()) {
- // The profiles can only be updated once they have been created
- // that is after the 'kernel.response' event of the main request
- $this->updateProfiles($token, true);
- }
break;
case KernelEvents::TERMINATE:
- $token = $event->getResponse()->headers->get('X-Debug-Token');
// In the special case described in the `preDispatch` method above, the `$token` section
// does not exist, then closing it throws an exception which must be caught.
+ $token = $event->getResponse()->headers->get('X-Debug-Token');
try {
$this->stopwatch->stopSection($token);
} catch (\LogicException $e) {}
- // The children profiles have been updated by the previous 'kernel.response'
- // event. Only the root profile need to be updated with the 'kernel.terminate'
- // timing informations.
- $this->updateProfiles($token, false);
break;
}
@@ -448,7 +392,9 @@ private function wrapListener($eventName, $listener)
call_user_func($listener, $event, $eventName, $self);
- $e->stop();
+ if ($e->isStarted()) {
+ $e->stop();
+ }
if ($event->isPropagationStopped()) {
$self->logSkippedListeners($eventName, $event, $listener);
diff --git a/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php b/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php
index 7ee267c510fa2..217bbdff11815 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php
@@ -11,13 +11,16 @@
namespace Symfony\Component\HttpKernel\EventListener;
+use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
+use Symfony\Component\HttpKernel\Event\PostResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Profiler\Profile;
use Symfony\Component\HttpKernel\Profiler\Profiler;
use Symfony\Component\HttpFoundation\RequestMatcherInterface;
+use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
@@ -32,9 +35,10 @@ class ProfilerListener implements EventSubscriberInterface
protected $onlyException;
protected $onlyMasterRequests;
protected $exception;
- protected $children;
protected $requests;
protected $profiles;
+ protected $requestStack;
+ protected $parents;
/**
* Constructor.
@@ -44,14 +48,16 @@ class ProfilerListener implements EventSubscriberInterface
* @param Boolean $onlyException true if the profiler only collects data when an exception occurs, false otherwise
* @param Boolean $onlyMasterRequests true if the profiler only collects data when the request is a master request, false otherwise
*/
- public function __construct(Profiler $profiler, RequestMatcherInterface $matcher = null, $onlyException = false, $onlyMasterRequests = false)
+ public function __construct(Profiler $profiler, RequestMatcherInterface $matcher = null, $onlyException = false, $onlyMasterRequests = false, RequestStack $requestStack = null)
{
$this->profiler = $profiler;
$this->matcher = $matcher;
$this->onlyException = (Boolean) $onlyException;
$this->onlyMasterRequests = (Boolean) $onlyMasterRequests;
- $this->children = new \SplObjectStorage();
- $this->profiles = array();
+ $this->profiles = new \SplObjectStorage();
+ $this->parents = new \SplObjectStorage();
+ $this->requests = array();
+ $this->requestStack = $requestStack;
}
/**
@@ -68,9 +74,14 @@ public function onKernelException(GetResponseForExceptionEvent $event)
$this->exception = $event->getException();
}
+ /**
+ * @deprecated Deprecated since version 2.4, to be removed in 3.0.
+ */
public function onKernelRequest(GetResponseEvent $event)
{
- $this->requests[] = $event->getRequest();
+ if (null === $this->requestStack) {
+ $this->requests[] = $event->getRequest();
+ }
}
/**
@@ -101,42 +112,35 @@ public function onKernelResponse(FilterResponseEvent $event)
return;
}
- $this->profiles[] = $profile;
+ $this->profiles[$request] = $profile;
- if (null !== $exception) {
- foreach ($this->profiles as $profile) {
- $this->profiler->saveProfile($profile);
- }
-
- return;
- }
-
- // keep the profile as the child of its parent
- if (!$master) {
+ if (null !== $this->requestStack) {
+ $this->parents[$request] = $this->requestStack->getParentRequest();
+ } elseif (!$master) {
+ // to be removed when requestStack is required
array_pop($this->requests);
- $parent = end($this->requests);
-
- // when simulating requests, we might not have the parent
- if ($parent) {
- $profiles = isset($this->children[$parent]) ? $this->children[$parent] : array();
- $profiles[] = $profile;
- $this->children[$parent] = $profiles;
- }
+ $this->parents[$request] = end($this->requests);
}
+ }
- if (isset($this->children[$request])) {
- foreach ($this->children[$request] as $child) {
- $profile->addChild($child);
+ public function onKernelTerminate(PostResponseEvent $event)
+ {
+ // attach children to parents
+ foreach ($this->profiles as $request) {
+ if ($parentRequest = $this->parents[$request]) {
+ $this->profiles[$parentRequest]->addChild($this->profiles[$request]);
}
- $this->children[$request] = array();
}
- if ($master) {
- $this->saveProfiles($profile);
-
- $this->children = new \SplObjectStorage();
+ // save profiles
+ foreach ($this->profiles as $request) {
+ $this->profiler->saveProfile($this->profiles[$request]);
}
+
+ $this->profiles = new \SplObjectStorage();
+ $this->parents = new \SplObjectStorage();
+ $this->requests = array();
}
public static function getSubscribedEvents()
@@ -147,19 +151,7 @@ public static function getSubscribedEvents()
KernelEvents::REQUEST => array('onKernelRequest', 1024),
KernelEvents::RESPONSE => array('onKernelResponse', -100),
KernelEvents::EXCEPTION => 'onKernelException',
+ KernelEvents::TERMINATE => array('onKernelTerminate', -1024),
);
}
-
- /**
- * Saves the profile hierarchy.
- *
- * @param Profile $profile The root profile
- */
- private function saveProfiles(Profile $profile)
- {
- $this->profiler->saveProfile($profile);
- foreach ($profile->getChildren() as $profile) {
- $this->saveProfiles($profile);
- }
- }
}
diff --git a/src/Symfony/Component/HttpKernel/Profiler/Profiler.php b/src/Symfony/Component/HttpKernel/Profiler/Profiler.php
index d5bad876a0eb8..1c90a8a3c85bb 100644
--- a/src/Symfony/Component/HttpKernel/Profiler/Profiler.php
+++ b/src/Symfony/Component/HttpKernel/Profiler/Profiler.php
@@ -15,6 +15,7 @@
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface;
use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface;
+use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
use Psr\Log\LoggerInterface;
/**
@@ -109,6 +110,13 @@ public function loadProfile($token)
*/
public function saveProfile(Profile $profile)
{
+ // late collect
+ foreach ($profile->getCollectors() as $collector) {
+ if ($collector instanceof LateDataCollectorInterface) {
+ $collector->lateCollect();
+ }
+ }
+
if (!($ret = $this->storage->write($profile)) && null !== $this->logger) {
$this->logger->warning('Unable to store the profiler information.');
}
@@ -215,8 +223,8 @@ public function collect(Request $request, Response $response, \Exception $except
foreach ($this->collectors as $collector) {
$collector->collect($request, $response, $exception);
- // forces collectors to become "read/only" (they loose their object dependencies)
- $profile->addCollector(unserialize(serialize($collector)));
+ // we need to clone for sub-requests
+ $profile->addCollector(clone $collector);
}
return $profile;