Completed
Pull Request — master (#96)
by Andreas
19:26 queued 05:41
created

Container::isProtected()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
namespace League\Container;
4
5
use Interop\Container\ContainerInterface as InteropContainerInterface;
6
use League\Container\Definition\DefinitionFactory;
7
use League\Container\Definition\DefinitionFactoryInterface;
8
use League\Container\Definition\DefinitionInterface;
9
use League\Container\Exception\ProtectedException;
10
use League\Container\Exception\NotFoundException;
11
use League\Container\Inflector\InflectorAggregate;
12
use League\Container\Inflector\InflectorAggregateInterface;
13
use League\Container\ServiceProvider\ServiceProviderAggregate;
14
use League\Container\ServiceProvider\ServiceProviderAggregateInterface;
15
16
class Container implements ContainerInterface
17
{
18
    /**
19
     * @var \League\Container\Definition\DefinitionFactoryInterface
20
     */
21
    protected $definitionFactory;
22
23
    /**
24
     * @var \League\Container\Definition\DefinitionInterface[]
25
     */
26
    protected $definitions = [];
27
28
    /**
29
     * @var \League\Container\Definition\DefinitionInterface[]
30
     */
31
    protected $sharedDefinitions = [];
32
33
    /**
34
     * @var \League\Container\Inflector\InflectorAggregateInterface
35
     */
36
    protected $inflectors;
37
38
    /**
39
     * @var \League\Container\ServiceProvider\ServiceProviderAggregateInterface
40
     */
41
    protected $providers;
42
43
    /**
44
     * @var array
45
     */
46
    protected $shared = [];
47
48
    /**
49
     * @var \Interop\Container\ContainerInterface[]
50
     */
51
    protected $delegates = [];
52
53
    /**
54
     * @var bool
55
     */
56
    protected $isProtected = false;
57
58
    /**
59 42
     * Constructor.
60
     *
61
     * @param \League\Container\ServiceProvider\ServiceProviderAggregateInterface|null $providers
62
     * @param \League\Container\Inflector\InflectorAggregateInterface|null             $inflectors
63
     * @param \League\Container\Definition\DefinitionFactoryInterface|null             $definitionFactory
64
     */
65 42
    public function __construct(
66 42
        ServiceProviderAggregateInterface $providers         = null,
67 42
        InflectorAggregateInterface       $inflectors        = null,
68
        DefinitionFactoryInterface        $definitionFactory = null
69 42
    ) {
70 42
        // set required dependencies
71 42
        $this->providers         = (is_null($providers))
72
                                 ? (new ServiceProviderAggregate)->setContainer($this)
73 42
                                 : $providers->setContainer($this);
74 42
75 42
        $this->inflectors        = (is_null($inflectors))
76 42
                                 ? (new InflectorAggregate)->setContainer($this)
77
                                 : $inflectors->setContainer($this);
78
79
        $this->definitionFactory = (is_null($definitionFactory))
80
                                 ? (new DefinitionFactory)->setContainer($this)
81 30
                                 : $definitionFactory->setContainer($this);
82
    }
83 30
84
    /**
85 30
     * {@inheritdoc}
86 12
     */
87 12
    public function get($alias, array $args = [])
88 12
    {
89
        $service = $this->getFromThisContainer($alias, $args);
90 30
91 24
        if ($service === false && $this->providers->provides($alias)) {
92
            $this->providers->register($alias);
93
            $service = $this->getFromThisContainer($alias, $args);
94 12
        }
95 6
96
        if ($service !== false) {
97
            return $service;
98 6
        }
99 6
100 6
        if ($resolved = $this->getFromDelegate($alias, $args)) {
101
            return $this->inflectors->inflect($resolved);
102
        }
103
104
        throw new NotFoundException(
105
            sprintf('Alias (%s) is not being managed by the container', $alias)
106 18
        );
107
    }
108 18
109 12
    /**
110
     * {@inheritdoc}
111
     */
112 9
    public function has($alias)
113 3
    {
114
        if (array_key_exists($alias, $this->definitions) || $this->hasShared($alias)) {
115
            return true;
116 9
        }
117
118
        if ($this->providers->provides($alias)) {
119
            return true;
120
        }
121
122
        return $this->hasInDelegate($alias);
123
    }
124
125
    /**
126 36
     * Returns a boolean to determine if the container has a shared instance of an alias.
127
     *
128 36
     * @param  string  $alias
129
     * @param  boolean $resolved
130 36
     * @return boolean
131
     */
132
    public function hasShared($alias, $resolved = false)
133
    {
134
        $shared = ($resolved === false) ? array_merge($this->shared, $this->sharedDefinitions) : $this->shared;
135
136 30
        return (array_key_exists($alias, $shared));
137
    }
138 30
139 3
    /**
140 3
     * {@inheritdoc}
141
     */
142 30
    public function add($alias, $concrete = null, $share = false)
143
    {
144 30
        if ($this->isProtected()) {
145 27
            throw new ProtectedException('Container has been protected, adding services is not allowed.');
146 9
        }
147 9
148 21
        if (is_null($concrete)) {
149
            $concrete = $alias;
150
        }
151 27
152
        $definition = $this->definitionFactory->getDefinition($alias, $concrete);
153
154
        if ($definition instanceof DefinitionInterface) {
155 3
            if ($share === false) {
156 3
                $this->definitions[$alias] = $definition;
157
            } else {
158
                $this->sharedDefinitions[$alias] = $definition;
159
            }
160
161 21
            return $definition;
162
        }
163 21
164
        // dealing with a value that cannot build a definition
165
        $this->shared[$alias] = $concrete;
166
    }
167
168
    /**
169 12
     * {@inheritdoc}
170
     */
171 12
    public function share($alias, $concrete = null)
172
    {
173 12
        return $this->add($alias, $concrete, true);
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179 6
    public function addServiceProvider($provider)
180
    {
181 6
        if ($this->isProtected()) {
182 3
            throw new ProtectedException('Container has been protected, adding service providers is not allowed');
183 3
        }
184
185 6
        $this->providers->add($provider);
186 3
187
        return $this;
188
    }
189 6
190 3
    /**
191
     * {@inheritdoc}
192
     */
193 3
    public function extend($alias)
194 3
    {
195 3
        if ($this->isProtected()) {
196
            throw new ProtectedException('Container has been protected, extending services is not allowed');
197
        }
198
        
199
        if ($this->providers->provides($alias)) {
200
            $this->providers->register($alias);
201
        }
202
203
        if (array_key_exists($alias, $this->definitions)) {
204
            return $this->definitions[$alias];
205
        }
206
207
        if (array_key_exists($alias, $this->sharedDefinitions)) {
208
            return $this->sharedDefinitions[$alias];
209
        }
210
211
        throw new NotFoundException(
212
            sprintf('Unable to extend alias (%s) as it is not being managed as a definition', $alias)
213
        );
214
    }
215
216
    /**
217
     * {@inheritdoc}
218
     */
219
    public function inflector($type, callable $callback = null)
220
    {
221 9
        if ($this->isProtected()) {
222
            throw new ProtectedException('Container has been protected, adding inflectors is not allowed');
223 9
        }
224
225 9
        return $this->inflectors->add($type, $callback);
226 3
    }
227 3
228
    /**
229 9
     * {@inheritdoc}
230
     */
231
    public function call(callable $callable, array $args = [])
232
    {
233
        return (new ReflectionContainer)->setContainer($this)->call($callable, $args);
234
    }
235
236
    /**
237
     * Marks the container as protected.
238 9
     *
239
     * Prevents overriding services and delegating to other containers once the container is marked as protected.
240 9
     */
241 3
    public function protect()
242 3
    {
243
        $this->isProtected = true;
244 9
    }
245
246 6
    /**
247
     * Marks the container as unprotected.
248
     *
249
     * Allows overriding services and delegating to other containers (this is the default).
250
     */
251
    public function unprotect()
252
    {
253
        $this->isProtected = false;
254
    }
255
256 12
    /**
257
     * Returns true if the container is protected.
258 12
     *
259 6
     * @return bool
260 6
     */
261
    public function isProtected()
262
    {
263 3
        return $this->isProtected;
264 6
    }
265
266 6
    /**
267
     * Delegate a backup container to be checked for services if it
268
     * cannot be resolved via this container.
269
     *
270
     * @param  \Interop\Container\ContainerInterface $container
271
     * @return $this
272
     */
273
    public function delegate(InteropContainerInterface $container)
274
    {
275
        if ($this->isProtected()) {
276 30
            throw new ProtectedException('Container has been protected, delegating to additional containers is not allowed');
277
        }
278 30
279 9
        $this->delegates[] = $container;
280
281
        if ($container instanceof ImmutableContainerAwareInterface) {
282 27
            $container->setContainer($this);
283 15
        }
284 15
285 15
        return $this;
286
    }
287
288 24
    /**
289 6
     * Returns true if service is registered in one of the delegated backup containers.
290 6
     *
291 6
     * @param  string $alias
292
     * @return boolean
293
     */
294 21
    public function hasInDelegate($alias)
295
    {
296
        foreach ($this->delegates as $container) {
297
            if ($container->has($alias)) {
298
                return true;
299
            }
300
        }
301
302
        return false;
303
    }
304
305
    /**
306
     * Attempt to get a service from the stack of delegated backup containers.
307
     *
308
     * @param  string $alias
309
     * @param  array  $args
310
     * @return mixed
311
     */
312
    protected function getFromDelegate($alias, array $args = [])
313
    {
314
        foreach ($this->delegates as $container) {
315
            if ($container->has($alias)) {
316
                return $container->get($alias, $args);
317
            }
318
319
            continue;
320
        }
321
322
        return false;
323
    }
324
325
    /**
326
     * Get a service that has been registered in this container.
327
     *
328
     * @param  string $alias
329
     * @param  array $args
330
     * @return mixed
331
     */
332
    protected function getFromThisContainer($alias, array $args = [])
333
    {
334
        if ($this->hasShared($alias, true)) {
335
            return $this->inflectors->inflect($this->shared[$alias]);
336
        }
337
338
        if (array_key_exists($alias, $this->sharedDefinitions)) {
339
            $shared = $this->inflectors->inflect($this->sharedDefinitions[$alias]->build());
340
            $this->shared[$alias] = $shared;
341
            return $shared;
342
        }
343
344
        if (array_key_exists($alias, $this->definitions)) {
345
            return $this->inflectors->inflect(
346
                $this->definitions[$alias]->build($args)
347
            );
348
        }
349
350
        return false;
351
    }
352
}
353