Skip to content

Commit e764f96

Browse files
committed
Implement an event dispatcher interface of the PSR-14.
1 parent 0d9173f commit e764f96

3 files changed

Lines changed: 163 additions & 0 deletions

File tree

packages/Events/src/Event.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the aether/aether.
5+
*
6+
* Copyright (C) 2024 Dominik Szamburski
7+
*
8+
* This software may be modified and distributed under the terms
9+
* of the MIT license. See the LICENSE file for details.
10+
*/
11+
12+
namespace Aether\Events;
13+
14+
use Psr\EventDispatcher\StoppableEventInterface;
15+
16+
class Event implements StoppableEventInterface
17+
{
18+
protected bool $propagationStopped = false;
19+
20+
public function isPropagationStopped(): bool
21+
{
22+
return $this->propagationStopped;
23+
}
24+
25+
/**
26+
* Sets the event propagation should be stopped.
27+
*
28+
* @return void
29+
*/
30+
public function setPropagationStopped(): void
31+
{
32+
$this->propagationStopped = true;
33+
}
34+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the aether/aether.
5+
*
6+
* Copyright (C) 2024 Dominik Szamburski
7+
*
8+
* This software may be modified and distributed under the terms
9+
* of the MIT license. See the LICENSE file for details.
10+
*/
11+
12+
namespace Aether\Events;
13+
14+
use PHPUnit\Framework\Attributes\CoversClass;
15+
use Psr\EventDispatcher\StoppableEventInterface;
16+
17+
#[CoversClass(EventDispatcher::class)]
18+
class EventDispatcher implements EventDispatcherInterface
19+
{
20+
/** @var callable[][][] */
21+
protected array $listeners = [];
22+
23+
/** @var callable[][] */
24+
protected array $sortedListeners = [];
25+
26+
public function listen(string $eventName, callable $listener, int $priority = 10): void
27+
{
28+
$this->listeners[$eventName][$priority][] = $listener;
29+
unset($this->sortedListeners[$eventName]);
30+
}
31+
32+
public function dispatch(object $event, ?string $eventName = null): object
33+
{
34+
$eventName ??= $event::class;
35+
36+
if (($listeners = $this->getListeners($eventName)) !== []) {
37+
$this->invokeListeners($listeners, $event, $eventName);
38+
}
39+
40+
return $event;
41+
}
42+
43+
public function getListeners(string $eventName): iterable
44+
{
45+
if (! isset($this->listeners[$eventName])) {
46+
return [];
47+
}
48+
49+
if (! isset($this->sortedListeners[$eventName])) {
50+
$this->sortListeners($eventName);
51+
}
52+
53+
return $this->sortedListeners[$eventName];
54+
}
55+
56+
/**
57+
* @param callable[] $listeners The event listeners.
58+
* @param object $event The event object to pass to the listener.
59+
* @param string $eventName The event name.
60+
*/
61+
protected function invokeListeners(iterable $listeners, object $event, string $eventName): void
62+
{
63+
foreach ($listeners as $listener) {
64+
if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
65+
break;
66+
}
67+
68+
$listener($event, $eventName, $this);
69+
}
70+
}
71+
72+
/**
73+
* Sorts the event listeners by priority.
74+
*/
75+
protected function sortListeners(string $eventName): void
76+
{
77+
\ksort($this->listeners[$eventName]);
78+
$this->sortedListeners[$eventName] = [];
79+
80+
foreach ($this->listeners[$eventName] as &$listeners) {
81+
foreach ($listeners as &$listener) {
82+
$this->sortedListeners[$eventName][] = $listener;
83+
}
84+
}
85+
}
86+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the aether/aether.
5+
*
6+
* Copyright (C) 2024 Dominik Szamburski
7+
*
8+
* This software may be modified and distributed under the terms
9+
* of the MIT license. See the LICENSE file for details.
10+
*/
11+
12+
namespace Aether\Events;
13+
14+
use Psr\EventDispatcher\EventDispatcherInterface as BaseEventDispatcher;
15+
16+
interface EventDispatcherInterface extends BaseEventDispatcher
17+
{
18+
/**
19+
* Registers an event listener with the event dispatcher.
20+
*
21+
* @param string $eventName The event name.
22+
* @param callable $listener A listener for given event.
23+
* @param int $priority The higher the priority, the earlier the listener will be triggered.
24+
*/
25+
public function listen(string $eventName, callable $listener, int $priority = 10): void;
26+
27+
/**
28+
* @template T of object
29+
*
30+
* @param T $event The object to process.
31+
* @param null|string $eventName The event name.
32+
* @return T The passed $event MUST be returned.
33+
*/
34+
public function dispatch(object $event, ?string $eventName = null): object;
35+
36+
/**
37+
* Gets listeners for given event.
38+
*
39+
* @param string $eventName The event name.
40+
* @return callable[] Returns a sorted array of listeners.
41+
*/
42+
public function getListeners(string $eventName): iterable;
43+
}

0 commit comments

Comments
 (0)