2010-02-17 13:54:36 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/*
|
2010-04-07 01:51:29 +01:00
|
|
|
* This file is part of the Symfony package.
|
2010-02-17 13:54:36 +00:00
|
|
|
*
|
2011-03-06 11:40:06 +00:00
|
|
|
* (c) Fabien Potencier <fabien@symfony.com>
|
2010-02-17 13:54:36 +00:00
|
|
|
*
|
|
|
|
* For the full copyright and license information, please view the LICENSE
|
|
|
|
* file that was distributed with this source code.
|
|
|
|
*/
|
|
|
|
|
2011-01-15 13:29:43 +00:00
|
|
|
namespace Symfony\Bundle\FrameworkBundle\Debug;
|
|
|
|
|
Replaced EventDispatcher by Doctrine's EventManager implementation
Doctrine's EventManager implementation has several advantages over the
EventDispatcher implementation of Symfony2. Therefore I suggest that we
use their implementation.
Advantages:
* Event Listeners are objects, not callbacks. These objects have handler
methods that have the same name as the event. This helps a lot when
reading the code and makes the code for adding an event listener shorter.
* You can create Event Subscribers, which are event listeners with an
additional getSubscribedEvents() method. The benefit here is that the
code that registers the subscriber doesn't need to know about its
implementation.
* All events are defined in static Events classes, so users of IDEs benefit
of code completion
* The communication between the dispatching class of an event and all
listeners is done through a subclass of EventArgs. This subclass can be
tailored to the type of event. A constructor, setters and getters can be
implemented that verify the validity of the data set into the object.
See examples below.
* Because each event type corresponds to an EventArgs implementation,
developers of event listeners can look up the available EventArgs methods
and benefit of code completion.
* EventArgs::stopPropagation() is more flexible and (IMO) clearer to use
than notifyUntil(). Also, it is a concept that is also used in other
event implementations
Before:
class EventListener
{
public function handle(EventInterface $event, $data) { ... }
}
$dispatcher->connect('core.request', array($listener, 'handle'));
$dispatcher->notify('core.request', new Event(...));
After (with listeners):
final class Events
{
const onCoreRequest = 'onCoreRequest';
}
class EventListener
{
public function onCoreRequest(RequestEventArgs $eventArgs) { ... }
}
$evm->addEventListener(Events::onCoreRequest, $listener);
$evm->dispatchEvent(Events::onCoreRequest, new RequestEventArgs(...));
After (with subscribers):
class EventSubscriber
{
public function onCoreRequest(RequestEventArgs $eventArgs) { ... }
public function getSubscribedEvents()
{
return Events::onCoreRequest;
}
}
$evm->addEventSubscriber($subscriber);
$evm->dispatchEvent(Events::onCoreRequest, new RequestEventArgs(...));
2011-03-05 14:30:34 +00:00
|
|
|
use Doctrine\Common\EventManager;
|
2011-01-25 13:23:23 +00:00
|
|
|
use Symfony\Component\EventDispatcher\EventInterface;
|
2011-01-15 13:29:43 +00:00
|
|
|
use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
Replaced EventDispatcher by Doctrine's EventManager implementation
Doctrine's EventManager implementation has several advantages over the
EventDispatcher implementation of Symfony2. Therefore I suggest that we
use their implementation.
Advantages:
* Event Listeners are objects, not callbacks. These objects have handler
methods that have the same name as the event. This helps a lot when
reading the code and makes the code for adding an event listener shorter.
* You can create Event Subscribers, which are event listeners with an
additional getSubscribedEvents() method. The benefit here is that the
code that registers the subscriber doesn't need to know about its
implementation.
* All events are defined in static Events classes, so users of IDEs benefit
of code completion
* The communication between the dispatching class of an event and all
listeners is done through a subclass of EventArgs. This subclass can be
tailored to the type of event. A constructor, setters and getters can be
implemented that verify the validity of the data set into the object.
See examples below.
* Because each event type corresponds to an EventArgs implementation,
developers of event listeners can look up the available EventArgs methods
and benefit of code completion.
* EventArgs::stopPropagation() is more flexible and (IMO) clearer to use
than notifyUntil(). Also, it is a concept that is also used in other
event implementations
Before:
class EventListener
{
public function handle(EventInterface $event, $data) { ... }
}
$dispatcher->connect('core.request', array($listener, 'handle'));
$dispatcher->notify('core.request', new Event(...));
After (with listeners):
final class Events
{
const onCoreRequest = 'onCoreRequest';
}
class EventListener
{
public function onCoreRequest(RequestEventArgs $eventArgs) { ... }
}
$evm->addEventListener(Events::onCoreRequest, $listener);
$evm->dispatchEvent(Events::onCoreRequest, new RequestEventArgs(...));
After (with subscribers):
class EventSubscriber
{
public function onCoreRequest(RequestEventArgs $eventArgs) { ... }
public function getSubscribedEvents()
{
return Events::onCoreRequest;
}
}
$evm->addEventSubscriber($subscriber);
$evm->dispatchEvent(Events::onCoreRequest, new RequestEventArgs(...));
2011-03-05 14:30:34 +00:00
|
|
|
use Symfony\Component\HttpKernel\Debug\TraceableEventManagerInterface;
|
2011-01-23 17:02:16 +00:00
|
|
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
2011-01-15 13:29:43 +00:00
|
|
|
|
2010-02-17 13:54:36 +00:00
|
|
|
/**
|
|
|
|
* EventDispatcher extends the original EventDispatcher class to add some debugging tools.
|
|
|
|
*
|
2011-03-06 11:40:06 +00:00
|
|
|
* @author Fabien Potencier <fabien@symfony.com>
|
2010-02-17 13:54:36 +00:00
|
|
|
*/
|
Replaced EventDispatcher by Doctrine's EventManager implementation
Doctrine's EventManager implementation has several advantages over the
EventDispatcher implementation of Symfony2. Therefore I suggest that we
use their implementation.
Advantages:
* Event Listeners are objects, not callbacks. These objects have handler
methods that have the same name as the event. This helps a lot when
reading the code and makes the code for adding an event listener shorter.
* You can create Event Subscribers, which are event listeners with an
additional getSubscribedEvents() method. The benefit here is that the
code that registers the subscriber doesn't need to know about its
implementation.
* All events are defined in static Events classes, so users of IDEs benefit
of code completion
* The communication between the dispatching class of an event and all
listeners is done through a subclass of EventArgs. This subclass can be
tailored to the type of event. A constructor, setters and getters can be
implemented that verify the validity of the data set into the object.
See examples below.
* Because each event type corresponds to an EventArgs implementation,
developers of event listeners can look up the available EventArgs methods
and benefit of code completion.
* EventArgs::stopPropagation() is more flexible and (IMO) clearer to use
than notifyUntil(). Also, it is a concept that is also used in other
event implementations
Before:
class EventListener
{
public function handle(EventInterface $event, $data) { ... }
}
$dispatcher->connect('core.request', array($listener, 'handle'));
$dispatcher->notify('core.request', new Event(...));
After (with listeners):
final class Events
{
const onCoreRequest = 'onCoreRequest';
}
class EventListener
{
public function onCoreRequest(RequestEventArgs $eventArgs) { ... }
}
$evm->addEventListener(Events::onCoreRequest, $listener);
$evm->dispatchEvent(Events::onCoreRequest, new RequestEventArgs(...));
After (with subscribers):
class EventSubscriber
{
public function onCoreRequest(RequestEventArgs $eventArgs) { ... }
public function getSubscribedEvents()
{
return Events::onCoreRequest;
}
}
$evm->addEventSubscriber($subscriber);
$evm->dispatchEvent(Events::onCoreRequest, new RequestEventArgs(...));
2011-03-05 14:30:34 +00:00
|
|
|
class TraceableEventManager extends EventManager implements TraceableEventManagerInterface
|
2010-02-17 13:54:36 +00:00
|
|
|
{
|
2010-05-06 12:25:53 +01:00
|
|
|
protected $logger;
|
2010-08-27 10:24:30 +01:00
|
|
|
protected $called;
|
2010-05-06 12:25:53 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor.
|
|
|
|
*
|
2011-01-23 17:02:16 +00:00
|
|
|
* @param ContainerInterface $container A ContainerInterface instance
|
|
|
|
* @param LoggerInterface $logger A LoggerInterface instance
|
2010-05-06 12:25:53 +01:00
|
|
|
*/
|
2011-01-23 17:02:16 +00:00
|
|
|
public function __construct(ContainerInterface $container, LoggerInterface $logger = null)
|
2010-02-17 13:54:36 +00:00
|
|
|
{
|
2011-01-23 17:02:16 +00:00
|
|
|
parent::__construct($container);
|
|
|
|
|
2010-05-06 12:25:53 +01:00
|
|
|
$this->logger = $logger;
|
2010-08-27 10:24:30 +01:00
|
|
|
$this->called = array();
|
2010-02-17 13:54:36 +00:00
|
|
|
}
|
|
|
|
|
2010-05-06 12:25:53 +01:00
|
|
|
/**
|
2011-01-09 10:25:50 +00:00
|
|
|
* {@inheritDoc}
|
2010-05-06 12:25:53 +01:00
|
|
|
*/
|
2011-01-25 13:23:23 +00:00
|
|
|
public function notify(EventInterface $event)
|
2010-02-17 13:54:36 +00:00
|
|
|
{
|
2010-05-07 15:09:11 +01:00
|
|
|
foreach ($this->getListeners($event->getName()) as $listener) {
|
2011-01-24 15:46:04 +00:00
|
|
|
if (is_array($listener) && is_string($listener[0])) {
|
|
|
|
$listener[0] = $this->container->get($listener[0]);
|
|
|
|
}
|
|
|
|
|
2010-08-27 10:24:30 +01:00
|
|
|
$this->addCall($event, $listener, 'notify');
|
2010-05-06 12:25:53 +01:00
|
|
|
|
|
|
|
call_user_func($listener, $event);
|
2010-02-17 13:54:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-06 12:25:53 +01:00
|
|
|
/**
|
2011-01-09 10:25:50 +00:00
|
|
|
* {@inheritDoc}
|
2010-05-06 12:25:53 +01:00
|
|
|
*/
|
2011-01-25 13:23:23 +00:00
|
|
|
public function notifyUntil(EventInterface $event)
|
2010-02-17 13:54:36 +00:00
|
|
|
{
|
2010-08-26 10:18:15 +01:00
|
|
|
foreach ($this->getListeners($event->getName()) as $i => $listener) {
|
2011-01-24 15:46:04 +00:00
|
|
|
if (is_array($listener) && is_string($listener[0])) {
|
|
|
|
$listener[0] = $this->container->get($listener[0]);
|
|
|
|
}
|
|
|
|
|
2010-08-27 10:24:30 +01:00
|
|
|
$this->addCall($event, $listener, 'notifyUntil');
|
2010-05-06 12:25:53 +01:00
|
|
|
|
2011-01-26 06:50:06 +00:00
|
|
|
$ret = call_user_func($listener, $event);
|
|
|
|
if ($event->isProcessed()) {
|
2010-05-08 14:32:30 +01:00
|
|
|
if (null !== $this->logger) {
|
2010-05-06 12:25:53 +01:00
|
|
|
$this->logger->debug(sprintf('Listener "%s" processed the event "%s"', $this->listenerToString($listener), $event->getName()));
|
2010-08-26 10:18:15 +01:00
|
|
|
|
|
|
|
$listeners = $this->getListeners($event->getName());
|
|
|
|
while (++$i < count($listeners)) {
|
|
|
|
$this->logger->debug(sprintf('Listener "%s" was not called for event "%s"', $this->listenerToString($listeners[$i]), $event->getName()));
|
|
|
|
}
|
2010-05-06 12:25:53 +01:00
|
|
|
}
|
|
|
|
|
2011-01-26 06:50:06 +00:00
|
|
|
return $ret;
|
2010-05-06 12:25:53 +01:00
|
|
|
}
|
|
|
|
}
|
2010-02-17 13:54:36 +00:00
|
|
|
}
|
|
|
|
|
2010-05-06 12:25:53 +01:00
|
|
|
/**
|
2011-01-09 10:25:50 +00:00
|
|
|
* {@inheritDoc}
|
2010-05-06 12:25:53 +01:00
|
|
|
*/
|
2011-01-25 13:23:23 +00:00
|
|
|
public function filter(EventInterface $event, $value)
|
2010-05-06 12:25:53 +01:00
|
|
|
{
|
2010-05-07 15:09:11 +01:00
|
|
|
foreach ($this->getListeners($event->getName()) as $listener) {
|
2011-01-24 15:46:04 +00:00
|
|
|
if (is_array($listener) && is_string($listener[0])) {
|
|
|
|
$listener[0] = $this->container->get($listener[0]);
|
|
|
|
}
|
|
|
|
|
2010-08-27 10:24:30 +01:00
|
|
|
$this->addCall($event, $listener, 'filter');
|
2010-02-17 13:54:36 +00:00
|
|
|
|
2010-05-06 12:25:53 +01:00
|
|
|
$value = call_user_func($listener, $event, $value);
|
|
|
|
}
|
2010-02-17 13:54:36 +00:00
|
|
|
|
2011-01-26 06:50:06 +00:00
|
|
|
return $value;
|
2010-02-17 13:54:36 +00:00
|
|
|
}
|
|
|
|
|
2011-01-09 10:25:50 +00:00
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public function getCalledListeners()
|
2010-08-27 10:24:30 +01:00
|
|
|
{
|
|
|
|
return $this->called;
|
|
|
|
}
|
|
|
|
|
2011-01-09 10:25:50 +00:00
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public function getNotCalledListeners()
|
2010-08-27 10:24:30 +01:00
|
|
|
{
|
|
|
|
$notCalled = array();
|
|
|
|
|
|
|
|
foreach (array_keys($this->listeners) as $name) {
|
|
|
|
foreach ($this->getListeners($name) as $listener) {
|
2011-01-24 15:46:04 +00:00
|
|
|
if (is_array($listener) && is_string($listener[0])) {
|
|
|
|
$listener[0] = $this->container->get($listener[0]);
|
|
|
|
}
|
|
|
|
|
2010-08-27 10:24:30 +01:00
|
|
|
$listener = $this->listenerToString($listener);
|
|
|
|
|
|
|
|
if (!isset($this->called[$name.'.'.$listener])) {
|
|
|
|
$notCalled[] = array(
|
|
|
|
'event' => $name,
|
|
|
|
'listener' => $listener,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $notCalled;
|
|
|
|
}
|
|
|
|
|
2010-05-06 12:25:53 +01:00
|
|
|
protected function listenerToString($listener)
|
2010-02-17 13:54:36 +00:00
|
|
|
{
|
2010-05-07 15:09:11 +01:00
|
|
|
if (is_object($listener) && $listener instanceof \Closure) {
|
2010-05-06 12:25:53 +01:00
|
|
|
return 'Closure';
|
|
|
|
}
|
|
|
|
|
2010-05-07 15:09:11 +01:00
|
|
|
if (is_string($listener)) {
|
2010-05-06 12:25:53 +01:00
|
|
|
return $listener;
|
|
|
|
}
|
|
|
|
|
2010-05-07 15:09:11 +01:00
|
|
|
if (is_array($listener)) {
|
2010-08-27 10:24:30 +01:00
|
|
|
return sprintf('%s::%s', is_object($listener[0]) ? get_class($listener[0]) : $listener[0], $listener[1]);
|
2010-05-06 12:25:53 +01:00
|
|
|
}
|
2010-02-17 13:54:36 +00:00
|
|
|
}
|
2010-08-27 10:24:30 +01:00
|
|
|
|
2011-01-25 13:23:23 +00:00
|
|
|
protected function addCall(EventInterface $event, $listener, $type)
|
2010-08-27 10:24:30 +01:00
|
|
|
{
|
|
|
|
$listener = $this->listenerToString($listener);
|
|
|
|
|
|
|
|
if (null !== $this->logger) {
|
|
|
|
$this->logger->debug(sprintf('Notified event "%s" to listener "%s" (%s)', $event->getName(), $listener, $type));
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->called[$event->getName().'.'.$listener] = array(
|
|
|
|
'event' => $event->getName(),
|
|
|
|
'caller' => null !== $event->getSubject() ? get_class($event->getSubject()) : null,
|
|
|
|
'listener' => $listener,
|
|
|
|
);
|
|
|
|
}
|
2010-02-17 13:54:36 +00:00
|
|
|
}
|