Completed
Push — master ( 6e2da3...5d3ec8 )
by Miloš
01:15
created

Container::has()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.2098

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
dl 0
loc 12
ccs 5
cts 7
cp 0.7143
crap 3.2098
rs 9.8666
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\ClassNotFoundException;
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 16
    public function __construct(DefinitionFactoryInterface $definitionFactory, ResolverFactoryInterface $resolverFactory)
62
    {
63 16
        $this->definitions = new ArrayObject();
64 16
        $this->entries = new ArrayObject();
65 16
        $this->resolving = new ArrayObject();
66 16
        $this->definitionFactory = $definitionFactory;
67 16
        $this->resolverFactory = $resolverFactory;
68 16
        $this->resolverFactory->setContainer($this);
69 16
    }
70
71
    /**
72
     * @inheritDoc
73
     */
74 15
    public function get($id)
75
    {
76 15
        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 13
        if ($this->entries->offsetExists($id)) {
82 1
            return $this->entries->offsetGet($id);
83
        }
84
85 13
        $entry = $this->resolveEntry($id);
86 9
        $this->entries->offsetSet($id, $entry);
87
88 9
        return $entry;
89
    }
90
91
    /**
92
     * @inheritDoc
93
     */
94 2
    public function has($id): bool
95
    {
96
        try {
97 2
            $this->get($id);
98
99 1
            return true;
100 1
        } catch (NotFoundExceptionInterface $ex) {
101 1
            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 ClassNotFoundException
114
     *
115
     * @return mixed
116
     */
117 13
    private function resolveEntry(string $id)
118
    {
119 13
        $definition = $this->getDefinition($id);
120
121 12
        $this->startResolving($id);
122 12
        $entry = $this->resolverFactory->create($definition)->resolve($definition);
123 9
        $this->endResolving($id);
124
125 9
        return $entry;
126
    }
127
128
    /**
129
     * @param bool $autowire
130
     */
131 16
    public function setAutowire(bool $autowire): void
132
    {
133 16
        $this->autowire = $autowire;
134 16
    }
135
136 10
    private function isAutowire(): bool
137
    {
138 10
        return $this->autowire;
139
    }
140
141
    /**
142
     * @param array $definitions
143
     *
144
     * @throws ContainerException
145
     */
146 10
    public function addDefinitions(array $definitions): void
147
    {
148 10
        foreach ($definitions as $id => $definition) {
149 10
            $this->addDefinition($id, $definition);
150
        }
151 10
    }
152
153
    /**
154
     * @param string $id
155
     * @param $definition
156
     *
157
     * @throws ContainerException
158
     */
159 11
    public function addDefinition(string $id, $definition): void
160
    {
161 11
        if ($this->definitions->offsetExists($id)) {
162 1
            throw new ContainerException("More than one definition is found for entry or class $id");
163
        }
164
165 11
        $this->definitions->offsetSet($id, $definition);
166 11
    }
167
168
    /**
169
     * @param string $id
170
     *
171
     * @throws DefinitionNotFoundException
172
     * @throws InvalidDefinitionException
173
     *
174
     * @return DefinitionInterface
175
     */
176 13
    private function getDefinition(string $id): DefinitionInterface
177
    {
178 13
        $definition = $this->definitions->offsetExists($id)
179 9
            ? $this->definitions->offsetGet($id)
180 13
            : null;
181
182 13
        if ($definition === null && $this->isAutowire()) {
183 10
            $definition = new ClassDefinition($id);
184
        }
185
186 13
        if ($definition === null) {
187
            throw DefinitionNotFoundException::create($id);
188
        }
189
190 13
        return $this->definitionFactory->create($definition);
191
    }
192
193
    /**
194
     * @param string $id
195
     *
196
     * @throws CircularDependencyFoundException
197
     *
198
     * @return void
199
     */
200 12
    private function startResolving(string $id): void
201
    {
202 12
        if ($this->resolving->offsetExists($id)) {
203 1
            throw CircularDependencyFoundException::create($id);
204
        }
205
206 12
        $this->resolving->offsetSet($id, true);
207 12
    }
208
209
    /**
210
     * @param string $id
211
     *
212
     * @return void
213
     */
214 9
    private function endResolving(string $id): void
215
    {
216 9
        if ($this->resolving->offsetExists($id)) {
217 9
            $this->resolving->offsetUnset($id);
218
        }
219 9
    }
220
}
221