feature #10388 [FrameworkBundle] [Command] Event Dispatcher Debug - Display registered listeners (matthieuauger)

This PR was merged into the 2.6-dev branch.

Discussion
----------

[FrameworkBundle] [Command] Event Dispatcher Debug - Display registered listeners

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT

------------------------------------------
[Update] The PR has been updated in order to comply with @stof comments.

Current status :
- [x] New event dispatcher Descriptor
- [x] Manage all callables
- [x] Unit tests
- [x] Text description
- [x] XML description
- [x] Json description
- [x] Markdown description

-----------------------------------------
Hi. In some big applications with lots of events, it's often hard to debug which classes listen to which events, and what is the order of theses listeners. This PR allows to run

- *event-dispatcher:debug* which displays all configured listeners + the events they listen to

![capture d cran de 2014-03-07 20 13 56](https://f.cloud.github.com/assets/1172099/2361104/40a86a62-a62d-11e3-9ccd-360a8d75b2a4.png)

- *event-dispatcher:debug* **event** which displays configured listeners for this specific event (order by priority desc)

![capture d cran de 2014-03-07 20 14 31](https://f.cloud.github.com/assets/1172099/2361100/31e0d12c-a62d-11e3-963b-87623d05642c.png)

The output is similar to *container:debug* command and is available in all supported formats (txt, xml, json and markdown).

I found another PR with same goal (#8234), but the approach looks too complicated to me plus I think we should fetch the listeners directly with the event_dispatcher.

Commits
-------

ce53c8a [FrameworkBundle] Add Event Dispatcher debug command
This commit is contained in:
Fabien Potencier 2014-09-15 22:12:45 +02:00
commit 9046c483ed
44 changed files with 778 additions and 0 deletions

View File

@ -0,0 +1,84 @@
<?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\Command;
use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* A console command for retrieving information about event dispatcher
*
* @author Matthieu Auger <mail@matthieuauger.com>
*/
class EventDispatcherDebugCommand extends ContainerAwareCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('debug:event-dispatcher')
->setDefinition(array(
new InputArgument('event', InputArgument::OPTIONAL, 'An event name (foo)'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'To output description in other formats', 'txt'),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw description'),
))
->setDescription('Displays configured listeners for an application')
->setHelp(<<<EOF
The <info>%command.name%</info> command displays all configured listeners:
<info>php %command.full_name%</info>
To get specific listeners for an event, specify its name:
<info>php %command.full_name% kernel.request</info>
EOF
)
;
}
/**
* {@inheritdoc}
*
* @throws \LogicException
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
if ($event = $input->getArgument('event')) {
$options = array('event' => $event);
} else {
$options = array();
}
$dispatcher = $this->getEventDispatcher();
$helper = new DescriptorHelper();
$options['format'] = $input->getOption('format');
$options['raw_text'] = $input->getOption('raw');
$helper->describe($output, $dispatcher, $options);
}
/**
* Loads the Event Dispatcher from the container
*
* @return EventDispatcherInterface
*/
protected function getEventDispatcher()
{
return $this->getContainer()->get('event_dispatcher');
}
}

View File

@ -18,6 +18,7 @@ use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollection;
@ -66,6 +67,12 @@ abstract class Descriptor implements DescriptorInterface
case $object instanceof Alias: case $object instanceof Alias:
$this->describeContainerAlias($object, $options); $this->describeContainerAlias($object, $options);
break; break;
case $object instanceof EventDispatcherInterface:
$this->describeEventDispatcherListeners($object, $options);
break;
case is_callable($object):
$this->describeCallable($object, $options);
break;
default: default:
throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object))); throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object)));
} }
@ -176,6 +183,25 @@ abstract class Descriptor implements DescriptorInterface
*/ */
abstract protected function describeContainerParameter($parameter, array $options = array()); abstract protected function describeContainerParameter($parameter, array $options = array());
/**
* Describes event dispatcher listeners.
*
* Common options are:
* * name: name of listened event
*
* @param EventDispatcherInterface $eventDispatcher
* @param array $options
*/
abstract protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = array());
/**
* Describes a callable.
*
* @param callable $callable
* @param array $options
*/
abstract protected function describeCallable($callable, array $options = array());
/** /**
* Formats a value as string. * Formats a value as string.
* *

View File

@ -19,6 +19,7 @@ use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollection;
@ -134,6 +135,22 @@ class JsonDescriptor extends Descriptor
$this->writeData($this->getContainerAliasData($alias), $options); $this->writeData($this->getContainerAliasData($alias), $options);
} }
/**
* {@inheritdoc}
*/
protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = array())
{
$this->writeData($this->getEventDispatcherListenersData($eventDispatcher, array_key_exists('event', $options) ? $options['event'] : null), $options);
}
/**
* {@inheritdoc}
*/
protected function describeCallable($callable, array $options = array())
{
$this->writeData($this->getCallableData($callable, $options), $options);
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -222,4 +239,96 @@ class JsonDescriptor extends Descriptor
'public' => $alias->isPublic(), 'public' => $alias->isPublic(),
); );
} }
/**
* @param EventDispatcherInterface $eventDispatcher
* @param string|null $event
*
* @return array
*/
private function getEventDispatcherListenersData(EventDispatcherInterface $eventDispatcher, $event = null)
{
$data = array();
$registeredListeners = $eventDispatcher->getListeners($event);
if (null !== $event) {
foreach ($registeredListeners as $listener) {
$data[] = $this->getCallableData($listener);
}
} else {
ksort($registeredListeners);
foreach ($registeredListeners as $eventListened => $eventListeners) {
foreach ($eventListeners as $eventListener) {
$data[$eventListened][] = $this->getCallableData($eventListener);
}
}
}
return $data;
}
/**
* @param callable $callable
* @param array $options
*
* @return array
*/
private function getCallableData($callable, array $options = array())
{
$data = array();
if (is_array($callable)) {
$data['type'] = 'function';
if (is_object($callable[0])) {
$data['name'] = $callable[1];
$data['class'] = get_class($callable[0]);
} else {
if (0 !== strpos($callable[1], 'parent::')) {
$data['name'] = $callable[1];
$data['class'] = $callable[0];
$data['static'] = true;
} else {
$data['name'] = substr($callable[1], 8);
$data['class'] = $callable[0];
$data['static'] = true;
$data['parent'] = true;
}
}
return $data;
}
if (is_string($callable)) {
$data['type'] = 'function';
if (false === strpos($callable, '::')) {
$data['name'] = $callable;
} else {
$callableParts = explode('::', $callable);
$data['name'] = $callableParts[1];
$data['class'] = $callableParts[0];
$data['static'] = true;
}
return $data;
}
if ($callable instanceof \Closure) {
$data['type'] = 'closure';
return $data;
}
if (method_exists($callable, '__invoke')) {
$data['type'] = 'object';
$data['name'] = get_class($callable);
return $data;
}
throw new \InvalidArgumentException('Callable is not describable.');
}
} }

View File

@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollection;
@ -215,6 +216,106 @@ class MarkdownDescriptor extends Descriptor
$this->write(isset($options['parameter']) ? sprintf("%s\n%s\n\n%s", $options['parameter'], str_repeat('=', strlen($options['parameter'])), $this->formatParameter($parameter)): $parameter); $this->write(isset($options['parameter']) ? sprintf("%s\n%s\n\n%s", $options['parameter'], str_repeat('=', strlen($options['parameter'])), $this->formatParameter($parameter)): $parameter);
} }
/**
* {@inheritdoc}
*/
protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = array())
{
$event = array_key_exists('event', $options) ? $options['event'] : null;
$title = 'Registered listeners';
if (null !== $event) {
$title .= sprintf(' for event `%s` ordered by descending priority', $event);
}
$this->write(sprintf('# %s', $title)."\n");
$registeredListeners = $eventDispatcher->getListeners($event);
if (null !== $event) {
foreach ($registeredListeners as $order => $listener) {
$this->write("\n".sprintf('## Listener %d', $order + 1)."\n");
$this->describeCallable($listener);
}
} else {
ksort($registeredListeners);
foreach ($registeredListeners as $eventListened => $eventListeners) {
$this->write("\n".sprintf('## %s', $eventListened)."\n");
foreach ($eventListeners as $order => $eventListener) {
$this->write("\n".sprintf('### Listener %d', $order + 1)."\n");
$this->describeCallable($eventListener);
}
}
}
}
/**
* {@inheritdoc}
*/
protected function describeCallable($callable, array $options = array())
{
$string = '';
if (is_array($callable)) {
$string .= "\n- Type: `function`";
if (is_object($callable[0])) {
$string .= "\n".sprintf('- Name: `%s`', $callable[1]);
$string .= "\n".sprintf('- Class: `%s`', get_class($callable[0]));
} else {
if (0 !== strpos($callable[1], 'parent::')) {
$string .= "\n".sprintf('- Name: `%s`', $callable[1]);
$string .= "\n".sprintf('- Class: `%s`', $callable[0]);
$string .= "\n- Static: yes";
} else {
$string .= "\n".sprintf('- Name: `%s`', substr($callable[1], 8));
$string .= "\n".sprintf('- Class: `%s`', $callable[0]);
$string .= "\n- Static: yes";
$string .= "\n- Parent: yes";
}
}
return $this->write($string."\n");
}
if (is_string($callable)) {
$string .= "\n- Type: `function`";
if (false === strpos($callable, '::')) {
$string .= "\n".sprintf('- Name: `%s`', $callable);
} else {
$callableParts = explode('::', $callable);
$string .= "\n".sprintf('- Name: `%s`', $callableParts[1]);
$string .= "\n".sprintf('- Class: `%s`', $callableParts[0]);
$string .= "\n- Static: yes";
}
return $this->write($string."\n");
}
if ($callable instanceof \Closure) {
$string .= "\n- Type: `closure`";
return $this->write($string."\n");
}
if (method_exists($callable, '__invoke')) {
$string .= "\n- Type: `object`";
$string .= "\n".sprintf('- Name: `%s`', get_class($callable));
return $this->write($string."\n");
}
throw new \InvalidArgumentException('Callable is not describable.');
}
/**
* @param array $array
*
* @return string
*/
private function formatRouterConfig(array $array) private function formatRouterConfig(array $array)
{ {
if (!count($array)) { if (!count($array)) {

View File

@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollection;
@ -284,6 +285,58 @@ class TextDescriptor extends Descriptor
$this->writeText($this->formatParameter($parameter), $options); $this->writeText($this->formatParameter($parameter), $options);
} }
/**
* {@inheritdoc}
*/
protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = array())
{
$event = array_key_exists('event', $options) ? $options['event'] : null;
$label = 'Registered listeners';
if (null !== $event) {
$label .= sprintf(' for event <info>%s</info>', $event);
} else {
$label .= ' by event';
}
$this->writeText($this->formatSection('event_dispatcher', $label)."\n", $options);
$registeredListeners = $eventDispatcher->getListeners($event);
if (null !== $event) {
$this->writeText("\n");
$table = new TableHelper();
$table->setHeaders(array('Order', 'Callable'));
foreach ($registeredListeners as $order => $listener) {
$table->addRow(array(sprintf('#%d', $order + 1), $this->formatCallable($listener)));
}
$this->renderTable($table);
} else {
ksort($registeredListeners);
foreach ($registeredListeners as $eventListened => $eventListeners) {
$this->writeText(sprintf("\n<info>[Event]</info> %s\n", $eventListened), $options);
$table = new TableHelper();
$table->setHeaders(array('Order', 'Callable'));
foreach ($eventListeners as $order => $eventListener) {
$table->addRow(array(sprintf('#%d', $order + 1), $this->formatCallable($eventListener)));
}
$this->renderTable($table);
}
}
}
/**
* {@inheritdoc}
*/
protected function describeCallable($callable, array $options = array())
{
$this->writeText($this->formatCallable($callable), $options);
}
/** /**
* @param array $array * @param array $array
* *
@ -311,6 +364,36 @@ class TextDescriptor extends Descriptor
return sprintf('<info>[%s]</info> %s', $section, $message); return sprintf('<info>[%s]</info> %s', $section, $message);
} }
/**
* @param callable $callable
*
* @return string
*/
private function formatCallable($callable)
{
if (is_array($callable)) {
if (is_object($callable[0])) {
return sprintf('%s::%s()', get_class($callable[0]), $callable[1]);
}
return sprintf('%s::%s()', $callable[0], $callable[1]);
}
if (is_string($callable)) {
return sprintf('%s()', $callable);
}
if ($callable instanceof \Closure) {
return '\Closure()';
}
if (method_exists($callable, '__invoke')) {
return sprintf('%s::__invoke()', get_class($callable));
}
throw new \InvalidArgumentException('Callable is not describable.');
}
/** /**
* @param string $content * @param string $content
* @param array $options * @param array $options

View File

@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollection;
@ -91,6 +92,22 @@ class XmlDescriptor extends Descriptor
$this->writeDocument($this->getContainerAliasDocument($alias, isset($options['id']) ? $options['id'] : null)); $this->writeDocument($this->getContainerAliasDocument($alias, isset($options['id']) ? $options['id'] : null));
} }
/**
* {@inheritdoc}
*/
protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = array())
{
$this->writeDocument($this->getEventDispatcherListenersDocument($eventDispatcher, array_key_exists('event', $options) ? $options['event'] : null));
}
/**
* {@inheritdoc}
*/
protected function describeCallable($callable, array $options = array())
{
$this->writeDocument($this->getCallableDocument($callable));
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -393,4 +410,104 @@ class XmlDescriptor extends Descriptor
return $dom; return $dom;
} }
/**
* @param EventDispatcherInterface $eventDispatcher
* @param string|null $event
*
* @return \DOMDocument
*/
private function getEventDispatcherListenersDocument(EventDispatcherInterface $eventDispatcher, $event = null)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($eventDispatcherXML = $dom->createElement('event-dispatcher'));
$registeredListeners = $eventDispatcher->getListeners($event);
if (null !== $event) {
foreach ($registeredListeners as $listener) {
$callableXML = $this->getCallableDocument($listener);
$eventDispatcherXML->appendChild($eventDispatcherXML->ownerDocument->importNode($callableXML->childNodes->item(0), true));
}
} else {
ksort($registeredListeners);
foreach ($registeredListeners as $eventListened => $eventListeners) {
$eventDispatcherXML->appendChild($eventXML = $dom->createElement('event'));
$eventXML->setAttribute('name', $eventListened);
foreach ($eventListeners as $eventListener) {
$callableXML = $this->getCallableDocument($eventListener);
$eventXML->appendChild($eventXML->ownerDocument->importNode($callableXML->childNodes->item(0), true));
}
}
}
return $dom;
}
/**
* @param callable $callable
*
* @return \DOMDocument
*/
private function getCallableDocument($callable)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($callableXML = $dom->createElement('callable'));
if (is_array($callable)) {
$callableXML->setAttribute('type', 'function');
if (is_object($callable[0])) {
$callableXML->setAttribute('name', $callable[1]);
$callableXML->setAttribute('class', get_class($callable[0]));
} else {
if (0 !== strpos($callable[1], 'parent::')) {
$callableXML->setAttribute('name', $callable[1]);
$callableXML->setAttribute('class', $callable[0]);
$callableXML->setAttribute('static', 'true');
} else {
$callableXML->setAttribute('name', substr($callable[1], 8));
$callableXML->setAttribute('class', $callable[0]);
$callableXML->setAttribute('static', 'true');
$callableXML->setAttribute('parent', 'true');
}
}
return $dom;
}
if (is_string($callable)) {
$callableXML->setAttribute('type', 'function');
if (false === strpos($callable, '::')) {
$callableXML->setAttribute('name', $callable);
} else {
$callableParts = explode('::', $callable);
$callableXML->setAttribute('name', $callableParts[1]);
$callableXML->setAttribute('class', $callableParts[0]);
$callableXML->setAttribute('static', 'true');
}
return $dom;
}
if ($callable instanceof \Closure) {
$callableXML->setAttribute('type', 'closure');
return $dom;
}
if (method_exists($callable, '__invoke')) {
$callableXML->setAttribute('type', 'object');
$callableXML->setAttribute('name', get_class($callable));
return $dom;
}
throw new \InvalidArgumentException('Callable is not describable.');
}
} }

View File

@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollection;
@ -102,6 +103,28 @@ abstract class AbstractDescriptorTest extends \PHPUnit_Framework_TestCase
return $data; return $data;
} }
/** @dataProvider getDescribeEventDispatcherTestData */
public function testDescribeEventDispatcher(EventDispatcher $eventDispatcher, $expectedDescription, array $options)
{
$this->assertDescription($expectedDescription, $eventDispatcher, $options);
}
public function getDescribeEventDispatcherTestData()
{
return $this->getEventDispatcherDescriptionTestData(ObjectsProvider::getEventDispatchers());
}
/** @dataProvider getDescribeCallableTestData */
public function testDescribeCallable($callable, $expectedDescription)
{
$this->assertDescription($expectedDescription, $callable);
}
public function getDescribeCallableTestData()
{
return $this->getDescriptionTestData(ObjectsProvider::getCallables());
}
abstract protected function getDescriptor(); abstract protected function getDescriptor();
abstract protected function getFormat(); abstract protected function getFormat();
@ -148,4 +171,22 @@ abstract class AbstractDescriptorTest extends \PHPUnit_Framework_TestCase
return $data; return $data;
} }
private function getEventDispatcherDescriptionTestData(array $objects)
{
$variations = array(
'events' => array(),
'event1' => array('event' => 'event1'),
);
$data = array();
foreach ($objects as $name => $object) {
foreach ($variations as $suffix => $options) {
$description = file_get_contents(sprintf('%s/../../Fixtures/Descriptor/%s_%s.%s', __DIR__, $name, $suffix, $this->getFormat()));
$data[] = array($object, $description, $options);
}
}
return $data;
}
} }

View File

@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollection;
@ -121,4 +122,48 @@ class ObjectsProvider
'alias_2' => new Alias('service_2', false), 'alias_2' => new Alias('service_2', false),
); );
} }
public static function getEventDispatchers()
{
$eventDispatcher = new EventDispatcher();
$eventDispatcher->addListener('event1', 'global_function');
$eventDispatcher->addListener('event1', function () { return 'Closure'; });
$eventDispatcher->addListener('event2', new CallableClass());
return array('event_dispatcher_1' => $eventDispatcher);
}
public static function getCallables()
{
return array(
'callable_1' => 'array_key_exists',
'callable_2' => array('Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass', 'staticMethod'),
'callable_3' => array(new CallableClass(), 'method'),
'callable_4' => 'Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass::staticMethod',
'callable_5' => array('Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\ExtendedCallableClass', 'parent::staticMethod'),
'callable_6' => function () { return 'Closure'; },
'callable_7' => new CallableClass()
);
}
}
class CallableClass
{
public function __invoke()
{
}
public static function staticMethod()
{
}
public function method()
{
}
}
class ExtendedCallableClass extends CallableClass
{
public static function staticMethod()
{
}
} }

View File

@ -0,0 +1,4 @@
{
"type": "function",
"name": "array_key_exists"
}

View File

@ -0,0 +1,2 @@
- Type: `function`
- Name: `array_key_exists`

View File

@ -0,0 +1 @@
array_key_exists()

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<callable type="function" name="array_key_exists"/>

View File

@ -0,0 +1,6 @@
{
"type": "function",
"name": "staticMethod",
"class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass",
"static": true
}

View File

@ -0,0 +1,4 @@
- Type: `function`
- Name: `staticMethod`
- Class: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass`
- Static: yes

View File

@ -0,0 +1 @@
Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass::staticMethod()

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<callable type="function" name="staticMethod" class="Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass" static="true"/>

View File

@ -0,0 +1,5 @@
{
"type": "function",
"name": "method",
"class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass"
}

View File

@ -0,0 +1,3 @@
- Type: `function`
- Name: `method`
- Class: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass`

View File

@ -0,0 +1 @@
Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass::method()

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<callable type="function" name="method" class="Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass"/>

View File

@ -0,0 +1,6 @@
{
"type": "function",
"name": "staticMethod",
"class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass",
"static": true
}

View File

@ -0,0 +1,4 @@
- Type: `function`
- Name: `staticMethod`
- Class: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass`
- Static: yes

View File

@ -0,0 +1 @@
Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass::staticMethod()

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<callable type="function" name="staticMethod" class="Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass" static="true"/>

View File

@ -0,0 +1,7 @@
{
"type": "function",
"name": "staticMethod",
"class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\ExtendedCallableClass",
"static": true,
"parent": true
}

View File

@ -0,0 +1,5 @@
- Type: `function`
- Name: `staticMethod`
- Class: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\ExtendedCallableClass`
- Static: yes
- Parent: yes

View File

@ -0,0 +1 @@
Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\ExtendedCallableClass::parent::staticMethod()

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<callable type="function" name="staticMethod" class="Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\ExtendedCallableClass" static="true" parent="true"/>

View File

@ -0,0 +1,3 @@
{
"type": "closure"
}

View File

@ -0,0 +1 @@
- Type: `closure`

View File

@ -0,0 +1 @@
\Closure()

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<callable type="closure"/>

View File

@ -0,0 +1,4 @@
{
"type": "object",
"name": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass"
}

View File

@ -0,0 +1,3 @@
- Type: `object`
- Name: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass`

View File

@ -0,0 +1 @@
Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass::__invoke()

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<callable type="object" name="Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass"/>

View File

@ -0,0 +1,9 @@
[
{
"type": "function",
"name": "global_function"
},
{
"type": "closure"
}
]

View File

@ -0,0 +1,10 @@
# Registered listeners for event `event1` ordered by descending priority
## Listener 1
- Type: `function`
- Name: `global_function`
## Listener 2
- Type: `closure`

View File

@ -0,0 +1,8 @@
<info>[event_dispatcher]</info> Registered listeners for event <info>event1</info>
+-------+-------------------+
| Order | Callable |
+-------+-------------------+
| #1 | global_function() |
| #2 | \Closure() |
+-------+-------------------+

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<event-dispatcher>
<callable type="function" name="global_function"/>
<callable type="closure"/>
</event-dispatcher>

View File

@ -0,0 +1,17 @@
{
"event1": [
{
"type": "function",
"name": "global_function"
},
{
"type": "closure"
}
],
"event2": [
{
"type": "object",
"name": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass"
}
]
}

View File

@ -0,0 +1,19 @@
# Registered listeners
## event1
### Listener 1
- Type: `function`
- Name: `global_function`
### Listener 2
- Type: `closure`
## event2
### Listener 1
- Type: `object`
- Name: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass`

View File

@ -0,0 +1,16 @@
<info>[event_dispatcher]</info> Registered listeners by event
<info>[Event]</info> event1
+-------+-------------------+
| Order | Callable |
+-------+-------------------+
| #1 | global_function() |
| #2 | \Closure() |
+-------+-------------------+
<info>[Event]</info> event2
+-------+-----------------------------------------------------------------------------------+
| Order | Callable |
+-------+-----------------------------------------------------------------------------------+
| #1 | Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass::__invoke() |
+-------+-----------------------------------------------------------------------------------+

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<event-dispatcher>
<event name="event1">
<callable type="function" name="global_function"/>
<callable type="closure"/>
</event>
<event name="event2">
<callable type="object" name="Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass"/>
</event>
</event-dispatcher>