Test Failed
Push — dev ( 0b9faa...860b7c )
by Janko
09:34
created

StuContainer::getDefinedImplementationsOf()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
c 0
b 0
f 0
nc 2
nop 2
dl 0
loc 8
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stu\Config;
6
7
use DI\Container;
8
use DI\Definition\ArrayDefinition;
9
use DI\Definition\Definition;
10
use DI\Definition\FactoryDefinition;
11
use DI\Definition\Source\MutableDefinitionSource;
12
use DI\Proxy\ProxyFactory;
13
use Doctrine\Common\Collections\ArrayCollection;
14
use Doctrine\Common\Collections\Collection;
15
use Psr\Container\ContainerInterface;
16
17
use function DI\get;
18
19
class StuContainer extends Container
20
{
21
    private MutableDefinitionSource $definitionSource;
22
23
    /** @var array<string, Definition> */
24
    private ?array $definitions = null;
25
26
    /** @var Collection<string, Collection<int|string, mixed>> */
27
    private Collection $services;
28
29
    /**
30
     * @param ContainerInterface $wrapperContainer If the container is wrapped by another container.
31
     */
32
    public function __construct(
33
        MutableDefinitionSource $definitions,
34
        ?ProxyFactory $proxyFactory = null,
35
        ?ContainerInterface $wrapperContainer = null
36
    ) {
37
        parent::__construct($definitions, $proxyFactory, $wrapperContainer);
38
39
        $this->definitionSource = $definitions;
40
        $this->services = new ArrayCollection();
41
    }
42
43
    /**
44
     * @template T
45
     * @param class-string<T> $interfaceName
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...
46
     * 
47
     * @return Collection<int|string, T>
48
     */
49
    public function getDefinedImplementationsOf(string $interfaceName, bool $addDefinitionKey = false): Collection
50
    {
51
        $services = $this->services->get($interfaceName);
52
        if ($services === null) {
53
            $services = $this->getServices($interfaceName, $addDefinitionKey);
54
        }
55
56
        return $services;
57
    }
58
59
    /**
60
     * @template T
61
     * @param class-string<T> $interfaceName
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...
62
     * 
63
     * @return Collection<int, T>
64
     */
65
    private function getServices(string $interfaceName, bool $addDefinitionKey): Collection
66
    {
67
        $services = new ArrayCollection();
68
69
        $definitions = $this->getDefinitions();
70
71
        foreach ($definitions as $definitionKey => $definition) {
72
73
            if ($definition instanceof ArrayDefinition) {
74
75
                foreach (
76
                    get($definitionKey)->resolve($this->delegateContainer)
77
                    as $arrayKey => $service
78
                ) {
79
                    $this->addDefinition(
80
                        $service,
81
                        $addDefinitionKey ? sprintf('%s-%s', $definitionKey, $arrayKey) : $arrayKey,
82
                        $services,
83
                        $interfaceName
84
                    );
85
                }
86
            } elseif (!$definition instanceof FactoryDefinition) {
87
                $this->addDefinition(
88
                    get($definitionKey)->resolve($this->delegateContainer),
89
                    $definitionKey,
90
                    $services,
91
                    $interfaceName
92
                );
93
            }
94
        }
95
96
        $this->services->set($interfaceName, $services);
97
98
        return $services;
99
    }
100
101
    /** @return array<string, Definition> */
102
    private function getDefinitions(): array
103
    {
104
        if ($this->definitions === null) {
105
            $this->definitions = $this->definitionSource->getDefinitions();
106
        }
107
108
        return $this->definitions;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->definitions could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
109
    }
110
111
    /**
112
     * @template T
113
     * @param class-string<T> $interfaceName
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...
114
     * @param T $service
115
     * @param Collection<int|string, T> $services
116
     */
117
    private function addDefinition(
118
        $service,
119
        int|string $key,
120
        Collection $services,
121
        string $interfaceName
122
    ): void {
123
        $classImplements = class_implements($service);
124
        if (!$classImplements) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $classImplements of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
125
            return;
126
        }
127
128
        if (in_array($interfaceName, $classImplements)) {
129
            $services->set($key, $service);
130
        }
131
    }
132
}
133