Container::get()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
nc 4
nop 1
dl 0
loc 16
ccs 9
cts 9
cp 1
crap 4
rs 9.7333
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file is part of the Container package.
5
 *
6
 * Copyright (c) Miloš Đurić <[email protected]>
7
 *
8
 * For full copyright and license information, please refer to the LICENSE file,
9
 * located at the package root folder.
10
 */
11
12
namespace Laganica\Di;
13
14
use ArrayObject;
15
use InvalidArgumentException;
16
use Laganica\Di\Definition\ClassDefinition;
17
use Laganica\Di\Definition\DefinitionFactoryInterface;
18
use Laganica\Di\Definition\DefinitionInterface;
19
use Laganica\Di\Exception\CircularDependencyFoundException;
20
use Laganica\Di\Exception\ContainerException;
21
use Laganica\Di\Exception\DefinitionNotFoundException;
22
use Laganica\Di\Exception\InvalidDefinitionException;
23
use Laganica\Di\Exception\ClassNotFoundException;
24
use Laganica\Di\Resolver\ResolverFactoryInterface;
25
use Psr\Container\ContainerExceptionInterface;
26
use Psr\Container\ContainerInterface;
27
use Psr\Container\NotFoundExceptionInterface;
28
29
/**
30
 * Class Container
31
 *
32
 * @package Laganica\Di
33
 */
34
class Container implements ContainerInterface
35
{
36
    /**
37
     * @var ArrayObject
38
     */
39
    private $definitions;
40
41
    /**
42
     * @var ArrayObject
43
     */
44
    private $entries;
45
46
    /**
47
     * @var ArrayObject
48
     */
49
    private $resolving;
50
51
    /**
52
     * @var bool
53
     */
54
    private $useAutowiring;
55
56
    /**
57
     * @var bool
58
     */
59
    private $useAnnotations;
60
61
    /**
62
     * @var ResolverFactoryInterface
63
     */
64
    private $resolverFactory;
65
66
    /**
67
     * @var DefinitionFactoryInterface
68
     */
69
    private $definitionFactory;
70
71
    /**
72
     * @param DefinitionFactoryInterface $definitionFactory
73
     * @param ResolverFactoryInterface $resolverFactory
74
     */
75 21
    public function __construct(DefinitionFactoryInterface $definitionFactory, ResolverFactoryInterface $resolverFactory)
76
    {
77 21
        $this->definitions = new ArrayObject();
78 21
        $this->entries = new ArrayObject();
79 21
        $this->resolving = new ArrayObject();
80 21
        $this->definitionFactory = $definitionFactory;
81 21
        $this->resolverFactory = $resolverFactory;
82 21
        $this->resolverFactory->setContainer($this);
83 21
    }
84
85
    /**
86
     * @inheritDoc
87
     */
88 20
    public function get($id)
89
    {
90 20
        if (!is_string($id)) {
91 2
            $type = is_object($id) ? get_class($id) : gettype($id);
92 2
            throw new InvalidArgumentException("Argument \$id must be string, $type given");
93
        }
94
95 18
        if ($this->entries->offsetExists($id)) {
96 2
            return $this->entries->offsetGet($id);
97
        }
98
99 18
        $entry = $this->resolveEntry($id);
100 10
        $this->entries->offsetSet($id, $entry);
101
102 10
        return $entry;
103
    }
104
105
    /**
106
     * Creates new entry by its identifier and returns it.
107
     *
108
     * @param string $id Identifier of the entry to look for.
109
     *
110
     * @throws NotFoundExceptionInterface  No entry was found for **this** identifier.
111
     * @throws ContainerExceptionInterface Error while creating the entry.
112
     *
113
     * @return mixed Entry.
114
     */
115 1
    public function make(string $id)
116
    {
117 1
        return $this->resolveEntry($id);
118
    }
119
120
    /**
121
     * @inheritDoc
122
     */
123 3
    public function has($id): bool
124
    {
125
        try {
126 3
            $this->get($id);
127
128 1
            return true;
129 2
        } catch (NotFoundExceptionInterface $ex) {
130 1
            return false;
131 1
        } catch (ContainerExceptionInterface $ex) {
132 1
            return true;
133
        }
134
    }
135
136
    /**
137
     * @param string $id
138
     *
139
     * @throws CircularDependencyFoundException
140
     * @throws ClassNotFoundException
141
     * @throws ContainerException
142
     * @throws DefinitionNotFoundException
143
     * @throws InvalidDefinitionException
144
     *
145
     * @return mixed
146
     */
147 19
    private function resolveEntry(string $id)
148
    {
149 19
        $definition = $this->getDefinition($id);
150
151 17
        $this->startResolving($id);
152 17
        $entry = $this->resolverFactory->create($definition)->resolve($definition);
153 11
        $this->endResolving($id);
154
155 11
        return $entry;
156
    }
157
158
    /**
159
     * @param bool $useAutowiring
160
     */
161 21
    public function useAutowiring(bool $useAutowiring): void
162
    {
163 21
        $this->useAutowiring = $useAutowiring;
164 21
    }
165
166
    /**
167
     * @return bool
168
     */
169 13
    private function hasAutowiringEnabled(): bool
170
    {
171 13
        return $this->useAutowiring;
172
    }
173
174
    /**
175
     * @param bool $useAnnotations
176
     */
177 21
    public function useAnnotations(bool $useAnnotations): void
178
    {
179 21
        $this->useAnnotations = $useAnnotations;
180 21
    }
181
182
    /**
183
     * @return bool
184
     */
185 10
    public function hasAnnotationsEnabled(): bool
186
    {
187 10
        return $this->useAnnotations;
188
    }
189
190
    /**
191
     * @param ArrayObject $definitions
192
     */
193 21
    public function setDefinitions(ArrayObject $definitions): void
194
    {
195 21
        $this->definitions = $definitions;
196 21
    }
197
198
    /**
199
     * @param string $id
200
     *
201
     * @throws DefinitionNotFoundException
202
     * @throws InvalidDefinitionException
203
     *
204
     * @return DefinitionInterface
205
     */
206 19
    private function getDefinition(string $id): DefinitionInterface
207
    {
208 19
        $definition = $this->definitions->offsetExists($id)
209 13
            ? $this->definitions->offsetGet($id)
210 19
            : null;
211
212 19
        if ($definition === null && $this->hasAutowiringEnabled()) {
213 12
            $definition = new ClassDefinition($id);
214
        }
215
216 19
        if ($definition === null) {
217 1
            throw DefinitionNotFoundException::create($id);
218
        }
219
220 18
        return $this->definitionFactory->create($definition);
221
    }
222
223
    /**
224
     * @param string $id
225
     *
226
     * @throws CircularDependencyFoundException
227
     *
228
     * @return void
229
     */
230 17
    private function startResolving(string $id): void
231
    {
232 17
        if ($this->resolving->offsetExists($id)) {
233 1
            throw CircularDependencyFoundException::create($id);
234
        }
235
236 17
        $this->resolving->offsetSet($id, true);
237 17
    }
238
239
    /**
240
     * @param string $id
241
     *
242
     * @return void
243
     */
244 11
    private function endResolving(string $id): void
245
    {
246 11
        if ($this->resolving->offsetExists($id)) {
247 11
            $this->resolving->offsetUnset($id);
248
        }
249 11
    }
250
}
251