فهرست منبع

Merge remote branch 'vicb/event_debug'

* vicb/event_debug:
  [FrameworkBundle] Make some TraceableEventDispacther properties private
  [Event] Tweak phpDoc for consistency
  [FrameworkBundle] Add error detection to the TraceableEventDispatcher
Fabien Potencier 14 سال پیش
والد
کامیت
4e047b5e27

+ 7 - 8
src/Symfony/Bundle/FrameworkBundle/ContainerAwareEventDispatcher.php

@@ -49,20 +49,19 @@ class ContainerAwareEventDispatcher extends EventDispatcher
     /**
      * Adds a service as event listener
      *
-     * @param string|array $events  One or more events for which the listener
-     *                              is added
-     * @param string $serviceId     The ID of the listener service
-     * @param integer $priority     The higher this value, the earlier an event
-     *                              listener will be triggered in the chain.
-     *                              Defaults to 0.
+     * @param string|array $eventNames One or more events for which the listener is added
+     * @param string       $serviceId  The ID of the listener service
+     * @param integer      $priority   The higher this value, the earlier an event listener
+     *                                 will be triggered in the chain.
+     *                                 Defaults to 0.
      */
-    public function addListenerService($events, $serviceId, $priority = 0)
+    public function addListenerService($eventNames, $serviceId, $priority = 0)
     {
         if (!is_string($serviceId)) {
             throw new \InvalidArgumentException('Expected a string argument');
         }
 
-        foreach ((array) $events as $event) {
+        foreach ((array) $eventNames as $event) {
             // Prevent duplicate entries
             $this->listenerIds[$event][$serviceId] = $priority;
         }

+ 79 - 15
src/Symfony/Bundle/FrameworkBundle/Debug/TraceableEventDispatcher.php

@@ -24,8 +24,9 @@ use Symfony\Component\EventDispatcher\Event;
  */
 class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements TraceableEventDispatcherInterface
 {
-    protected $logger;
-    protected $called;
+    private $logger;
+    private $called;
+    private $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}
      */
@@ -97,28 +154,35 @@ class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements
         return $notCalled;
     }
 
-    protected function getListenerInfo($listener, $eventName)
+    /**
+     * Returns information about the listener
+     * 
+     * @param object $listener  The listener
+     * @param string $eventName The event name
+     * 
+     * @return array Informations about the listener
+     */
+    private function getListenerInfo($listener, $eventName)
     {
         $info = array('event' => $eventName);
         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');
+    }
+}

+ 2 - 2
src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php

@@ -37,9 +37,9 @@ namespace Symfony\Component\EventDispatcher;
 interface EventSubscriberInterface
 {
     /**
-     * Returns an array of events this subscriber wants to listen to.
+     * Returns an array of event names this subscriber wants to listen to.
      *
-     * @return array
+     * @return array The event names to listen to
      */
     static function getSubscribedEvents();
 }