Skip to content

Add listener config to EventManager #83

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: 2.0.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
<file>src</file>
<file>tests</file>

<rule ref="Doctrine" />
<rule ref="Doctrine">
<exclude name="Squiz.Commenting.FunctionComment.ExtraParamComment" />
</rule>

<rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint">
<!-- This interface is commonly implemented by userland code.
Expand Down
2 changes: 2 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ parameters:
paths:
- src/
- tests/
ignoreErrors:
- identifier: parameter.notFound
30 changes: 24 additions & 6 deletions src/EventManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Doctrine\Common;

use function func_get_arg;
use function func_num_args;
use function spl_object_hash;

/**
Expand All @@ -21,6 +23,14 @@ class EventManager
*/
private array $listeners = [];

/**
* Map of registered listener configurations.
* <hash><event> => <configuration>
*
* @var array<string, array<string, array{method?: string}>>
*/
private array $listenerConfigs = [];

/**
* Dispatches an event to all registered listeners.
*
Expand All @@ -37,8 +47,10 @@ public function dispatchEvent(string $eventName, EventArgs|null $eventArgs = nul

$eventArgs ??= EventArgs::getEmptyInstance();

foreach ($this->listeners[$eventName] as $listener) {
$listener->$eventName($eventArgs);
foreach ($this->listeners[$eventName] as $hash => $listener) {
$method = $this->listenerConfigs[$hash][$eventName]['method'] ?? $eventName;

$listener->$method($eventArgs);
}
}

Expand Down Expand Up @@ -75,18 +87,23 @@ public function hasListeners(string $event): bool
/**
* Adds an event listener that listens on the specified events.
*
* @param string|string[] $events The event(s) to listen on.
* @param object $listener The listener object.
* @param string|string[] $events The event(s) to listen on.
* @param object $listener The listener object.
* @param array<string, array{method?: string}> $listenerConfig The listener configuration, indexed by event.
*/
public function addEventListener(string|array $events, object $listener): void
public function addEventListener(string|array $events, object $listener, /* array $listenerConfig = [] */): void
{
/** @var array<string, array{method?: string}> $listenerConfig */
$listenerConfig = 3 <= func_num_args() ? func_get_arg(2) : [];

// Picks the hash code related to that listener
$hash = spl_object_hash($listener);

foreach ((array) $events as $event) {
// Overrides listener if a previous one was associated already
// Prevents duplicate listeners on same event (same instance only)
$this->listeners[$event][$hash] = $listener;
$this->listeners[$event][$hash] = $listener;
$this->listenerConfigs[$hash][$event] = $listenerConfig[$event] ?? [];
}
}

Expand All @@ -100,6 +117,7 @@ public function removeEventListener(string|array $events, object $listener): voi
// Picks the hash code related to that listener
$hash = spl_object_hash($listener);

unset($this->listenerConfigs[$hash]);
foreach ((array) $events as $event) {
unset($this->listeners[$event][$hash]);
}
Expand Down
30 changes: 28 additions & 2 deletions tests/EventManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ class EventManagerTest extends TestCase
private const POST_FOO = 'postFoo';
private const PRE_BAR = 'preBar';

private bool $preFooInvoked = false;
private bool $postFooInvoked = false;
private bool $preFooInvoked = false;
private bool $postFooInvoked = false;
private bool $customMethodInvoked = false;
private EventManager $eventManager;

protected function setUp(): void
Expand Down Expand Up @@ -48,6 +49,17 @@ public function testAddEventListener(): void
self::assertSame(['preFoo', 'postFoo'], array_keys($this->eventManager->getAllListeners()));
}

public function testAddEventListenerWithConfig(): void
{
$this->eventManager->addEventListener(['preFoo', 'postFoo'], $this, ['preFoo' => ['method' => 'customMethod']]);
self::assertTrue($this->eventManager->hasListeners(self::PRE_FOO));
self::assertTrue($this->eventManager->hasListeners(self::POST_FOO));
self::assertCount(1, $this->eventManager->getListeners(self::PRE_FOO));
self::assertCount(1, $this->eventManager->getListeners(self::POST_FOO));
self::assertCount(2, $this->eventManager->getAllListeners());
self::assertSame(['preFoo', 'postFoo'], array_keys($this->eventManager->getAllListeners()));
}

public function testDispatchEvent(): void
{
$this->eventManager->addEventListener(['preFoo', 'postFoo'], $this);
Expand All @@ -56,6 +68,15 @@ public function testDispatchEvent(): void
self::assertFalse($this->postFooInvoked);
}

public function testDispatchEventWithConfig(): void
{
$this->eventManager->addEventListener(['preFoo', 'postFoo'], $this, ['preFoo' => ['method' => 'customMethod']]);
$this->eventManager->dispatchEvent(self::PRE_FOO);
self::assertTrue($this->customMethodInvoked);
self::assertFalse($this->preFooInvoked);
self::assertFalse($this->postFooInvoked);
}

public function testRemoveEventListener(): void
{
$this->eventManager->addEventListener(['preBar'], $this);
Expand Down Expand Up @@ -107,6 +128,11 @@ public function postFoo(EventArgs $e): void
{
$this->postFooInvoked = true;
}

public function customMethod(EventArgs $e): void
{
$this->customMethodInvoked = true;
}
}

class TestEventSubscriber implements EventSubscriber
Expand Down