DefinitionHelper   A
last analyzed

Complexity

Total Complexity 12

Size/Duplication

Total Lines 86
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 39
c 1
b 0
f 0
dl 0
loc 86
rs 10
wmc 12

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getReflectionClassForServiceId() 0 3 1
A getDefinitionForServiceId() 0 3 1
A mapDefinitionMethodCalls() 0 5 1
A getAttributesFor() 0 5 1
A mapDefinitionArguments() 0 28 2
A __construct() 0 1 1
A getClassForServiceId() 0 5 2
A getServiceId() 0 6 3
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
7
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
8
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
9
 *
10
 * Copyright (c) 2024 Mykhailo Shtanko [email protected]
11
 *
12
 * For the full copyright and license information, please view the LICENSE.MD
13
 * file that was distributed with this source code.
14
 */
15
16
namespace FRZB\Component\DependencyInjection\Helper;
17
18
use Fp\Collections\ArrayList;
19
use Fp\Collections\HashMap;
20
use JetBrains\PhpStorm\Immutable;
21
use Symfony\Component\DependencyInjection\ContainerBuilder;
22
use Symfony\Component\DependencyInjection\Definition;
23
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
24
use Symfony\Component\DependencyInjection\Reference;
25
26
/** @internal */
27
#[Immutable]
28
final class DefinitionHelper
29
{
30
    private const SERVICE_PREFIX = '@';
31
    private const EMPTY_STRING = '';
32
33
    private function __construct() {}
34
35
    /**
36
     * @template T
37
     *
38
     * @param class-string<T> $attributeName
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
39
     *
40
     * @return T[]
41
     */
42
    public static function getAttributesFor(\ReflectionClass $reflectionClass, string $attributeName): array
43
    {
44
        return ArrayList::collect($reflectionClass->getAttributes($attributeName, \ReflectionAttribute::IS_INSTANCEOF))
45
            ->map(static fn (\ReflectionAttribute $reflectionAttribute) => $reflectionAttribute->newInstance())
46
            ->toArray()
47
        ;
48
    }
49
50
    public static function getServiceId(ContainerBuilder $container, \ReflectionClass $reflectionClass, ?string $serviceId): string
51
    {
52
        return match (true) {
53
            null !== $serviceId && $container->hasDefinition($serviceId) => $serviceId,
54
            null === $serviceId && $container->hasDefinition($reflectionClass->getName()) => $reflectionClass->getName(),
55
            default => throw new ServiceNotFoundException($attribute->id ?? $reflectionClass->getName()),
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $attribute seems to be never defined.
Loading history...
56
        };
57
    }
58
59
    public static function getClassForServiceId(ContainerBuilder $container, string $serviceId): string
60
    {
61
        return class_exists($serviceId)
0 ignored issues
show
Bug Best Practice introduced by
The expression return class_exists($ser...$serviceId)->getClass() could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
62
            ? $serviceId
63
            : $container->getDefinition($serviceId)->getClass();
64
    }
65
66
    /** @throws \ReflectionException */
67
    public static function getReflectionClassForServiceId(ContainerBuilder $container, string $serviceId): \ReflectionClass
68
    {
69
        return $container->getReflectionClass(self::getClassForServiceId($container, $serviceId));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $container->getRe...container, $serviceId)) could return the type null which is incompatible with the type-hinted return ReflectionClass. Consider adding an additional type-check to rule them out.
Loading history...
70
    }
71
72
    public static function getDefinitionForServiceId(ContainerBuilder $container, string $serviceId): Definition
73
    {
74
        return $container->getDefinition(self::getClassForServiceId($container, $serviceId));
75
    }
76
77
    public static function mapDefinitionMethodCalls(ContainerBuilder $container, array $methodCalls): array
78
    {
79
        return HashMap::collect($methodCalls)
80
            ->map(fn (array $value) => self::mapDefinitionArguments($container, $value))
81
            ->toList()
82
        ;
83
    }
84
85
    public static function mapDefinitionArguments(ContainerBuilder $container, array $arguments): array
86
    {
87
        $definitionsById = HashMap::collect($arguments)
88
            ->filter(static fn (string $value) => \is_string($value))
89
            ->filter(static fn (string $value) => str_contains($value, self::SERVICE_PREFIX))
90
            ->map(static fn (string $value) => str_replace(self::SERVICE_PREFIX, self::EMPTY_STRING, $value))
91
            ->filter(static fn (string $value) => $container->hasDefinition($value))
92
            ->map(static fn (string $value) => new Reference($value))
93
            ->toArray()
94
        ;
95
96
        $definitionsByClass = HashMap::collect($arguments)
97
            ->filter(static fn (string $value) => \is_string($value))
98
            ->filter(static fn (string $value) => class_exists($value))
99
            ->filter(static fn (string $value) => $container->hasDefinition($value))
100
            ->map(static fn (string $value) => new Reference($value))
101
            ->toArray()
102
        ;
103
104
        $definitionsByAlias = HashMap::collect($arguments)
105
            ->filter(static fn (string $value) => \is_string($value))
106
            ->filter(static fn (string $value) => interface_exists($value) || class_exists($value))
107
            ->filter(static fn (string $value) => $container->hasAlias($value))
108
            ->map(static fn (string $value) => new Reference($value))
109
            ->toArray()
110
        ;
111
112
        return [...$definitionsById, ...$definitionsByClass, ...$definitionsByAlias];
113
    }
114
}
115