Explorar o código

[HttpKernel] fixed profile parent/children for deep-nested requests

Fabien Potencier %!s(int64=13) %!d(string=hai) anos
pai
achega
2b0af5e93b

+ 1 - 0
src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml

@@ -26,6 +26,7 @@
         <service id="profiler_listener" class="%profiler_listener.class%">
             <tag name="kernel.event_listener" event="kernel.response" method="onKernelResponse" priority="-100" />
             <tag name="kernel.event_listener" event="kernel.exception" method="onKernelException" />
+            <tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" />
             <argument type="service" id="profiler" />
             <argument type="service" id="profiler.request_matcher" on-invalid="null" />
             <argument>%profiler_listener.only_exceptions%</argument>

+ 34 - 11
src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php

@@ -32,6 +32,7 @@ class ProfilerListener
     protected $onlyMasterRequests;
     protected $exception;
     protected $children;
+    protected $requests;
 
     /**
      * Constructor.
@@ -47,7 +48,7 @@ class ProfilerListener
         $this->matcher = $matcher;
         $this->onlyException = (Boolean) $onlyException;
         $this->onlyMasterRequests = (Boolean) $onlyMasterRequests;
-        $this->children = array();
+        $this->children = new \SplObjectStorage();
     }
 
     /**
@@ -64,6 +65,11 @@ class ProfilerListener
         $this->exception = $event->getException();
     }
 
+    public function onKernelRequest(GetResponseEvent $event)
+    {
+        $this->requests[] = $event->getRequest();
+    }
+
     /**
      * Handles the onKernelResponse event.
      *
@@ -87,18 +93,35 @@ class ProfilerListener
             return;
         }
 
-        if ($profile = $this->profiler->collect($event->getRequest(), $event->getResponse(), $exception)) {
-            if ($master) {
-                foreach ($this->children as $child) {
-                    $child->setParent($profile);
-                    $profile->addChild($child);
-                    $this->profiler->saveProfile($child);
-                }
-                $this->profiler->saveProfile($profile);
-                $this->children = array();
+        if (!$profile = $this->profiler->collect($event->getRequest(), $event->getResponse(), $exception)) {
+            return;
+        }
+
+        array_pop($this->requests);
+
+        // keep the profile as the child of its parent
+        if (!$master) {
+            $parent = $this->requests[count($this->requests) - 1];
+            if (!isset($this->children[$parent])) {
+                $profiles = array($profile);
             } else {
-                $this->children[] = $profile;
+                $profiles = $this->children[$parent];
+                $profiles[] = $profile;
+            }
+
+            $this->children[$parent] = $profiles;
+        }
+
+        // store the profile and its children
+        if (isset($this->children[$event->getRequest()])) {
+            foreach ($this->children[$event->getRequest()] as $child) {
+                $child->setParent($profile);
+                $profile->addChild($child);
+                $this->profiler->saveProfile($child);
             }
+            $this->children[$event->getRequest()] = array();
         }
+
+        $this->profiler->saveProfile($profile);
     }
 }

+ 18 - 6
src/Symfony/Component/HttpKernel/Profiler/PdoProfilerStorage.php

@@ -90,13 +90,25 @@ abstract class PdoProfilerStorage implements ProfilerStorageInterface
             ':time'       => $profile->getTime(),
             ':created_at' => time(),
         );
-        try {
-            $this->exec($db, 'INSERT INTO sf_profiler_data (token, parent, data, ip, url, time, created_at) VALUES (:token, :parent, :data, :ip, :url, :time, :created_at)', $args);
-            $this->cleanup();
-            $status = true;
-        } catch (\Exception $e) {
-            $status = false;
+
+        if ($this->read($profile->getToken())) {
+            try {
+                $this->exec($db, 'UPDATE sf_profiler_data SET parent = :parent, data = :data, ip = :ip, url = :url, time = :time, created_at = :created_at WHERE token = :token', $args);
+                $this->cleanup();
+                $status = true;
+            } catch (\Exception $e) {
+                $status = false;
+            }
+        } else {
+            try {
+                $this->exec($db, 'INSERT INTO sf_profiler_data (token, parent, data, ip, url, time, created_at) VALUES (:token, :parent, :data, :ip, :url, :time, :created_at)', $args);
+                $this->cleanup();
+                $status = true;
+            } catch (\Exception $e) {
+                $status = false;
+            }
         }
+
         $this->close($db);
 
         return $status;

+ 7 - 2
tests/Symfony/Tests/Component/HttpKernel/Profiler/SqliteProfilerStorageTest.php

@@ -72,9 +72,14 @@ class SqliteProfilerStorageTest extends \PHPUnit_Framework_TestCase
     public function testStoreDuplicateToken()
     {
         $profile = new Profile('token');
+        $profile->setUrl('http://example.com/');
 
-        $this->assertTrue(true === self::$storage->write($profile), '->write() returns true when the token is unique');
-        $this->assertTrue(false === self::$storage->write($profile), '->write() return false when the token is already present in the DB');
+        $this->assertTrue(self::$storage->write($profile), '->write() returns true when the token is unique');
+
+        $profile->setUrl('http://example.net/');
+
+        $this->assertTrue(self::$storage->write($profile), '->write() returns true when the token is already present in the DB');
+        $this->assertEquals('http://example.net/', self::$storage->read('token')->getUrl(), '->write() overwrites the current profile data');
     }
 
     public function testRetrieveByIp()