Container::__construct()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.1481

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 5
c 2
b 0
f 0
dl 0
loc 12
ccs 4
cts 6
cp 0.6667
rs 10
cc 2
nc 2
nop 1
crap 2.1481
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Lit\Air\Psr;
6
7
use Lit\Air\Configurator;
8
use Lit\Air\Recipe\AbstractRecipe;
9
use Lit\Air\Recipe\RecipeInterface;
10
use Psr\Container\ContainerInterface;
11
12
/**
13
 * Air DI container
14
 */
15
class Container implements ContainerInterface
16
{
17
    public const CONFIGURATOR_CLASS = Configurator::class;
18
    /**
19
     * @var RecipeInterface[]
20
     */
21
    protected $recipe = [];
22
    protected $local = [];
23
24
    /**
25
     * @var ContainerInterface|null
26
     */
27
    protected $delegateContainer;
28
29
    /**
30
     * Container constructor.
31
     *
32
     * @param array|null $config Configuration array. See `Configurator` for more details.
33
     */
34 16
    public function __construct(?array $config = null)
35
    {
36 16
        if ($config) {
37
            $class = static::CONFIGURATOR_CLASS;
38
            /**
39
             * @see Configurator::config()
40
             * @var Configurator $class
41
             */
42
            $class::config($this, $config);
43
        }
44 16
        $this->set(static::class, $this);
45 16
        $this->set(ContainerInterface::class, $this);
46 16
    }
47
48
    /**
49
     * Wraps a PSR container
50
     *
51
     * @param ContainerInterface $container The container.
52
     * @return Container
53
     */
54 1
    public static function wrap(ContainerInterface $container): self
55
    {
56 1
        return (new static())->setDelegateContainer($container);
57
    }
58
59
60 14
    public function get($id)
61
    {
62 14
        if (array_key_exists($id, $this->local)) {
63 11
            return $this->local[$id];
64
        }
65
66 11
        if (array_key_exists($id, $this->recipe)) {
67 9
            return $this->recipe[$id]->resolve($this);
68
        }
69
70 2
        if ($this->delegateContainer && $this->delegateContainer->has($id)) {
71 1
            return $this->delegateContainer->get($id);
72
        }
73
74 1
        throw new NotFoundException($id);
75
    }
76
77 13
    public function has($id)
78
    {
79 13
        return array_key_exists($id, $this->local)
80 13
            || array_key_exists($id, $this->recipe)
81 13
            || ($this->delegateContainer && $this->delegateContainer->has($id));
82
    }
83
84
    /**
85
     * Set a recipe to given id
86
     *
87
     * @param string          $id     The key.
88
     * @param RecipeInterface $recipe The recipe instance.
89
     * @return Container
90
     */
91 10
    public function define(string $id, RecipeInterface $recipe): self
92
    {
93 10
        $this->recipe[$id] = $recipe;
94 10
        return $this;
95
    }
96
97
    /**
98
     * Provide parameters for a class.
99
     * (Define an autowire recipe with in container with name of the class)
100
     *
101
     * @param string $className The Classname.
102
     * @param array  $extra     Extra parameteres.
103
     * @param bool   $cached    Whether to reuse the instance
104
     * @return Container
105
     */
106
    public function provideParameter(string $className, array $extra = [], bool $cached = true)
107
    {
108
        return $this->define($className, AbstractRecipe::autowire($className, $extra, $cached));
109
    }
110
111
    /**
112
     * Get recipe instance from the id
113
     *
114
     * @param string $id The key.
115
     * @return RecipeInterface|null
116
     */
117 2
    public function getRecipe(string $id): ?RecipeInterface
118
    {
119 2
        return $this->recipe[$id] ?? null;
120
    }
121
122
    /**
123
     * Get a recipe from the id, wrap a new recipe to replace it.
124
     *
125
     * @param string   $id      The key.
126
     * @param callable $wrapper A recipe wrapper with signature (RecipeInterface): RecipeInterface.
127
     * @return Container
128
     */
129 1
    public function extendRecipe(string $id, callable $wrapper): self
130
    {
131 1
        if (!array_key_exists($id, $this->recipe)) {
132
            throw new \InvalidArgumentException("recipe [$id] unexists");
133
        }
134
135 1
        $recipe = static::applyRecipeWrapper($wrapper, $this->recipe[$id]);
136
137 1
        $this->recipe[$id] = $recipe;
138
139 1
        return $this;
140
    }
141
142
    /**
143
     * Detect if there is a local entry for id
144
     *
145
     * @param string $id The key.
146
     * @return boolean
147
     */
148 1
    public function hasLocalEntry(string $id): bool
149
    {
150 1
        return array_key_exists($id, $this->local);
151
    }
152
153
    /**
154
     * Remove the local entry in id. Note this will never touch recipe.
155
     *
156
     * @param string $id The key.
157
     * @return Container
158
     */
159 4
    public function flush(string $id): self
160
    {
161 4
        unset($this->local[$id]);
162 4
        return $this;
163
    }
164
165
    /**
166
     * Convert a value into recipe and resolve it. See
167
     * http://litphp.github.io/docs/air-config#structure-of-configuration
168
     *
169
     * @param mixed $value The value.
170
     * @return mixed
171
     */
172 4
    public function resolveRecipe($value)
173
    {
174 4
        $class = static::CONFIGURATOR_CLASS;
175
        /**
176
         * @var Configurator $class
177
         */
178 4
        return $class::convertToRecipe($value)->resolve($this);
179
    }
180
181
    /**
182
     * Set a local entry
183
     *
184
     * @param string $id    The key.
185
     * @param mixed  $value The Value.
186
     * @return Container
187
     */
188 16
    public function set(string $id, $value): self
189
    {
190 16
        $this->local[$id] = $value;
191 16
        return $this;
192
    }
193
194
    /**
195
     * Set a delegate container.
196
     * https://github.com/container-interop/container-interop/blob/HEAD/docs/Delegate-lookup.md
197
     *
198
     * @param ContainerInterface $delegateContainer The delegate container.
199
     * @return $this
200
     */
201 1
    public function setDelegateContainer(ContainerInterface $delegateContainer): self
202
    {
203 1
        $this->delegateContainer = $delegateContainer;
204
205 1
        return $this;
206
    }
207
208 1
    protected static function applyRecipeWrapper(callable $wrapper, RecipeInterface $recipe): RecipeInterface
209
    {
210 1
        $recipe = $wrapper($recipe);
211
212 1
        return $recipe;
213
    }
214
}
215