Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
EventDispatcherServiceProvider
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
2 / 2
2
100.00% covered (success)
100.00%
1 / 1
 getFactories
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
1
 getExtensions
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5/**
6 * This file is part of php-fast-forward/event-dispatcher.
7 *
8 * This source file is subject to the license bundled
9 * with this source code in the file LICENSE.
10 *
11 * @copyright Copyright (c) 2025-2026 Felipe SayĆ£o Lobato Abreu <github@mentordosnerds.com>
12 * @license   https://opensource.org/licenses/MIT MIT License
13 *
14 * @see       https://github.com/php-fast-forward/event-dispatcher
15 * @see       https://github.com/php-fast-forward
16 * @see       https://datatracker.ietf.org/doc/html/rfc2119
17 */
18
19namespace FastForward\EventDispatcher\ServiceProvider;
20
21use FastForward\Config\ConfigInterface;
22use FastForward\EventDispatcher\ServiceProvider\Extension\ReflectionBasedListenerProviderExtension;
23use FastForward\Container\Factory\AliasFactory;
24use FastForward\Container\Factory\InvokableFactory;
25use FastForward\EventDispatcher\ServiceProvider\Extension\ListenerProviderAggregateExtension;
26use FastForward\EventDispatcher\EventDispatcher;
27use FastForward\EventDispatcher\ListenerProvider\EventSubscriberListenerProvider;
28use FastForward\EventDispatcher\ServiceProvider\Configuration\ConfiguredListenerProviderCollection;
29use FastForward\EventDispatcher\ServiceProvider\Extension\EventSubscriberListenerProviderExtension;
30use FastForward\EventDispatcher\ServiceProvider\Extension\PrioritizedListenerProviderExtension;
31use Interop\Container\ServiceProviderInterface;
32use Phly\EventDispatcher\ListenerProvider\ListenerProviderAggregate;
33use Phly\EventDispatcher\ListenerProvider\PrioritizedListenerProvider;
34use Phly\EventDispatcher\ListenerProvider\ReflectionBasedListenerProvider;
35use Psr\EventDispatcher\EventDispatcherInterface;
36use Psr\EventDispatcher\ListenerProviderInterface;
37use Symfony\Contracts\EventDispatcher\EventDispatcherInterface as SymfonyContractsEventDispatcherInterface;
38
39/**
40 * Register the event-dispatcher services exposed by this package.
41 */
42 class EventDispatcherServiceProvider implements ServiceProviderInterface
43{
44    /**
45     * Return the service factories provided by this library.
46     *
47     * @return array<class-string, mixed> factory definitions keyed by service identifier
48     */
49    public function getFactories(): array
50    {
51        return [
52            ListenerProviderInterface::class                => AliasFactory::get(ListenerProviderAggregate::class),
53            EventDispatcherInterface::class                 => AliasFactory::get(EventDispatcher::class),
54            SymfonyContractsEventDispatcherInterface::class => AliasFactory::get(EventDispatcher::class),
55
56            ListenerProviderAggregate::class => new InvokableFactory(
57                ListenerProviderAggregate::class,
58                PrioritizedListenerProvider::class,
59                ReflectionBasedListenerProvider::class,
60                EventSubscriberListenerProvider::class,
61            ),
62
63            EventDispatcher::class => new InvokableFactory(
64                EventDispatcher::class,
65                ListenerProviderInterface::class
66            ),
67
68            PrioritizedListenerProvider::class     => new InvokableFactory(PrioritizedListenerProvider::class),
69            ReflectionBasedListenerProvider::class => new InvokableFactory(ReflectionBasedListenerProvider::class),
70            EventSubscriberListenerProvider::class => new InvokableFactory(EventSubscriberListenerProvider::class),
71
72            ConfiguredListenerProviderCollection::class => new InvokableFactory(
73                ConfiguredListenerProviderCollection::class,
74                ConfigInterface::class,
75            ),
76        ];
77    }
78
79    /**
80     * Return service extensions applied after factory creation.
81     *
82     * @return array<class-string, object> extension callbacks keyed by service identifier
83     */
84    public function getExtensions(): array
85    {
86        return [
87            ListenerProviderAggregate::class => new ListenerProviderAggregateExtension(),
88            PrioritizedListenerProvider::class => new PrioritizedListenerProviderExtension(),
89            ReflectionBasedListenerProvider::class => new ReflectionBasedListenerProviderExtension(),
90            EventSubscriberListenerProvider::class => new EventSubscriberListenerProviderExtension(),
91        ];
92    }
93
94    // /**
95    //  * @param string $class
96    //  *
97    //  * @return IteratorAggregate
98    //  */
99    // private function getReflectionAttributes(string $class): IteratorAggregate
100    // {
101    //     return new GeneratorCachingIteratorAggregate(function () use ($class) {
102    //         $reflection = new ReflectionClass($class);
103
104    //         $factory = function ($attribute) use ($reflection) {
105    //             $instance = $attribute->newInstance();
106    //             $instance->method ??= '__invoke';
107    //             $instance->event ??= $this->getReflectionEventType($reflection->getMethod($instance->method));
108
109    //             return $instance;
110    //         };
111
112    //         yield from array_map($factory, $reflection->getAttributes(AsEventListener::class));
113
114    //         foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
115    //             $factory = function ($attribute) use ($method) {
116    //                 $instance = $attribute->newInstance();
117    //                 $instance->method ??= $method->getName();
118    //                 $instance->event ??= $this->getReflectionEventType($method);
119
120    //                 return $instance;
121    //             };
122
123    //             yield from array_map($factory, $method->getAttributes(AsEventListener::class));
124    //         }
125    //     });
126    // }
127
128    // /**
129    //  * @param ReflectionMethod $method
130    //  *
131    //  * @return string
132    //  *
133    //  * @throws RuntimeException
134    //  */
135    // private function getReflectionEventType(ReflectionMethod $method): string
136    // {
137    //     $parameters = $method->getParameters();
138    //     if (empty($parameters)) {
139    //         throw RuntimeException::forMethodWithoutParameters();
140    //     }
141
142    //     $parameter = $parameters[0];
143
144    //     if (! $parameter->hasType()) {
145    //         throw RuntimeException::forMethodParameterWithoutType();
146    //     }
147
148    //     return $parameter->getType()
149    //         ->getName();
150    // }
151
152    // /**
153    //  * @param callable|EventSubscriberInterface|ListenerProviderInterface|string $listener
154    //  *
155    //  * @return string
156    //  */
157    // private function getListenerProviderType(
158    //     callable|EventSubscriberInterface|ListenerProviderInterface|string $listener
159    // ): string {
160    //     $hasAttribute = $this->getReflectionAttributes($listener)
161    //         ->getIterator()
162    //         ->current();
163
164    //     $listenerProviderType = match (true) {
165    //         $hasAttribute                                               => PrioritizedListenerProvider::class,
166    //         is_subclass_of($listener, ListenerProviderInterface::class) => ListenerProviderAggregate::class,
167    //         is_subclass_of($listener, EventSubscriberInterface::class)  => EventSubscriberListenerProvider::class,
168    //         default                                                     => ReflectionBasedListenerProvider::class,
169    //     };
170
171    //     if (ReflectionBasedListenerProvider::class === $listenerProviderType
172    //         && ! \is_string($listener)
173    //         && ! \is_callable($listener)
174    //     ) {
175    //         throw RuntimeException::forUnsupportedType($listener);
176    //     }
177
178    //     return $listenerProviderType;
179    // }
180
181    // /**
182    //  * @param ContainerInterface $container
183    //  * @param PrioritizedListenerProvider $prioritizedListenerProvider
184    //  *
185    //  * @return void
186    //  */
187    // private function extendPrioritizedListenerProvider(
188    //     ContainerInterface $container,
189    //     PrioritizedListenerProvider $prioritizedListenerProvider,
190    // ): void {
191    //     foreach ($this->listeners[PrioritizedListenerProvider::class] as $listener) {
192    //         $attributes = $this->getReflectionAttributes($listener);
193
194    //         foreach ($attributes as $attribute) {
195    //             $priority  = $attribute->priority;
196    //             $eventType = $attribute->event;
197    //             $method    = $attribute->method;
198
199    //             if (\is_string($listener) && $container->has($listener)) {
200    //                 $listener = new LazyListener($container, $listener, $method);
201    //             }
202
203    //             $prioritizedListenerProvider->listen($eventType, $listener, $priority);
204    //         }
205    //     }
206    // }
207
208    // /**
209    //  * @param ContainerInterface $container
210    //  * @param ReflectionBasedListenerProvider $reflectionBasedListenerProvider
211    //  *
212    //  * @return void
213    //  */
214    // private function extendReflectionBasedListenerProvider(
215    //     ContainerInterface $container,
216    //     ReflectionBasedListenerProvider $reflectionBasedListenerProvider,
217    // ): void {
218    //     foreach ($this->listeners[ReflectionBasedListenerProvider::class] as $listener) {
219    //         if ($container->has($listener)) {
220    //             $listener = new LazyListener($container, $listener);
221    //         }
222
223    //         $reflectionBasedListenerProvider->listen($listener);
224    //     }
225    // }
226}