فهرست منبع

[FrameworkBundle] Add error detection to the TraceableEventDispatcher

Victor Berchet 14 سال پیش
والد
کامیت
a6c3b4bfa2

+ 68 - 12
src/Symfony/Bundle/FrameworkBundle/Debug/TraceableEventDispatcher.php

@@ -26,6 +26,7 @@ class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements
 {
     protected $logger;
     protected $called;
+    protected $container;
 
     /**
      * Constructor.
@@ -37,10 +38,66 @@ class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements
     {
         parent::__construct($container);
 
+        $this->container = $container;
         $this->logger = $logger;
         $this->called = array();
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * @throws \RuntimeException if the listener method is not callable
+     */
+    public function addListener($eventNames, $listener, $priority = 0)
+    {
+        if (!$listener instanceof \Closure) {
+            foreach ((array) $eventNames as $method) {
+                if (!is_callable(array($listener, $method))) {
+                    $msg = sprintf('The event method "%s()" is not callable on the class "%s"', $method, get_class($listener));
+                    if (null !== $this->logger) {
+                        $this->logger->err($msg);
+                    }
+                    throw new \RuntimeException($msg);
+                }
+            }
+        }
+        parent::addListener($eventNames, $listener, $priority);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws \RuntimeException if the service does not exist
+     * @throws \RuntimeException if the listener service method is not callable
+     */
+    public function addListenerService($eventNames, $serviceId, $priority = 0)
+    {
+        $error = null;
+
+        if (!$this->container->has($serviceId)) {
+            $error = sprintf('The container has no service "%s"', $serviceId);
+        } else {
+            $listener = $this->container->get($serviceId);
+            if (!$listener instanceof \Closure) {
+                foreach ((array) $eventNames as $method) {
+                    if (!is_callable(array($listener, $method))) {
+                        $error = sprintf('The event method "%s()" is not callable on the service "%s"', $method, $serviceId);
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (null !== $error) {
+            if (null !== $this->logger) {
+                $this->logger->err($error);
+            }
+            throw new \RuntimeException($error);
+        }
+
+        parent::addListenerService($eventNames, $serviceId, $priority);
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -103,22 +160,21 @@ class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements
         if ($listener instanceof \Closure) {
             $info += array('type' => 'Closure');
         } else {
-            $info += array(
-                'type'  => 'Method',
-                'class' => $class = get_class($listener)
-            );
+            $class = get_class($listener);
             try {
                 $r = new \ReflectionMethod($class, $eventName);
-                $info += array(
-                    'file'  => $r->getFileName(),
-                    'line'  => $r->getStartLine()
-                );
+                $file = $r->getFileName();
+                $line = $r->getStartLine();
             } catch (\ReflectionException $e) {
-                $info += array(
-                    'file'  => null,
-                    'line'  => null
-                );
+                $file = null;
+                $line = null;
             }
+            $info += array(
+                'type'  => 'Method',
+                'class' => $class,
+                'file'  => $file,
+                'line'  => $line
+            );
         }
 
         return $info;

+ 70 - 0
src/Symfony/Bundle/FrameworkBundle/Tests/Debug/TraceableEventDispactherTest.php

@@ -0,0 +1,70 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\Tests\Debug;
+
+use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
+use Symfony\Bundle\FrameworkBundle\Debug\TraceableEventDispatcher;
+
+class TraceableEventDispactherTest extends TestCase
+{
+
+    /**
+     * @expectedException \RuntimeException
+     */
+    public function testThrowsAnExceptionWhenAListenerMethodIsNotCallable()
+    {
+        $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
+        $dispatcher = new TraceableEventDispatcher($container);
+        $dispatcher->addListener('onFooEvent', new \stdClass());
+    }
+
+    /**
+     * @expectedException \RuntimeException
+     */
+    public function testThrowsAnExceptionWhenAListenerServiceIsNotFound()
+    {
+        $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
+        $container
+            ->expects($this->once())
+            ->method('has')
+            ->with($this->equalTo('listener.service'))
+            ->will($this->returnValue(false))
+        ;
+
+        $dispatcher = new TraceableEventDispatcher($container);
+
+        $dispatcher->addListenerService('onFooEvent', 'listener.service');
+    }
+
+    /**
+     * @expectedException \RuntimeException
+     */
+    public function testThrowsAnExceptionWhenAListenerServiceMethodIsNotCallable()
+    {
+        $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
+        $container
+            ->expects($this->once())
+            ->method('has')
+            ->with($this->equalTo('listener.service'))
+            ->will($this->returnValue(true))
+        ;
+        $container
+            ->expects($this->once())
+            ->method('get')
+            ->with($this->equalTo('listener.service'))
+            ->will($this->returnValue(new \stdClass()))
+        ;
+
+        $dispatcher = new TraceableEventDispatcher($container);
+        $dispatcher->addListenerService('onFooEvent', 'listener.service');
+    }
+}