Passed
Branch master (893f23)
by Olivier
10:57
created

HandlerProviderPass::collectHandlers()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 34
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 19
nc 4
nop 1
dl 0
loc 34
rs 9.6333
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the ICanBoogie package.
5
 *
6
 * (c) Olivier Laviale <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace ICanBoogie\MessageBus\Symfony;
13
14
use ICanBoogie\MessageBus\HandlerProvider;
15
use ICanBoogie\MessageBus\PSR\ContainerHandlerProvider;
16
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
17
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
18
use Symfony\Component\DependencyInjection\ContainerBuilder;
19
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
20
use Symfony\Component\DependencyInjection\Exception\LogicException;
21
use Symfony\Component\DependencyInjection\TypedReference;
22
23
use function is_string;
24
25
/**
26
 * Collect message handlers and register their provider.
27
 */
28
class HandlerProviderPass implements CompilerPassInterface
29
{
30
    public const DEFAULT_SERVICE_ID = HandlerProvider::class;
31
    public const DEFAULT_HANDLER_TAG = 'message_dispatcher.handler';
32
    public const DEFAULT_MESSAGE_PROPERTY = 'message';
33
    public const DEFAULT_PROVIDER_CLASS = ContainerHandlerProvider::class;
34
35
    public function __construct(
36
        private string $serviceId = self::DEFAULT_SERVICE_ID,
37
        private string $handlerTag = self::DEFAULT_HANDLER_TAG,
38
        private string $messageProperty = self::DEFAULT_MESSAGE_PROPERTY,
39
        private string $providerClass = self::DEFAULT_PROVIDER_CLASS
40
    ) {
41
    }
42
43
    /**
44
     * @inheritdoc
45
     */
46
    public function process(ContainerBuilder $container): void
47
    {
48
        [ $mapping, $refMap ] = $this->collectHandlers($container);
49
50
        $container
51
            ->register($this->serviceId, $this->providerClass)
52
            ->setArguments([
53
                ServiceLocatorTagPass::register($container, $refMap),
54
                $mapping
55
            ]);
56
    }
57
58
    /**
59
     * @return array{0: array<string, string>, 1: array<string, TypedReference>}
60
     */
61
    private function collectHandlers(ContainerBuilder $container): array
62
    {
63
        $handlers = $container->findTaggedServiceIds($this->handlerTag, true);
64
        $messageProperty = $this->messageProperty;
65
        $mapping = [];
66
        $refMap = [];
67
68
        foreach ($handlers as $id => $tags) {
69
            assert(is_string($id));
70
71
            $command = $tags[0][$messageProperty]
72
                ?? throw new InvalidArgumentException(
73
                    "The `$messageProperty` property is required for service `$id`."
74
                );
75
76
            assert(is_string($command));
77
78
            if (isset($mapping[$command])) {
79
                throw new LogicException(
80
                    "The command `$command` already has an handler: `{$mapping[$command]}`."
81
                );
82
            }
83
84
            $class = $container->getDefinition($id)->getClass();
85
86
            if (!$class) {
87
                throw new LogicException("Unable to get class of service `$id`.");
88
            }
89
90
            $mapping[$command] = $id;
91
            $refMap[$id] = new TypedReference($id, $class);
92
        }
93
94
        return [ $mapping, $refMap ];
95
    }
96
}
97