Passed
Push — main ( 00e164...d31815 )
by Breno
01:52
created

Container   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 262
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
eloc 100
c 5
b 0
f 0
dl 0
loc 262
rs 9.1199
wmc 41

22 Methods

Rating   Name   Duplication   Size   Complexity  
A has() 0 10 6
A offsetUnset() 0 3 1
A shouldAutowireResolve() 0 3 2
A useDefaultShared() 0 4 1
A addDelegate() 0 4 1
A defaultShared() 0 3 1
A definition() 0 3 1
A offsetGet() 0 3 1
A assertString() 0 7 3
A get() 0 10 2
A delete() 0 4 1
A injectDependency() 0 3 1
A useAttributes() 0 8 2
A add() 0 18 6
A offsetExists() 0 3 1
A autowireEnabled() 0 3 1
A useAutowire() 0 4 1
A resolve() 0 28 5
A attributesEnabled() 0 3 1
A __construct() 0 19 1
A offsetSet() 0 3 1
A addProvider() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Container often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Container, and based on these observations, apply Extract Interface, too.

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\ContainerException;
23
use Habemus\Exception\NotFoundException;
24
use Habemus\ServiceProvider\ServiceProvider;
25
use Habemus\ServiceProvider\ServiceProviderManager;
26
use Psr\Container\ContainerInterface;
27
28
class Container implements ContainerInterface, ArrayAccess
29
{
30
    use DefinitionBuilder;
31
32
    /**
33
     * @var CircularDependencyDetection
34
     */
35
    protected $circularDependencyDetection;
36
37
    /**
38
     * @var DefinitionList
39
     */
40
    protected $definitions;
41
42
    /**
43
     * @var ResolvedList
44
     */
45
    protected $resolved;
46
47
    /**
48
     * @var DefinitionDetection
49
     */
50
    protected $detection;
51
52
    /**
53
     * @var DefinitionResolverInterface
54
     */
55
    protected $definitionResolver;
56
57
    /**
58
     * @var ClassResolver
59
     */
60
    protected $classResolver;
61
62
    /**
63
     * @var Reflector
64
     */
65
    protected $reflector;
66
67
    /**
68
     * @var AttributesInjection
69
     */
70
    protected $attributesInjection;
71
72
    /**
73
     * @var ServiceProviderManager
74
     */
75
    protected $providers;
76
77
    /**
78
     * @var ContainerComposite
79
     */
80
    protected $delegates;
81
82
    /**
83
     * @var bool
84
     */
85
    protected $defaultShared;
86
87
    /**
88
     * @var bool
89
     */
90
    protected $useAutowire;
91
92
    /**
93
     * @var bool
94
     */
95
    protected $useAttributes;
96
97
    public function __construct()
98
    {
99
        $this->reflector = new Reflector();
100
        $this->useAutowire = true;
101
        $this->defaultShared = true;
102
        $this->useAttributes = $this->reflector->attributesAvailable();
103
104
        $this->circularDependencyDetection = new CircularDependencyDetection();
105
        $this->definitions = new DefinitionList();
106
        $this->resolved = new ResolvedList();
107
        $this->delegates = new ContainerComposite();
108
        $this->providers = new ServiceProviderManager($this);
109
        $this->attributesInjection = new AttributesInjection($this, $this->reflector);
110
        $this->classResolver = new ReflectionResolver($this, $this->attributesInjection, $this->reflector);
111
        $this->definitionResolver = new DefinitionResolver($this, $this->resolved, $this->attributesInjection);
112
        $this->detection = new AutoDetection($this, $this->classResolver);
113
114
        $this->add(ContainerInterface::class, new RawDefinition($this));
115
        $this->add(self::class, new RawDefinition($this));
116
    }
117
118
    public function add(string $id, $value = null): DefinitionWrapper
119
    {
120
        $value = $value === null && count(func_get_args()) === 1 ? $id : $value;
121
        $definition = $this->detection->detect($value);
122
        $definition->setIdentity($id);
123
124
        $this->resolved->delete($id);
125
        $this->definitions->add($definition);
126
127
        if ($definition instanceof Shareable && $definition->isShared() === null) {
128
            $definition->setShared($this->defaultShared);
129
        }
130
131
        if ($definition instanceof RawDefinition) {
132
            $this->resolved->share($id, $definition->getValue());
133
        }
134
135
        return new DefinitionWrapper($definition);
136
    }
137
138
    public function has($id): bool
139
    {
140
        $this->assertString($id);
141
        return
142
            $this->resolved->has($id)           ||
143
            $this->definitions->has($id)        ||
144
            $this->definitions->hasTag($id)     ||
145
            $this->providers->provides($id)     ||
146
            $this->delegates->has($id)          ||
147
            $this->shouldAutowireResolve($id);
148
    }
149
150
    public function get($id)
151
    {
152
        $this->assertString($id);
153
        if ($this->resolved->has($id)) {
154
            return $this->resolved->get($id);
155
        }
156
157
        return
158
            $this->circularDependencyDetection->execute($id, function () use ($id) {
159
                return $this->resolve($id);
160
            });
161
    }
162
163
    protected function resolve(string $id)
164
    {
165
        $this->providers->registerLazyProviderFor($id);
166
167
        if ($this->definitions->has($id)) {
168
            $definition = $this->definitions->get($id);
169
            return $this->definitionResolver->resolve($definition);
170
        }
171
172
        if ($this->definitions->hasTag($id)) {
173
            $tagged = $this->definitions->getTagged($id);
174
            return $this->definitionResolver->resolveMany(...$tagged);
175
        }
176
177
        if ($this->shouldAutowireResolve($id)) {
178
            $definition =
179
                (new ClassDefinition($id))
180
                    ->setIdentity($id)
181
                    ->setShared($this->defaultShared)
182
                    ->setClassResolver($this->classResolver);
183
            return $this->definitionResolver->resolve($definition);
184
        }
185
186
        if ($this->delegates->has($id)) {
187
            return $this->delegates->get($id);
188
        }
189
190
        throw NotFoundException::noEntryWasFound($id);
191
    }
192
193
    public function delete(string $id): void
194
    {
195
        $this->definitions->delete($id);
196
        $this->resolved->delete($id);
197
    }
198
199
    public function definition(string $id): DefinitionWrapper
200
    {
201
        return new DefinitionWrapper($this->definitions->get($id));
202
    }
203
204
    public function injectDependency($object)
205
    {
206
        $this->attributesInjection->inject($object);
207
    }
208
209
    public function addProvider(ServiceProvider ...$providers): self
210
    {
211
        $this->providers->add(...$providers);
212
        return $this;
213
    }
214
215
    public function addDelegate(ContainerInterface $container, ?int $priority = null): self
216
    {
217
        $this->delegates->add($container, $priority);
218
        return $this;
219
    }
220
221
    public function useDefaultShared(bool $share): self
222
    {
223
        $this->defaultShared = $share;
224
        return $this;
225
    }
226
227
    public function defaultShared(): bool
228
    {
229
        return $this->defaultShared;
230
    }
231
232
    public function autowireEnabled(): bool
233
    {
234
        return $this->useAutowire;
235
    }
236
237
    public function useAutowire(bool $enabled): self
238
    {
239
        $this->useAutowire = $enabled;
240
        return $this;
241
    }
242
243
    public function attributesEnabled(): bool
244
    {
245
        return $this->useAttributes;
246
    }
247
248
    public function useAttributes(bool $useAttributes): self
249
    {
250
        if ($useAttributes) {
251
            $this->reflector->assertAttributesAvailable();
252
        }
253
254
        $this->useAttributes = $useAttributes;
255
        return $this;
256
    }
257
258
    protected function shouldAutowireResolve($id): bool
259
    {
260
        return $this->useAutowire && $this->classResolver->canResolve($id);
261
    }
262
263
    public function offsetExists($offset): bool
264
    {
265
        return $this->has($offset);
266
    }
267
268
    public function offsetGet($offset)
269
    {
270
        return $this->get($offset);
271
    }
272
273
    public function offsetSet($offset, $value)
274
    {
275
        $this->add($offset, $value);
276
    }
277
278
    public function offsetUnset($offset)
279
    {
280
        $this->delete($offset);
281
    }
282
283
    protected function assertString($value): void
284
    {
285
        if (!is_string($value)) {
286
            throw new ContainerException(
287
                sprintf(
288
                    "Expected string. Got: %s.",
289
                    is_object($value) ? get_class($value) : gettype($value)
290
                )
291
            );
292
        }
293
    }
294
}
295