vendor/symfony/src/Symfony/Bundle/FrameworkBundle/Debug/TraceableEventDispatcher.php
changeset 0 7f95f8617b0b
equal deleted inserted replaced
-1:000000000000 0:7f95f8617b0b
       
     1 <?php
       
     2 
       
     3 /*
       
     4  * This file is part of the Symfony package.
       
     5  *
       
     6  * (c) Fabien Potencier <fabien@symfony.com>
       
     7  *
       
     8  * For the full copyright and license information, please view the LICENSE
       
     9  * file that was distributed with this source code.
       
    10  */
       
    11 
       
    12 namespace Symfony\Bundle\FrameworkBundle\Debug;
       
    13 
       
    14 use Symfony\Bundle\FrameworkBundle\ContainerAwareEventDispatcher;
       
    15 use Symfony\Component\HttpKernel\Log\LoggerInterface;
       
    16 use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcherInterface;
       
    17 use Symfony\Component\DependencyInjection\ContainerInterface;
       
    18 use Symfony\Component\EventDispatcher\Event;
       
    19 
       
    20 /**
       
    21  * Extends the ContainerAwareEventDispatcher to add some debugging tools.
       
    22  *
       
    23  * @author Fabien Potencier <fabien@symfony.com>
       
    24  */
       
    25 class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements TraceableEventDispatcherInterface
       
    26 {
       
    27     private $logger;
       
    28     private $called;
       
    29 
       
    30     /**
       
    31      * Constructor.
       
    32      *
       
    33      * @param ContainerInterface $container A ContainerInterface instance
       
    34      * @param LoggerInterface    $logger    A LoggerInterface instance
       
    35      */
       
    36     public function __construct(ContainerInterface $container, LoggerInterface $logger = null)
       
    37     {
       
    38         parent::__construct($container);
       
    39 
       
    40         $this->logger = $logger;
       
    41         $this->called = array();
       
    42     }
       
    43 
       
    44     /**
       
    45      * {@inheritDoc}
       
    46      *
       
    47      * @throws \RuntimeException if the listener method is not callable
       
    48      */
       
    49     public function addListener($eventName, $listener, $priority = 0)
       
    50     {
       
    51         if (!is_callable($listener)) {
       
    52             if (is_string($listener)) {
       
    53                 $typeDefinition = '[string] '.$listener;
       
    54             } elseif (is_array($listener)) {
       
    55                 $typeDefinition = '[array] '.(is_object($listener[0]) ? get_class($listener[0]) : $listener[0]).'::'.$listener[1];
       
    56             } elseif (is_object($listener)) {
       
    57                 $typeDefinition = '[object] '.get_class($listener);
       
    58             } else {
       
    59                 $typeDefinition = '[?] '.var_export($listener, true);
       
    60             }
       
    61 
       
    62             throw new \RuntimeException(sprintf('The given callback (%s) for event "%s" is not callable.', $typeDefinition, $eventName));
       
    63         }
       
    64 
       
    65         parent::addListener($eventName, $listener, $priority);
       
    66     }
       
    67 
       
    68     /**
       
    69      * {@inheritDoc}
       
    70      */
       
    71     protected function doDispatch($listeners, $eventName, Event $event)
       
    72     {
       
    73         foreach ($listeners as $listener) {
       
    74             $info = $this->getListenerInfo($listener, $eventName);
       
    75 
       
    76             if (null !== $this->logger) {
       
    77                 $this->logger->debug(sprintf('Notified event "%s" to listener "%s".', $eventName, $info['pretty']));
       
    78             }
       
    79 
       
    80             $this->called[$eventName.'.'.$info['pretty']] = $info;
       
    81 
       
    82             call_user_func($listener, $event);
       
    83 
       
    84             if ($event->isPropagationStopped()) {
       
    85                 if (null !== $this->logger) {
       
    86                     $this->logger->debug(sprintf('Listener "%s" stopped propagation of the event "%s".', $info['pretty'], $eventName));
       
    87 
       
    88                     $skippedListeners = $this->getListeners($eventName);
       
    89                     $skipped = false;
       
    90 
       
    91                     foreach ($skippedListeners as $skippedListener) {
       
    92                         if ($skipped) {
       
    93                             if (is_object($skippedListener)) {
       
    94                                 $typeDefinition = get_class($skippedListener);
       
    95                             } elseif (is_array($skippedListener)) {
       
    96                                 if (is_object($skippedListener[0])) {
       
    97                                     $typeDefinition = get_class($skippedListener[0]);
       
    98                                 } else {
       
    99                                     $typeDefinition = implode('::', $skippedListener);
       
   100                                 }
       
   101                             } else {
       
   102                                 $typeDefinition = $skippedListener;
       
   103                             }
       
   104                             $this->logger->debug(sprintf('Listener "%s" was not called for event "%s".', $typeDefinition, $eventName));
       
   105                         }
       
   106 
       
   107                         if ($skippedListener === $listener) {
       
   108                             $skipped = true;
       
   109                         }
       
   110                     }
       
   111                 }
       
   112 
       
   113                 break;
       
   114             }
       
   115         }
       
   116     }
       
   117 
       
   118     /**
       
   119      * {@inheritDoc}
       
   120      */
       
   121     public function getCalledListeners()
       
   122     {
       
   123         return $this->called;
       
   124     }
       
   125 
       
   126     /**
       
   127      * {@inheritDoc}
       
   128      */
       
   129     public function getNotCalledListeners()
       
   130     {
       
   131         $notCalled = array();
       
   132 
       
   133         foreach ($this->getListeners() as $name => $listeners) {
       
   134             foreach ($listeners as $listener) {
       
   135                 $info = $this->getListenerInfo($listener, $name);
       
   136                 if (!isset($this->called[$name.'.'.$info['pretty']])) {
       
   137                     $notCalled[$name.'.'.$info['pretty']] = $info;
       
   138                 }
       
   139             }
       
   140         }
       
   141 
       
   142         return $notCalled;
       
   143     }
       
   144 
       
   145     /**
       
   146      * Returns information about the listener
       
   147      *
       
   148      * @param object $listener  The listener
       
   149      * @param string $eventName The event name
       
   150      *
       
   151      * @return array Informations about the listener
       
   152      */
       
   153     private function getListenerInfo($listener, $eventName)
       
   154     {
       
   155         $info = array('event' => $eventName);
       
   156         if ($listener instanceof \Closure) {
       
   157             $info += array(
       
   158                 'type' => 'Closure',
       
   159                 'pretty' => 'closure'
       
   160             );
       
   161         } elseif (is_string($listener)) {
       
   162             try {
       
   163                 $r = new \ReflectionFunction($listener);
       
   164                 $file = $r->getFileName();
       
   165                 $line = $r->getStartLine();
       
   166             } catch (\ReflectionException $e) {
       
   167                 $file = null;
       
   168                 $line = null;
       
   169             }
       
   170             $info += array(
       
   171                 'type'  => 'Function',
       
   172                 'function' => $listener,
       
   173                 'file'  => $file,
       
   174                 'line'  => $line,
       
   175                 'pretty' => $listener,
       
   176             );
       
   177         } elseif (is_array($listener) || (is_object($listener) && is_callable($listener))) {
       
   178             if (!is_array($listener)) {
       
   179                 $listener = array($listener, '__invoke');
       
   180             }
       
   181             $class = get_class($listener[0]);
       
   182             try {
       
   183                 $r = new \ReflectionMethod($class, $listener[1]);
       
   184                 $file = $r->getFileName();
       
   185                 $line = $r->getStartLine();
       
   186             } catch (\ReflectionException $e) {
       
   187                 $file = null;
       
   188                 $line = null;
       
   189             }
       
   190             $info += array(
       
   191                 'type'  => 'Method',
       
   192                 'class' => $class,
       
   193                 'method' => $listener[1],
       
   194                 'file'  => $file,
       
   195                 'line'  => $line,
       
   196                 'pretty' => $class.'::'.$listener[1],
       
   197             );
       
   198         }
       
   199 
       
   200         return $info;
       
   201     }
       
   202 }