Passed
Push — main ( 0c6c03...d8a441 )
by Breno
01:55
created

Container::add()   A

Complexity

Conditions 6
Paths 16

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 6
eloc 10
c 2
b 0
f 0
nc 16
nop 2
dl 0
loc 18
rs 9.2222
1
<?php
2
declare(strict_types=1);
3
4
namespace Habemus;
5
6
use ArrayAccess;
7
use Habemus\Autowiring\Attributes\AttributesInjection;
8
use Habemus\Autowiring\ClassResolver;
9
use Habemus\Autowiring\ReflectionResolver;
10
use Habemus\Autowiring\Reflector;
11
use Habemus\Definition\AutoDetection;
12
use Habemus\Definition\Build\ClassDefinition;
13
use Habemus\Definition\Build\RawDefinition;
14
use Habemus\Definition\Definition;
15
use Habemus\Definition\DefinitionDetection;
16
use Habemus\Definition\DefinitionBuilder;
17
use Habemus\Definition\DefinitionList;
18
use Habemus\Definition\DefinitionResolver;
19
use Habemus\Definition\DefinitionResolverInterface;
20
use Habemus\Definition\DefinitionWrapper;
21
use Habemus\Definition\Sharing\Shareable;
22
use Habemus\Exception\NotFoundException;
23
use Habemus\ServiceProvider\ServiceProvider;
24
use Habemus\ServiceProvider\ServiceProviderManager;
25
use Psr\Container\ContainerInterface;
26
27
class Container implements ContainerInterface, ArrayAccess
28
{
29
    use DefinitionBuilder;
30
31
    /**
32
     * @var CircularDependencyDetection
33
     */
34
    protected $circularDependencyDetection;
35
36
    /**
37
     * @var DefinitionList
38
     */
39
    protected $definitions;
40
41
    /**
42
     * @var ResolvedList
43
     */
44
    protected $resolved;
45
46
    /**
47
     * @var DefinitionDetection
48
     */
49
    protected $detection;
50
51
    /**
52
     * @var DefinitionResolverInterface
53
     */
54
    protected $definitionResolver;
55
56
    /**
57
     * @var ClassResolver
58
     */
59
    protected $classResolver;
60
61
    /**
62
     * @var Reflector
63
     */
64
    protected $reflector;
65
66
    /**
67
     * @var AttributesInjection
68
     */
69
    protected $attributesInjection;
70
71
    /**
72
     * @var ServiceProviderManager
73
     */
74
    protected $providers;
75
76
    /**
77
     * @var ContainerComposite
78
     */
79
    protected $delegates;
80
81
    /**
82
     * @var bool
83
     */
84
    protected $defaultShared;
85
86
    /**
87
     * @var bool
88
     */
89
    protected $useAutowire;
90
91
    /**
92
     * @var bool
93
     */
94
    protected $useAttributes;
95
96
    public function __construct()
97
    {
98
        $this->reflector = new Reflector();
99
        $this->useAutowire = true;
100
        $this->defaultShared = true;
101
        $this->useAttributes = $this->reflector->attributesAvailable();
102
103
        $this->circularDependencyDetection = new CircularDependencyDetection();
104
        $this->definitions = new DefinitionList();
105
        $this->resolved = new ResolvedList();
106
        $this->delegates = new ContainerComposite();
107
        $this->providers = new ServiceProviderManager($this);
108
        $this->attributesInjection = new AttributesInjection($this, $this->reflector);
109
        $this->classResolver = new ReflectionResolver($this, $this->attributesInjection, $this->reflector);
110
        $this->definitionResolver = new DefinitionResolver($this, $this->resolved, $this->attributesInjection);
111
        $this->detection = new AutoDetection($this, $this->classResolver);
112
113
        $this->add(ContainerInterface::class, new RawDefinition($this));
114
        $this->add(self::class, new RawDefinition($this));
115
    }
116
117
    public function add(string $id, $value = null): DefinitionWrapper
118
    {
119
        $value = $value === null && count(func_get_args()) === 1 ? $id : $value;
120
        $definition = $this->detection->detect($value);
121
        $definition->setIdentity($id);
122
123
        $this->resolved->delete($id);
124
        $this->definitions->add($definition);
125
126
        if ($definition instanceof Shareable && $definition->isShared() === null) {
127
            $definition->setShared($this->defaultShared);
128
        }
129
130
        if ($definition instanceof RawDefinition) {
131
            $this->resolved->share($id, $definition->getValue());
132
        }
133
134
        return new DefinitionWrapper($definition);
135
    }
136
137
    public function has($id): bool
138
    {
139
        return
140
            $this->resolved->has($id)           ||
141
            $this->definitions->has($id)        ||
142
            $this->definitions->hasTag($id)     ||
143
            $this->providers->provides($id)     ||
144
            $this->delegates->has($id)          ||
145
            $this->shouldAutowireResolve($id);
146
    }
147
148
    public function get($id)
149
    {
150
        if ($this->resolved->has($id)) {
151
            return $this->resolved->get($id);
152
        }
153
154
        return
155
            $this->circularDependencyDetection
156
                ->execute($id, function () use ($id) {
157
                    return $this->resolve($id);
158
                });
159
    }
160
161
    protected function resolve(string $id)
162
    {
163
        $this->providers->registerLazyProviderFor($id);
164
165
        if ($this->definitions->has($id)) {
166
            $definition = $this->definitions->get($id);
167
            return $this->definitionResolver->resolve($definition);
168
        }
169
170
        if ($this->definitions->hasTag($id)) {
171
            $tagged = $this->definitions->getTagged($id);
172
            return $this->definitionResolver->resolveMany(...$tagged);
173
        }
174
175
        if ($this->shouldAutowireResolve($id)) {
176
            $definition =
177
                (new ClassDefinition($id))
178
                    ->setIdentity($id)
179
                    ->setShared($this->defaultShared)
180
                    ->setClassResolver($this->classResolver);
181
            return $this->definitionResolver->resolve($definition);
182
        }
183
184
        if ($this->delegates->has($id)) {
185
            return $this->delegates->get($id);
186
        }
187
188
        throw NotFoundException::noEntryWasFound($id);
189
    }
190
191
    public function injectDependency($object)
192
    {
193
        $this->attributesInjection->inject($object);
194
    }
195
196
    public function addProvider(ServiceProvider ...$providers): self
197
    {
198
        $this->providers->add(...$providers);
199
        return $this;
200
    }
201
202
    public function addDelegate(ContainerInterface $container, ?int $priority = null): self
203
    {
204
        $this->delegates->add($container, $priority);
205
        return $this;
206
    }
207
208
    public function useDefaultShared(bool $share): self
209
    {
210
        $this->defaultShared = $share;
211
        return $this;
212
    }
213
214
    public function defaultShared(): bool
215
    {
216
        return $this->defaultShared;
217
    }
218
219
    public function autowireEnabled(): bool
220
    {
221
        return $this->useAutowire;
222
    }
223
224
    public function useAutowire(bool $enabled): self
225
    {
226
        $this->useAutowire = $enabled;
227
        return $this;
228
    }
229
230
    public function attributesEnabled(): bool
231
    {
232
        return $this->useAttributes;
233
    }
234
235
    public function useAttributes(bool $useAttributes): self
236
    {
237
        if ($useAttributes == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
238
            $this->reflector->assertAttributesAvailable();
239
        }
240
241
        $this->useAttributes = $useAttributes;
242
        return $this;
243
    }
244
245
    protected function shouldAutowireResolve($id): bool
246
    {
247
        return $this->useAutowire && $this->classResolver->canResolve($id);
248
    }
249
250
    public function offsetExists($offset): bool
251
    {
252
        return $this->has($offset);
253
    }
254
255
    public function offsetGet($offset)
256
    {
257
        return $this->get($offset);
258
    }
259
260
    public function offsetSet($offset, $value)
261
    {
262
        $this->add($offset, $value);
263
    }
264
265
    public function offsetUnset($offset)
266
    {
267
        $this->definitions->delete($offset);
268
        $this->resolved->delete($offset);
269
    }
270
}
271