Test Failed
Push — dev ( ecfdfd...86afd3 )
by Janko
13:33 queued 29s
created

StuContainer::setAdditionalService()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 5
rs 10
c 0
b 0
f 0
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\DependencyException;
13
use DI\NotFoundException;
14
use DI\Proxy\ProxyFactory;
15
use Doctrine\Common\Collections\ArrayCollection;
16
use Doctrine\Common\Collections\Collection;
17
use Psr\Container\ContainerInterface;
18
19
use function DI\get;
20
21
class StuContainer extends Container
22
{
23
    private MutableDefinitionSource $definitionSource;
24
25
    /** @var array<string, Definition> */
26
    private ?array $definitions = null;
27
28
    /** @var Collection<string, Collection<int|string, mixed>> */
29
    private Collection $services;
30
31
    /** @var Collection<string, Object> */
32
    private Collection $additionalServices;
33
34
    /**
35
     * @param ContainerInterface $wrapperContainer If the container is wrapped by another container.
36
     */
37
    public function __construct(
38
        MutableDefinitionSource $definitions,
39
        ?ProxyFactory $proxyFactory = null,
40
        ?ContainerInterface $wrapperContainer = null
41
    ) {
42
        parent::__construct($definitions, $proxyFactory, $wrapperContainer);
43
44
        $this->definitionSource = $definitions;
45
        $this->services = new ArrayCollection();
46
        $this->additionalServices = new ArrayCollection();
47
    }
48
49
    /**
50
     * Returns an entry of the container by its name.
51
     *
52
     * @template T
53
     * @param string|class-string<T> $id Entry name or a class name.
0 ignored issues
show
Documentation Bug introduced by
The doc comment string|class-string<T> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in string|class-string<T>.
Loading history...
54
     *
55
     * @return mixed|T
56
     * @throws DependencyException Error while resolving the entry.
57
     * @throws NotFoundException No entry found for the given name.
58
     */
59
    public function get(string $id): mixed
60
    {
61
        $service = $this->additionalServices->get($id);
62
63
        return $service === null
64
            ? parent::get($id)
65
            : $service;
66
    }
67
68
    /**
69
     * @template T
70
     * @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...
71
     * 
72
     * @return Collection<int|string, T>
73
     */
74
    public function getDefinedImplementationsOf(string $interfaceName, bool $addDefinitionKey = false): Collection
75
    {
76
        $services = $this->services->get($interfaceName);
77
        if ($services === null) {
78
            $services = $this->getServices($interfaceName, $addDefinitionKey);
79
        }
80
81
        return $services;
82
    }
83
84
    public function setAdditionalService(string $id, Object $service): StuContainer
85
    {
86
        $this->additionalServices->set($id, $service);
87
88
        return $this;
89
    }
90
91
    /**
92
     * @template T
93
     * @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...
94
     * 
95
     * @return Collection<int, T>
96
     */
97
    private function getServices(string $interfaceName, bool $addDefinitionKey): Collection
98
    {
99
        $services = new ArrayCollection();
100
101
        $definitions = $this->getDefinitions();
102
103
        foreach ($definitions as $definitionKey => $definition) {
104
105
            if ($definition instanceof ArrayDefinition) {
106
107
                foreach (
108
                    get($definitionKey)->resolve($this->delegateContainer)
109
                    as $arrayKey => $service
110
                ) {
111
                    $this->addDefinition(
112
                        $service,
113
                        $addDefinitionKey ? sprintf('%s-%s', $definitionKey, $arrayKey) : $arrayKey,
114
                        $services,
115
                        $interfaceName
116
                    );
117
                }
118
            } elseif (!$definition instanceof FactoryDefinition) {
119
                $this->addDefinition(
120
                    get($definitionKey)->resolve($this->delegateContainer),
121
                    $definitionKey,
122
                    $services,
123
                    $interfaceName
124
                );
125
            }
126
        }
127
128
        $this->services->set($interfaceName, $services);
129
130
        return $services;
131
    }
132
133
    /** @return array<string, Definition> */
134
    private function getDefinitions(): array
135
    {
136
        if ($this->definitions === null) {
137
            $this->definitions = $this->definitionSource->getDefinitions();
138
        }
139
140
        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...
141
    }
142
143
    /**
144
     * @template T
145
     * @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...
146
     * @param T $service
147
     * @param Collection<int|string, T> $services
148
     */
149
    private function addDefinition(
150
        $service,
151
        int|string $key,
152
        Collection $services,
153
        string $interfaceName
154
    ): void {
155
        $classImplements = class_implements($service);
156
        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...
157
            return;
158
        }
159
160
        if (in_array($interfaceName, $classImplements)) {
161
            $services->set($key, $service);
162
        }
163
    }
164
}
165