├── LICENSE ├── composer.json └── src ├── BufferedEventDispatcher.php ├── EventDispatcher.php ├── EventDispatcherAware.php ├── EventDispatcherAwareBehavior.php ├── EventDispatchingListenerRegistry.php ├── EventGenerator.php ├── EventGeneratorBehavior.php ├── HasEventName.php ├── Listener.php ├── ListenerPriority.php ├── ListenerRegistry.php ├── ListenerSubscriber.php ├── OneTimeListener.php ├── PrioritizedListenerRegistry.php ├── PrioritizedListenersForEvent.php └── UnableToSubscribeListener.php /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Frank de Jonge 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "league/event", 3 | "description": "Event package", 4 | "keywords": [ 5 | "event", 6 | "emitter", 7 | "listener" 8 | ], 9 | "license": "MIT", 10 | "authors": [ 11 | { 12 | "name": "Frank de Jonge", 13 | "email": "info@frenky.net" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=7.2.0", 18 | "psr/event-dispatcher": "^1.0" 19 | }, 20 | "require-dev": { 21 | "phpunit/phpunit": "^8.5", 22 | "phpstan/phpstan": "^0.12.45", 23 | "friendsofphp/php-cs-fixer": "^2.16" 24 | }, 25 | "provide": { 26 | "psr/event-dispatcher-implementation": "1.0" 27 | }, 28 | "autoload": { 29 | "psr-4": { 30 | "League\\Event\\": "src/" 31 | } 32 | }, 33 | "extra": { 34 | "branch-alias": { 35 | "dev-master": "3.0-dev" 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/BufferedEventDispatcher.php: -------------------------------------------------------------------------------- 1 | dispatcher = $dispatcher; 24 | } 25 | 26 | public function dispatch(object $event): object 27 | { 28 | $this->recordEvent($event); 29 | 30 | return $event; 31 | } 32 | 33 | /** 34 | * @return object[] 35 | */ 36 | public function dispatchBufferedEvents(): array 37 | { 38 | $events = []; 39 | 40 | foreach ($this->releaseEvents() as $event) { 41 | $events[] = $this->dispatcher->dispatch($event); 42 | } 43 | 44 | return $events; 45 | } 46 | 47 | public function subscribeTo(string $event, callable $listener, int $priority = ListenerPriority::NORMAL): void 48 | { 49 | if ( ! $this->dispatcher instanceof ListenerRegistry) { 50 | throw UnableToSubscribeListener::becauseTheEventDispatcherDoesNotAcceptListeners($this->dispatcher); 51 | } 52 | 53 | $this->dispatcher->subscribeTo($event, $listener, $priority); 54 | } 55 | 56 | public function subscribeOnceTo(string $event, callable $listener, int $priority = ListenerPriority::NORMAL): void 57 | { 58 | if ( ! $this->dispatcher instanceof ListenerRegistry) { 59 | throw UnableToSubscribeListener::becauseTheEventDispatcherDoesNotAcceptListeners($this->dispatcher); 60 | } 61 | 62 | $this->dispatcher->subscribeOnceTo($event, $listener, $priority); 63 | } 64 | 65 | public function subscribeListenersFrom(ListenerSubscriber $subscriber): void 66 | { 67 | if ( ! $this->dispatcher instanceof ListenerRegistry) { 68 | throw UnableToSubscribeListener::becauseTheEventDispatcherDoesNotAcceptListeners($this->dispatcher); 69 | } 70 | 71 | $this->dispatcher->subscribeListenersFrom($subscriber); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/EventDispatcher.php: -------------------------------------------------------------------------------- 1 | listenerProvider = $listenerProvider instanceof ListenerProviderInterface 20 | ? $listenerProvider 21 | : new PrioritizedListenerRegistry(); 22 | } 23 | 24 | public function dispatch(object $event): object 25 | { 26 | $listeners = $this->listenerProvider->getListenersForEvent($event); 27 | 28 | $event instanceof StoppableEventInterface 29 | ? $this->dispatchStoppableEvent($listeners, $event) 30 | : $this->dispatchUnstoppableEvent($listeners, $event); 31 | 32 | return $event; 33 | } 34 | 35 | public function dispatchGeneratedEvents(EventGenerator $generator): void 36 | { 37 | foreach ($generator->releaseEvents() as $event) { 38 | $this->dispatch($event); 39 | } 40 | } 41 | 42 | private function dispatchStoppableEvent(iterable $listeners, StoppableEventInterface $event): void 43 | { 44 | foreach ($listeners as $listener) { 45 | if ($event->isPropagationStopped()) { 46 | break; 47 | } 48 | 49 | $listener($event); 50 | } 51 | } 52 | 53 | private function dispatchUnstoppableEvent(iterable $listeners, object $event): void 54 | { 55 | foreach ($listeners as $listener) { 56 | $listener($event); 57 | } 58 | } 59 | 60 | public function subscribeTo(string $event, callable $listener, int $priority = ListenerPriority::NORMAL): void 61 | { 62 | if ( ! $this->listenerProvider instanceof ListenerRegistry) { 63 | throw UnableToSubscribeListener::becauseTheListenerProviderDoesNotAcceptListeners($this->listenerProvider); 64 | } 65 | 66 | $this->listenerProvider->subscribeTo($event, $listener, $priority); 67 | } 68 | 69 | public function subscribeOnceTo(string $event, callable $listener, int $priority = ListenerPriority::NORMAL): void 70 | { 71 | if ( ! $this->listenerProvider instanceof ListenerRegistry) { 72 | throw UnableToSubscribeListener::becauseTheListenerProviderDoesNotAcceptListeners($this->listenerProvider); 73 | } 74 | 75 | $this->listenerProvider->subscribeOnceTo($event, $listener, $priority); 76 | } 77 | 78 | public function subscribeListenersFrom(ListenerSubscriber $subscriber): void 79 | { 80 | if ( ! $this->listenerProvider instanceof ListenerRegistry) { 81 | throw UnableToSubscribeListener::becauseTheListenerProviderDoesNotAcceptListeners($this->listenerProvider); 82 | } 83 | 84 | $this->listenerProvider->subscribeListenersFrom($subscriber); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/EventDispatcherAware.php: -------------------------------------------------------------------------------- 1 | dispatcher = $emitter; 17 | } 18 | 19 | public function eventDispatcher(): EventDispatcher 20 | { 21 | if ($this->dispatcher === null) { 22 | $this->dispatcher = new EventDispatcher(new PrioritizedListenerRegistry()); 23 | } 24 | 25 | return $this->dispatcher; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/EventDispatchingListenerRegistry.php: -------------------------------------------------------------------------------- 1 | events[] = $event; 20 | 21 | return $this; 22 | } 23 | 24 | /** 25 | * @return object[] 26 | */ 27 | public function releaseEvents(): array 28 | { 29 | $events = $this->events; 30 | $this->events = []; 31 | 32 | return $events; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/HasEventName.php: -------------------------------------------------------------------------------- 1 | listener = $listener; 20 | } 21 | 22 | /** 23 | * @inheritdoc 24 | */ 25 | public function __invoke(object $event): void 26 | { 27 | call_user_func($this->listener, $event); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/PrioritizedListenerRegistry.php: -------------------------------------------------------------------------------- 1 | */ 12 | protected $listenersPerEvent = []; 13 | 14 | public function subscribeTo(string $event, callable $listener, int $priority = ListenerPriority::NORMAL): void 15 | { 16 | $group = array_key_exists($event, $this->listenersPerEvent) 17 | ? $this->listenersPerEvent[$event] 18 | : $this->listenersPerEvent[$event] = new PrioritizedListenersForEvent(); 19 | 20 | $group->addListener($listener, $priority); 21 | } 22 | 23 | public function subscribeOnceTo(string $event, callable $listener, int $priority = ListenerPriority::NORMAL): void 24 | { 25 | $this->subscribeTo($event, new OneTimeListener($listener), $priority); 26 | } 27 | 28 | public function getListenersForEvent(object $event): iterable 29 | { 30 | /** 31 | * @var string $key 32 | * @var PrioritizedListenersForEvent $group 33 | */ 34 | foreach ($this->listenersPerEvent as $key => $group) { 35 | if ($event instanceof $key) { 36 | yield from $group->getListeners(); 37 | } 38 | } 39 | 40 | if ($event instanceof HasEventName) { 41 | yield from $this->getListenersForEventName($event->eventName()); 42 | } 43 | } 44 | 45 | private function getListenersForEventName(string $eventName): iterable 46 | { 47 | if ( ! array_key_exists($eventName, $this->listenersPerEvent)) { 48 | return []; 49 | } 50 | 51 | return $this->listenersPerEvent[$eventName]->getListeners(); 52 | } 53 | 54 | public function subscribeListenersFrom(ListenerSubscriber $subscriber): void 55 | { 56 | $subscriber->subscribeListeners($this); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/PrioritizedListenersForEvent.php: -------------------------------------------------------------------------------- 1 | > */ 16 | private $listeners = []; 17 | /** @var array|null */ 18 | private $sortedListeners; 19 | 20 | public function addListener(callable $listener, int $priority): void 21 | { 22 | $this->sortedListeners = null; 23 | $this->listeners[$priority][] = $listener; 24 | } 25 | 26 | public function getListeners(): iterable 27 | { 28 | return $this->sortedListeners ?? $this->sortListeners(); 29 | } 30 | 31 | private function sortListeners(): array 32 | { 33 | $listeners = []; 34 | krsort($this->listeners, SORT_NUMERIC); 35 | $filter = static function ($listener): bool { 36 | return $listener instanceof OneTimeListener === false; 37 | }; 38 | 39 | foreach ($this->listeners as $priority => $group) { 40 | foreach ($group as $listener) { 41 | $listeners[] = $listener; 42 | } 43 | $this->listeners[$priority] = array_filter($group, $filter); 44 | } 45 | 46 | $this->sortedListeners = array_filter($listeners, $filter); 47 | 48 | return $listeners; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/UnableToSubscribeListener.php: -------------------------------------------------------------------------------- 1 |