Container::get()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 1
c 3
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
1
<?php
2
declare(strict_types = 1);
3
namespace Bnf\Di;
4
5
use Psr\Container\ContainerInterface;
6
7
class Container implements ContainerInterface
8
{
9
    /** @var array */
10
    private $entries = [];
11
12
    /** @var array */
13
    private $factories = [];
14
15
    /** @var ContainerInterface */
16
    private $container;
17
18
    /**
19
     * Instantiate the container.
20
     *
21
     * Objects and parameters can be passed as argument to the constructor.
22
     *
23
     * @param iterable $providers The service providers to register.
24
     * @param array $entries The default parameters or objects.
25
     */
26
    public function __construct(iterable $providers = [], array $entries = [], ContainerInterface $delegate = null)
27
    {
28
        $this->entries = $entries;
29
        $this->container = $delegate ?? $this;
30
31
        $factories = [];
32
        foreach ($providers as $provider) {
33
            $factories = (array) $provider->getFactories() + $factories;
34
            foreach ($provider->getExtensions() as $id => $extension) {
35
                // Decorate a previously defined extension or if that is not available,
36
                // create a lazy lookup to a factory from the list of vanilla factories.
37
                // Lazy because we currently can not know whether a factory will only
38
                // become available due to a subsequent provider.
39
                $innerFactory = $this->factories[$id] ?? function (ContainerInterface $c) use (&$factories, $id) {
40
                    return isset($factories[$id]) ? $factories[$id]($c) : null;
41
                };
42
43
                $this->factories[$id] = function (ContainerInterface $container) use ($extension, $innerFactory) {
44
                    $previous = $innerFactory($container);
45
                    return $extension($container, $previous);
46
                };
47
            }
48
        }
49
50
        // Add factories to the list of factories for services that were not extended.
51
        // (i.e those that have not been specified in getExtensions)
52
        $this->factories += $factories;
53
    }
54
55
    /**
56
     * @param string $id
57
     * @return bool
58
     */
59
    public function has($id)
60
    {
61
        return array_key_exists($id, $this->entries) || array_key_exists($id, $this->factories);
62
    }
63
64
    /**
65
     * @param string $id
66
     * @return mixed
67
     */
68
    private function create(string $id)
69
    {
70
        $factory = $this->factories[$id] ?? null;
71
72
        if ((bool) $factory) {
73
            // Remove factory as it is no longer required.
74
            // Set factory to false to be able to detect
75
            // cyclic dependency loops.
76
            $this->factories[$id] = false;
77
78
            return $this->entries[$id] = $factory($this->container);
79
        }
80
        if (array_key_exists($id, $this->entries)) {
81
            // This condition is triggered in the unlikely case that the entry is null
82
            // Note: That is because the coalesce operator used in get() can not handle that
83
            return $this->entries[$id];
84
        }
85
        if ($factory === null) {
86
            throw new NotFoundException('Container entry "' . $id . '" is not available.', 1519978105);
87
        }
88
        // if ($factory === false)
89
        throw new ContainerException('Container entry "' . $id . '" is part of a cyclic dependency chain.', 1520175002);
90
    }
91
92
    /**
93
     * @param string $id
94
     * @return mixed
95
     */
96
    public function get($id)
97
    {
98
        return $this->entries[$id] ?? $this->create($id);
99
    }
100
}
101