Completed
Push — master ( 5ffcc1...94ed56 )
by Miloš
01:45
created

Container::setDefinitions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
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 18
    public function __construct(DefinitionFactoryInterface $definitionFactory, ResolverFactoryInterface $resolverFactory)
62
    {
63 18
        $this->definitions = new ArrayObject();
64 18
        $this->entries = new ArrayObject();
65 18
        $this->resolving = new ArrayObject();
66 18
        $this->definitionFactory = $definitionFactory;
67 18
        $this->resolverFactory = $resolverFactory;
68 18
        $this->resolverFactory->setContainer($this);
69 18
    }
70
71
    /**
72
     * @inheritDoc
73
     */
74 18
    public function get($id)
75
    {
76 18
        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 16
        if ($this->entries->offsetExists($id)) {
82 1
            return $this->entries->offsetGet($id);
83
        }
84
85 16
        $entry = $this->resolveEntry($id);
86 9
        $this->entries->offsetSet($id, $entry);
87
88 9
        return $entry;
89
    }
90
91
    /**
92
     * @inheritDoc
93
     */
94 3
    public function has($id): bool
95
    {
96
        try {
97 3
            $this->get($id);
98
99 1
            return true;
100 2
        } catch (NotFoundExceptionInterface $ex) {
101 1
            return false;
102 1
        } catch (ContainerExceptionInterface $ex) {
103 1
            return true;
104
        }
105
    }
106
107
    /**
108
     * @param string $id
109
     *
110
     * @throws CircularDependencyFoundException
111
     * @throws ClassNotFoundException
112
     * @throws ContainerException
113
     * @throws DefinitionNotFoundException
114
     * @throws InvalidDefinitionException
115
     *
116
     * @return mixed
117
     */
118 16
    private function resolveEntry(string $id)
119
    {
120 16
        $definition = $this->getDefinition($id);
121
122 14
        $this->startResolving($id);
123 14
        $entry = $this->resolverFactory->create($definition)->resolve($definition);
124 9
        $this->endResolving($id);
125
126 9
        return $entry;
127
    }
128
129
    /**
130
     * @param bool $autowire
131
     */
132 18
    public function setAutowire(bool $autowire): void
133
    {
134 18
        $this->autowire = $autowire;
135 18
    }
136
137 10
    private function isAutowire(): bool
138
    {
139 10
        return $this->autowire;
140
    }
141
142
    /**
143
     * @param ArrayObject $definitions
144
     */
145 18
    public function setDefinitions(ArrayObject $definitions): void
146
    {
147 18
        $this->definitions = $definitions;
148 18
    }
149
150
    /**
151
     * @param string $id
152
     *
153
     * @throws DefinitionNotFoundException
154
     * @throws InvalidDefinitionException
155
     *
156
     * @return DefinitionInterface
157
     */
158 16
    private function getDefinition(string $id): DefinitionInterface
159
    {
160 16
        $definition = $this->definitions->offsetExists($id)
161 12
            ? $this->definitions->offsetGet($id)
162 16
            : null;
163
164 16
        if ($definition === null && $this->isAutowire()) {
165 9
            $definition = new ClassDefinition($id);
166
        }
167
168 16
        if ($definition === null) {
169 1
            throw DefinitionNotFoundException::create($id);
170
        }
171
172 15
        return $this->definitionFactory->create($definition);
173
    }
174
175
    /**
176
     * @param string $id
177
     *
178
     * @throws CircularDependencyFoundException
179
     *
180
     * @return void
181
     */
182 14
    private function startResolving(string $id): void
183
    {
184 14
        if ($this->resolving->offsetExists($id)) {
185 1
            throw CircularDependencyFoundException::create($id);
186
        }
187
188 14
        $this->resolving->offsetSet($id, true);
189 14
    }
190
191
    /**
192
     * @param string $id
193
     *
194
     * @return void
195
     */
196 9
    private function endResolving(string $id): void
197
    {
198 9
        if ($this->resolving->offsetExists($id)) {
199 9
            $this->resolving->offsetUnset($id);
200
        }
201 9
    }
202
}
203