Container::resolveInstance()   A
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 9
nc 5
nop 1
dl 0
loc 17
rs 9.6111
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the slince/di package.
7
 *
8
 * (c) Slince <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Slince\Di;
15
16
use Psr\Container\ContainerInterface;
17
use Slince\Di\Exception\DependencyInjectionException;
18
use Slince\Di\Exception\NotFoundException;
19
20
class Container implements \ArrayAccess, ContainerInterface
21
{
22
    /**
23
     * @var array
24
     */
25
    protected $aliases = [];
26
27
    /**
28
     * Array of Definitions.
29
     *
30
     * @var Definition[]
31
     */
32
    protected $definitions = [];
33
34
    /**
35
     * @var array
36
     */
37
    protected $instances;
38
39
    /**
40
     * Array of parameters.
41
     *
42
     * @var ParameterBag
43
     */
44
    protected $parameters;
45
46
    /**
47
     * @var Resolver
48
     */
49
    protected $resolver;
50
51
    /**
52
     * Defaults for the container.
53
     *
54
     * [
55
     *     'share' => true,
56
     *     'autowire' => true,
57
     *     'autoregister' => true
58
     * ]
59
     *
60
     * @var array
61
     */
62
    protected $defaults = [
63
        'share' => true,
64
        'autowire' => true,
65
        'autoregister' => true
66
    ];
67
68
    public function __construct()
69
    {
70
        $this->parameters = new ParameterBag();
71
        $this->resolver = new Resolver($this);
72
        $this->register($this);
73
    }
74
75
    /**
76
     * Determine if a given offset exists.
77
     *
78
     * @param string $key
79
     *
80
     * @return bool
81
     */
82
    public function offsetExists($key)
83
    {
84
        return $this->has($key);
85
    }
86
87
    /**
88
     * Get the value at a given offset.
89
     *
90
     * @param string $key
91
     *
92
     * @return mixed
93
     */
94
    public function offsetGet($key)
95
    {
96
        return $this->get($key);
97
    }
98
99
    /**
100
     * Set the value at a given offset.
101
     *
102
     * @param string $key
103
     * @param mixed  $value
104
     */
105
    public function offsetSet($key, $value)
106
    {
107
        $this->register($key, $value);
108
    }
109
110
    /**
111
     * Unset the value at a given offset.
112
     *
113
     * @param string $key
114
     */
115
    public function offsetUnset($key)
116
    {
117
        unset($this->definitions[$key], $this->instances[$key]);
118
    }
119
120
    /**
121
     * Register a definition.
122
     *
123
     * @param string|object $id
124
     * @param mixed         $concrete
125
     *
126
     * @return Definition
127
     */
128
    public function register($id, $concrete = null): Definition
129
    {
130
        if (null === $concrete) {
131
            $concrete = $id;
132
        }
133
        if (is_object($id)) {
134
            $id = get_class($id);
135
        }
136
        //Apply defaults.
137
        $definition = (new Definition($concrete))
138
            ->setShared($this->defaults['share'])
139
            ->setAutowired($this->defaults['autowire']);
140
141
        return $this->setDefinition($id, $definition);
142
    }
143
144
    /**
145
     * Set a definition.
146
     *
147
     * @param string     $id
148
     * @param Definition $definition
149
     *
150
     * @return Definition
151
     */
152
    public function setDefinition(string $id, Definition $definition): Definition
153
    {
154
        unset($this->aliases[$id]);
155
        return $this->definitions[$id] = $definition;
156
    }
157
158
    /**
159
     * Adds the service definitions.
160
     *
161
     * @param Definition[] $definitions An array of service definitions
162
     */
163
    public function addDefinitions(array $definitions)
164
    {
165
        foreach ($definitions as $id => $definition) {
166
            $this->setDefinition($id, $definition);
167
        }
168
    }
169
170
    /**
171
     * Sets the service definitions.
172
     *
173
     * @param Definition[] $definitions An array of service definitions
174
     */
175
    public function setDefinitions(array $definitions)
176
    {
177
        $this->definitions = [];
178
        $this->addDefinitions($definitions);
179
    }
180
181
    /**
182
     * Sets an alias for an existing service.
183
     *
184
     * @param string $alias
185
     * @param string $id
186
     */
187
    public function setAlias(string $alias, string $id)
188
    {
189
        $this->aliases[$alias] = $id;
190
    }
191
192
    /**
193
     * Get id of the alias.
194
     *
195
     * @param string $alias
196
     *
197
     * @return string|null
198
     */
199
    public function getAlias(string $alias): ?string
200
    {
201
        return $this->aliases[$alias] ?? null;
202
    }
203
204
    /**
205
     * Get a service instance by specified ID.
206
     *
207
     * @param string $id
208
     *
209
     * @return object
210
     */
211
    public function get(string $id)
212
    {
213
        $id = $this->resolveAlias($id);
214
215
        if (isset($this->instances[$id])) {
216
            return $this->instances[$id];
217
        }
218
219
        return $this->resolveInstance($id);
220
    }
221
222
    /**
223
     * Get a service instance by specified ID.
224
     *
225
     * @param string $id
226
     *
227
     * @return object
228
     */
229
    public function getNew(string $id)
230
    {
231
        $id = $this->resolveAlias($id);
232
233
        return $this->resolveInstance($id);
234
    }
235
236
    protected function resolveAlias(string $id)
237
    {
238
        if (isset($this->aliases[$id])) {
239
            $id = $this->aliases[$id];
240
        }
241
        return $id;
242
    }
243
244
    protected function resolveInstance(string $id)
245
    {
246
        if (!$this->has($id)) {
247
            //If there is no matching definition, creates a definition.
248
            if ($this->defaults['autoregister'] && class_exists($id)) {
249
                $this->register($id);
250
            } else {
251
                throw new NotFoundException(sprintf('There is no definition named "%s"', $id));
252
            }
253
        }
254
        // resolve instance.
255
        $instance = $this->resolver->resolve($this->definitions[$id]);
256
        if ($this->definitions[$id]->isShared()) {
257
            $this->instances[$id] = $instance;
258
        }
259
260
        return $instance;
261
    }
262
263
    /**
264
     * {@inheritdoc}
265
     */
266
    public function has($id): bool
267
    {
268
        return isset($this->definitions[$id]);
269
    }
270
271
    /**
272
     * Extends a definition.
273
     *
274
     * @param string $id
275
     *
276
     * @return Definition
277
     * @throws DependencyInjectionException
278
     */
279
    public function extend(string $id): Definition
280
    {
281
        if (!$this->has($id)) {
282
            throw new NotFoundException(sprintf('There is no definition named "%s"', $id));
283
        }
284
        $definition = $this->definitions[$id];
285
        if ($definition->getResolved()) {
286
            throw new DependencyInjectionException(sprintf('Cannot override frozen service "%s".', $id));
287
        }
288
289
        return $definition;
290
    }
291
292
    /**
293
     * Returns service ids for a given tag.
294
     *
295
     * Example:
296
     *
297
     *     $container->register('foo')->addTag('my.tag', array('hello' => 'world'));
298
     *
299
     *     $serviceIds = $container->findTaggedServiceIds('my.tag');
300
     *     foreach ($serviceIds as $serviceId => $tags) {
301
     *         foreach ($tags as $tag) {
302
     *             echo $tag['hello'];
303
     *         }
304
     *     }
305
     *
306
     * @param string $name
307
     *
308
     * @return array
309
     */
310
    public function findTaggedServiceIds(string $name): array
311
    {
312
        $tags = array();
313
        foreach ($this->definitions as $id => $definition) {
314
            if ($definition->hasTag($name)) {
315
                $tags[$id] = $definition->getTag($name);
316
            }
317
        }
318
319
        return $tags;
320
    }
321
322
    /**
323
     * Gets all global parameters.
324
     *
325
     * @return array
326
     */
327
    public function getParameters(): array
328
    {
329
        return $this->parameters->toArray();
330
    }
331
332
    /**
333
     * Sets array of parameters.
334
     *
335
     * @param array $parameterStore
336
     */
337
    public function setParameters(array $parameterStore)
338
    {
339
        $this->parameters->setParameters($parameterStore);
340
    }
341
342
    /**
343
     * Add some parameters.
344
     *
345
     * @param array $parameters
346
     */
347
    public function addParameters(array $parameters)
348
    {
349
        $this->parameters->addParameters($parameters);
350
    }
351
352
    /**
353
     * Sets a parameter with its name and value.
354
     *
355
     * @param string $name
356
     * @param mixed $value
357
     */
358
    public function setParameter(string $name, $value)
359
    {
360
        $this->parameters->setParameter($name, $value);
361
    }
362
363
    /**
364
     * Gets a parameter by given name.
365
     *
366
     * @param string $name
367
     * @param mixed $default
368
     *
369
     * @return mixed
370
     */
371
    public function getParameter(string $name, $default = null)
372
    {
373
        return $this->parameters->getParameter($name, $default);
374
    }
375
376
    /**
377
     * Gets a default option of the container.
378
     *
379
     * @param string $option
380
     *
381
     * @return mixed
382
     */
383
    public function getDefault(string $option)
384
    {
385
        return $this->defaults[$option] ?? null;
386
    }
387
388
    /**
389
     * Configure defaults.
390
     *
391
     * @param array $defaults
392
     *
393
     */
394
    public function setDefaults(array $defaults)
395
    {
396
        $this->defaults = array_merge($this->defaults, $defaults);
397
    }
398
}
399