Passed
Pull Request — master (#1095)
by Aleksei
11:10
created

EventsBootloader::boot()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 26
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 5

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 26
ccs 12
cts 12
cp 1
rs 9.6111
c 0
b 0
f 0
cc 5
nc 8
nop 7
crap 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spiral\Events\Bootloader;
6
7
use Psr\Container\ContainerExceptionInterface;
8
use Psr\Container\ContainerInterface;
9
use Psr\EventDispatcher\EventDispatcherInterface;
10
use Spiral\Boot\AbstractKernel;
11
use Spiral\Boot\Bootloader\Bootloader;
12
use Spiral\Boot\FinalizerInterface;
13
use Spiral\Bootloader\Attributes\AttributesBootloader;
14
use Spiral\Config\ConfiguratorInterface;
15
use Spiral\Config\Patch\Append;
16
use Spiral\Core\Container;
17
use Spiral\Core\Container\Autowire;
18
use Spiral\Core\CoreInterceptorInterface;
19
use Spiral\Core\FactoryInterface;
20
use Spiral\Core\InterceptorPipeline;
21
use Spiral\Events\AutowireListenerFactory;
22
use Spiral\Events\Config\EventsConfig;
23
use Spiral\Events\EventDispatcher;
24
use Spiral\Events\EventDispatcherAwareInterface;
25
use Spiral\Events\Interceptor\Core;
26
use Spiral\Events\ListenerFactoryInterface;
27
use Spiral\Events\ListenerProcessorRegistry;
28
use Spiral\Events\Processor\AttributeProcessor;
29
use Spiral\Events\Processor\ConfigProcessor;
30
use Spiral\Events\Processor\ProcessorInterface;
31
use Spiral\Interceptors\InterceptorInterface;
32
use Spiral\Tokenizer\Bootloader\TokenizerListenerBootloader;
33
34
/**
35
 * @psalm-import-type TInterceptor from EventsConfig
36
 */
37
final class EventsBootloader extends Bootloader
38
{
39
    protected const DEPENDENCIES = [
40
        TokenizerListenerBootloader::class,
41
        AttributesBootloader::class,
42
    ];
43
44
    protected const SINGLETONS = [
45
        ListenerFactoryInterface::class => AutowireListenerFactory::class,
46
        ListenerProcessorRegistry::class => ListenerProcessorRegistry::class,
47
    ];
48
49 321
    public function __construct(
50
        private readonly ConfiguratorInterface $configs
51
    ) {
52 321
    }
53
54 321
    public function init(): void
55
    {
56 321
        $this->configs->setDefaults(EventsConfig::CONFIG, [
57 321
            'listeners' => [],
58 321
            'processors' => [
59 321
                AttributeProcessor::class,
60 321
                ConfigProcessor::class,
61 321
            ],
62 321
            'interceptors' => [],
63 321
        ]);
64
    }
65
66 321
    public function boot(
67
        Container $container,
68
        FactoryInterface $factory,
69
        EventsConfig $config,
70
        AbstractKernel $kernel,
71
        ListenerProcessorRegistry $registry,
72
        FinalizerInterface $finalizer,
73
        ?EventDispatcherInterface $eventDispatcher = null
74
    ): void {
75 321
        if ($eventDispatcher !== null) {
76 6
            $this->initEventDispatcher(new Core($eventDispatcher), $config, $container, $factory);
77
        }
78
79 321
        foreach ($config->getProcessors() as $processor) {
80 321
            $processor = $this->autowire($processor, $container, $factory);
81
82 321
            \assert($processor instanceof ProcessorInterface);
83 321
            $registry->addProcessor($processor);
84
        }
85
86 321
        $kernel->bootstrapped(static function () use ($registry): void {
87 321
            $registry->process();
88 321
        });
89
90 321
        if ($finalizer instanceof EventDispatcherAwareInterface && $eventDispatcher !== null) {
91 2
            $finalizer->setEventDispatcher($eventDispatcher);
92
        }
93
    }
94
95
    /**
96
     * @param TInterceptor $interceptor
0 ignored issues
show
Bug introduced by
The type Spiral\Events\Bootloader\TInterceptor was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
97
     */
98 1
    public function addInterceptor(
99
        string|InterceptorInterface|CoreInterceptorInterface|Container\Autowire $interceptor,
100
    ): void {
101 1
        $this->configs->modify(EventsConfig::CONFIG, new Append('interceptors', null, $interceptor));
102
    }
103
104 6
    private function initEventDispatcher(
105
        Core $core,
106
        EventsConfig $config,
107
        Container $container,
108
        FactoryInterface $factory
109
    ): void {
110 6
        $pipeline = (new InterceptorPipeline())->withCore($core);
0 ignored issues
show
Deprecated Code introduced by
The class Spiral\Core\InterceptorPipeline has been deprecated: use {@see \Spiral\Interceptors\Handler\InterceptorPipeline} instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

110
        $pipeline = (/** @scrutinizer ignore-deprecated */ new InterceptorPipeline())->withCore($core);
Loading history...
111
112 6
        foreach ($config->getInterceptors() as $interceptor) {
113
            $interceptor = $this->autowire($interceptor, $container, $factory);
114
115
            \assert($interceptor instanceof CoreInterceptorInterface || $interceptor instanceof InterceptorInterface);
116
            $pipeline->addInterceptor($interceptor);
117
        }
118
119 6
        $container->removeBinding(EventDispatcherInterface::class);
120 6
        $container->bindSingleton(EventDispatcherInterface::class, new EventDispatcher($pipeline));
121
    }
122
123
    /**
124
     * @template T of object
125
     *
126
     * @param class-string<T>|Autowire<T>|T $id
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T>|Autowire<T>|T at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>|Autowire<T>|T.
Loading history...
127
     *
128
     * @return T
129
     *
130
     * @throws ContainerExceptionInterface
131
     */
132 321
    private function autowire(string|object $id, ContainerInterface $container, FactoryInterface $factory): object
133
    {
134 321
        return match (true) {
135 321
            \is_string($id) => $container->get($id),
0 ignored issues
show
Bug introduced by
It seems like $id can also be of type object; however, parameter $id of Psr\Container\ContainerInterface::get() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

135
            \is_string($id) => $container->get(/** @scrutinizer ignore-type */ $id),
Loading history...
136 321
            $id instanceof Autowire => $id->resolve($factory),
137 321
            default => $id,
138 321
        };
139
    }
140
}
141