Completed
Push — master ( 63f4dd...766eb0 )
by Miloš
01:54
created

Container::getDefinition()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 5.0342

Importance

Changes 0
Metric Value
cc 5
nc 8
nop 1
dl 0
loc 16
ccs 8
cts 9
cp 0.8889
crap 5.0342
rs 9.4222
c 0
b 0
f 0
1
<?php
2
3
namespace Laganica\Di;
4
5
use ArrayObject;
6
use InvalidArgumentException;
7
use Laganica\Di\Definition\ClassDefinition;
8
use Laganica\Di\Definition\DefinitionFactoryInterface;
9
use Laganica\Di\Definition\DefinitionInterface;
10
use Laganica\Di\Exception\CircularDependencyFoundException;
11
use Laganica\Di\Exception\ContainerException;
12
use Laganica\Di\Exception\DefinitionNotFoundException;
13
use Laganica\Di\Exception\InvalidDefinitionException;
14
use Laganica\Di\Exception\NotFoundException;
15
use Laganica\Di\Resolver\ResolverFactoryInterface;
16
use Psr\Container\ContainerExceptionInterface;
17
use Psr\Container\ContainerInterface;
18
use Psr\Container\NotFoundExceptionInterface;
19
20
/**
21
 * Class Container
22
 *
23
 * @package Laganica\Di
24
 */
25
class Container implements ContainerInterface
26
{
27
    /**
28
     * @var ArrayObject
29
     */
30
    private $definitions;
31
32
    /**
33
     * @var ArrayObject
34
     */
35
    private $entries;
36
37
    /**
38
     * @var ArrayObject
39
     */
40
    private $resolving;
41
42
    /**
43
     * @var bool
44
     */
45
    private $autowire = true;
46
47
    /**
48
     * @var ResolverFactoryInterface
49
     */
50
    private $resolverFactory;
51
52
    /**
53
     * @var DefinitionFactoryInterface
54
     */
55
    private $definitionFactory;
56
57
    /**
58
     * @param DefinitionFactoryInterface $definitionFactory
59
     * @param ResolverFactoryInterface $resolverFactory
60
     */
61 10
    public function __construct(DefinitionFactoryInterface $definitionFactory, ResolverFactoryInterface $resolverFactory)
62
    {
63 10
        $this->definitions = new ArrayObject();
64 10
        $this->entries = new ArrayObject();
65 10
        $this->resolving = new ArrayObject();
66 10
        $this->definitionFactory = $definitionFactory;
67 10
        $this->resolverFactory = $resolverFactory;
68 10
        $this->resolverFactory->setContainer($this);
69 10
    }
70
71
    /**
72
     * @inheritDoc
73
     */
74 10
    public function get($id)
75
    {
76 10
        if (!is_string($id)) {
77 2
            $type = is_object($id) ? get_class($id) : gettype($id);
78 2
            throw new InvalidArgumentException("Argument \$id must be string, $type given");
79
        }
80
81 8
        if ($this->entries->offsetExists($id)) {
82
            return $this->entries->offsetGet($id);
83
        }
84
85 8
        $entry = $this->resolveEntry($id);
86 7
        $this->entries->offsetSet($id, $entry);
87
88 7
        return $entry;
89
    }
90
91
    /**
92
     * @inheritDoc
93
     */
94
    public function has($id): bool
95
    {
96
        try {
97
            $this->get($id);
98
99
            return true;
100
        } catch (NotFoundExceptionInterface $ex) {
101
            return false;
102
        } catch (ContainerExceptionInterface $ex) {
103
            return true;
104
        }
105
    }
106
107
    /**
108
     * @param string $id
109
     *
110
     * @throws ContainerException
111
     * @throws DefinitionNotFoundException
112
     * @throws InvalidDefinitionException
113
     * @throws NotFoundException
114
     *
115
     * @return mixed
116
     */
117 8
    private function resolveEntry(string $id)
118
    {
119 8
        $definition = $this->getDefinition($id);
120
121 8
        $this->startResolving($id);
122 8
        $entry = $this->resolverFactory->create($definition)->resolve($definition);
123 7
        $this->endResolving($id);
124
125 7
        return $entry;
126
    }
127
128
    /**
129
     * @param bool $autowire
130
     */
131 10
    public function setAutowire(bool $autowire): void
132
    {
133 10
        $this->autowire = $autowire;
134 10
    }
135
136 7
    private function isAutowire(): bool
137
    {
138 7
        return $this->autowire;
139
    }
140
141
    /**
142
     * @param array $definitions
143
     *
144
     * @throws ContainerException
145
     */
146 8
    public function addDefinitions(array $definitions): void
147
    {
148 8
        foreach ($definitions as $id => $definition) {
149 8
            $this->addDefinition($id, $definition);
150
        }
151 8
    }
152
153
    /**
154
     * @param string $id
155
     * @param $definition
156
     *
157
     * @throws ContainerException
158
     */
159 8
    public function addDefinition(string $id, $definition): void
160
    {
161 8
        if ($this->definitions->offsetExists($id)) {
162
            throw new ContainerException("More than one definition is found for entry or class $id");
163
        }
164
165 8
        $this->definitions->offsetSet($id, $definition);
166 8
    }
167
168
    /**
169
     * @param string $id
170
     *
171
     * @throws DefinitionNotFoundException
172
     * @throws InvalidDefinitionException
173
     *
174
     * @return DefinitionInterface
175
     */
176 8
    private function getDefinition(string $id): DefinitionInterface
177
    {
178 8
        $definition = $this->definitions->offsetExists($id)
179 7
            ? $this->definitions->offsetGet($id)
180 8
            : null;
181
182 8
        if ($definition === null && $this->isAutowire()) {
183 7
            $definition = new ClassDefinition($id);
184
        }
185
186 8
        if ($definition === null) {
187
            throw DefinitionNotFoundException::create($id);
188
        }
189
190 8
        return $this->definitionFactory->create($definition);
191
    }
192
193
    /**
194
     * @param string $id
195
     *
196
     * @throws CircularDependencyFoundException
197
     *
198
     * @return void
199
     */
200 8
    private function startResolving(string $id): void
201
    {
202 8
        if ($this->resolving->offsetExists($id)) {
203 1
            throw CircularDependencyFoundException::create($id);
204
        }
205
206 8
        $this->resolving->offsetSet($id, true);
207 8
    }
208
209
    /**
210
     * @param string $id
211
     *
212
     * @return void
213
     */
214 7
    private function endResolving(string $id): void
215
    {
216 7
        if ($this->resolving->offsetExists($id)) {
217 7
            $this->resolving->offsetUnset($id);
218
        }
219 7
    }
220
}
221