Passed
Push — main ( 4be1ac...bb044d )
by Breno
01:37
created

Container::extend()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
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 delete(string $id): void
192
    {
193
        $this->definitions->delete($id);
194
        $this->resolved->delete($id);
195
    }
196
197
    public function extend(string $id): DefinitionWrapper
198
    {
199
        return new DefinitionWrapper($this->definitions->get($id));
200
    }
201
202
    public function injectDependency($object)
203
    {
204
        $this->attributesInjection->inject($object);
205
    }
206
207
    public function addProvider(ServiceProvider ...$providers): self
208
    {
209
        $this->providers->add(...$providers);
210
        return $this;
211
    }
212
213
    public function addDelegate(ContainerInterface $container, ?int $priority = null): self
214
    {
215
        $this->delegates->add($container, $priority);
216
        return $this;
217
    }
218
219
    public function useDefaultShared(bool $share): self
220
    {
221
        $this->defaultShared = $share;
222
        return $this;
223
    }
224
225
    public function defaultShared(): bool
226
    {
227
        return $this->defaultShared;
228
    }
229
230
    public function autowireEnabled(): bool
231
    {
232
        return $this->useAutowire;
233
    }
234
235
    public function useAutowire(bool $enabled): self
236
    {
237
        $this->useAutowire = $enabled;
238
        return $this;
239
    }
240
241
    public function attributesEnabled(): bool
242
    {
243
        return $this->useAttributes;
244
    }
245
246
    public function useAttributes(bool $useAttributes): self
247
    {
248
        if ($useAttributes === true) {
249
            $this->reflector->assertAttributesAvailable();
250
        }
251
252
        $this->useAttributes = $useAttributes;
253
        return $this;
254
    }
255
256
    protected function shouldAutowireResolve($id): bool
257
    {
258
        return $this->useAutowire && $this->classResolver->canResolve($id);
259
    }
260
261
    public function offsetExists($offset): bool
262
    {
263
        return $this->has($offset);
264
    }
265
266
    public function offsetGet($offset)
267
    {
268
        return $this->get($offset);
269
    }
270
271
    public function offsetSet($offset, $value)
272
    {
273
        $this->add($offset, $value);
274
    }
275
276
    public function offsetUnset($offset)
277
    {
278
        $this->delete($offset);
279
    }
280
}
281