Passed
Push — master ( 02eddd...2aa0cc )
by mcfog
02:49 queued 18s
created

Container::instance()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 1
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\AliasRecipe;
10
use Lit\Air\Recipe\AutowireRecipe;
11
use Lit\Air\Recipe\BuilderRecipe;
12
use Lit\Air\Recipe\FixedValueRecipe;
13
use Lit\Air\Recipe\InstanceRecipe;
14
use Lit\Air\Recipe\RecipeInterface;
15
use Psr\Container\ContainerInterface;
16
17
/**
18
 * Air DI container
19
 */
20
class Container implements ContainerInterface
21
{
22
    public const CONFIGURATOR_CLASS = Configurator::class;
23
    /**
24
     * @var RecipeInterface[]
25
     */
26
    protected $recipe = [];
27
    protected $local = [];
28
29
    /**
30
     * @var ContainerInterface|null
31
     */
32
    protected $delegateContainer;
33
34
    /**
35
     * Container constructor.
36
     *
37
     * @param array|null $config Configuration array. See `Configurator` for more details.
38
     */
39 16
    public function __construct(?array $config = null)
40
    {
41 16
        if ($config) {
42
            $class = static::CONFIGURATOR_CLASS;
43
            /**
44
             * @see Configurator::config()
45
             * @var Configurator $class
46
             */
47
            $class::config($this, $config);
48
        }
49 16
        $this->set(static::class, $this);
50 16
        $this->set(ContainerInterface::class, $this);
51 16
    }
52
53
    /**
54
     * Create a alias
55
     *
56
     * @param string $alias The alias string key.
57
     * @return AbstractRecipe
58
     */
59 1
    public static function alias(string $alias): AbstractRecipe
60
    {
61 1
        return new AliasRecipe($alias);
62
    }
63
64
    /**
65
     * Autowire this entry
66
     *
67
     * @param string|null $className Optional classname. Can be ommited when the entry key is the classname.
68
     * @param array       $extra     Extra parameteres.
69
     * @return AbstractRecipe
70
     */
71 2
    public static function autowire(?string $className = null, array $extra = []): AbstractRecipe
72
    {
73 2
        return new AutowireRecipe($className, $extra);
74
    }
75
76
    /**
77
     * Populate an instance by factory
78
     *
79
     * @param string|null $className Optional classname. Can be ommited when the entry key is the classname.
80
     * @param array       $extra     Extra parameteres.
81
     * @return AbstractRecipe
82
     */
83 1
    public static function instance(?string $className = null, array $extra = []): AbstractRecipe
84
    {
85 1
        return new InstanceRecipe($className, $extra);
86
    }
87
88
    /**
89
     * Calls a builder method using factory.
90
     *
91
     * @param callable $builder The builder method. Its parameter will be injected as dependency.
92
     * @param array    $extra   Extra parameters.
93
     * @return AbstractRecipe
94
     */
95 2
    public static function builder(callable $builder, array $extra = []): AbstractRecipe
96
    {
97 2
        return new BuilderRecipe($builder, $extra);
98
    }
99
100
    /**
101
     * A fixed value
102
     *
103
     * @param mixed $value The value.
104
     * @return AbstractRecipe
105
     */
106 7
    public static function value($value): AbstractRecipe
107
    {
108 7
        return new FixedValueRecipe($value);
109
    }
110
111
    /**
112
     * Wraps a PSR container
113
     *
114
     * @param ContainerInterface $container The container.
115
     * @return Container
116
     */
117 1
    public static function wrap(ContainerInterface $container): self
118
    {
119 1
        return (new static())->setDelegateContainer($container);
120
    }
121
122 14
    public function get($id)
123
    {
124 14
        if (array_key_exists($id, $this->local)) {
125 11
            return $this->local[$id];
126
        }
127
128 11
        if (array_key_exists($id, $this->recipe)) {
129 9
            return $this->recipe[$id]->resolve($this, $id);
130
        }
131
132 2
        if ($this->delegateContainer && $this->delegateContainer->has($id)) {
133 1
            return $this->delegateContainer->get($id);
134
        }
135
136 1
        throw new NotFoundException($id);
137
    }
138
139 13
    public function has($id)
140
    {
141 13
        return array_key_exists($id, $this->local)
142 13
            || array_key_exists($id, $this->recipe)
143 13
            || ($this->delegateContainer && $this->delegateContainer->has($id));
144
    }
145
146
    /**
147
     * Set a recipe to given id
148
     *
149
     * @param string          $id     The key.
150
     * @param RecipeInterface $recipe The recipe instance.
151
     * @return Container
152
     */
153 10
    public function define(string $id, RecipeInterface $recipe): self
154
    {
155 10
        $this->recipe[$id] = $recipe;
156 10
        return $this;
157
    }
158
159
    /**
160
     * Get recipe instance from the id
161
     *
162
     * @param string $id The key.
163
     * @return RecipeInterface|null
164
     */
165 1
    public function getRecipe(string $id): ?RecipeInterface
166
    {
167 1
        if (array_key_exists($id, $this->recipe)) {
168
            return $this->recipe[$id];
169
        }
170
171 1
        return null;
172
    }
173
174
    /**
175
     * Get a recipe from the id, wrap a new recipe to replace it.
176
     *
177
     * @param string   $id      The key.
178
     * @param callable $wrapper A recipe wrapper with signature (RecipeInterface): RecipeInterface.
179
     * @return Container
180
     */
181 1
    public function extendRecipe(string $id, callable $wrapper): self
182
    {
183 1
        if (!array_key_exists($id, $this->recipe)) {
184
            throw new \InvalidArgumentException("recipe [$id] unexists");
185
        }
186
187 1
        $recipe = static::applyRecipeWrapper($wrapper, $this->recipe[$id]);
188
189 1
        $this->recipe[$id] = $recipe;
190
191 1
        return $this;
192
    }
193
194
    /**
195
     * Detect if there is a local entry for id
196
     *
197
     * @param string $id The key.
198
     * @return boolean
199
     */
200 5
    public function hasLocalEntry(string $id): bool
201
    {
202 5
        return array_key_exists($id, $this->local);
203
    }
204
205
    /**
206
     * Remove the local entry in id. Note this will never touch recipe.
207
     *
208
     * @param string $id The key.
209
     * @return Container
210
     */
211 4
    public function flush(string $id): self
212
    {
213 4
        unset($this->local[$id]);
214 4
        return $this;
215
    }
216
217
    /**
218
     * Convert a value into recipe and resolve it. See
219
     * http://litphp.github.io/docs/air-config#structure-of-configuration
220
     *
221
     * @param mixed $value The value.
222
     * @return mixed
223
     */
224 5
    public function resolveRecipe($value)
225
    {
226 5
        $class = static::CONFIGURATOR_CLASS;
227
        /**
228
         * @see Configurator::convertArray()
229
         * @var Configurator $class
230
         */
231 5
        return $class::convertToRecipe($value)->resolve($this);
232
    }
233
234
    /**
235
     * Set a local entry
236
     *
237
     * @param string $id    The key.
238
     * @param mixed  $value The Value.
239
     * @return Container
240
     */
241 16
    public function set(string $id, $value): self
242
    {
243 16
        $this->local[$id] = $value;
244 16
        return $this;
245
    }
246
247
    /**
248
     * Set a delegate container.
249
     * https://github.com/container-interop/container-interop/blob/HEAD/docs/Delegate-lookup.md
250
     *
251
     * @param ContainerInterface $delegateContainer The delegate container.
252
     * @return $this
253
     */
254 1
    public function setDelegateContainer(ContainerInterface $delegateContainer): self
255
    {
256 1
        $this->delegateContainer = $delegateContainer;
257
258 1
        return $this;
259
    }
260
261 1
    protected static function applyRecipeWrapper(callable $wrapper, RecipeInterface $recipe): RecipeInterface
262
    {
263 1
        $recipe = $wrapper($recipe);
264
265 1
        return $recipe;
266
    }
267
}
268