Issues (3)

src/Container.php (1 issue)

Labels
Severity
1
<?php
2
3
/**
4
 * Phoole (PHP7.2+)
5
 *
6
 * @category  Library
7
 * @package   Phoole\Di
8
 * @copyright Copyright (c) 2019 Hong Zhang
9
 */
10
declare(strict_types=1);
11
12
namespace Phoole\Di;
13
14
use Phoole\Config\Config;
15
use Phoole\Di\Util\ContainerTrait;
16
use Phoole\Di\Util\StaticAccessTrait;
17
use Psr\Container\ContainerInterface;
18
use Phoole\Config\ConfigAwareInterface;
19
use Phoole\Di\Exception\LogicException;
20
use Phoole\Di\Exception\NotFoundException;
21
use Phoole\Base\Reference\ReferenceInterface;
22
use Phoole\Di\Exception\UnresolvedClassException;
23
24
/**
25
 * Dependency Injection
26
 *
27
 * @package Phoole\Di
28
 */
29
class Container implements ContainerInterface, ReferenceInterface, ConfigAwareInterface
30
{
31
    use ContainerTrait;
0 ignored issues
show
The trait Phoole\Di\Util\ContainerTrait requires the property $name which is not provided by Phoole\Di\Container.
Loading history...
32
    use StaticAccessTrait;
33
34
    /**
35
     * Constructor
36
     *
37
     * $config    for config & reference lookup
38
     * $delegator for object lookup. If NULL will use $this
39
     *
40
     * @param  Config             $config
41
     * @param  ContainerInterface $delegator
42
     */
43
    public function __construct(
44
        Config $config,
45
        ?ContainerInterface $delegator = NULL
46
    ) {
47
        $this->setConfig($config);
48
        $this->setDelegator($delegator ?? $this);
49
50
        // set object reference pattern
51
        $this->setReferencePattern('${#', '}');
52
53
        // for static access
54
        self::setContainer($this);
55
56
        // create all objects now
57
        $this->initContainer();
58
    }
59
60
    /**
61
     *
62
     * ```php
63
     * // get the cache object
64
     * $cache = $container->get('cache');
65
     *
66
     * // always get a NEW cache object
67
     * $cacheNew = $container->get('cache@');
68
     *
69
     * // get an object shared in a scope
70
     * $myCache = $container->get('cache@MyAPP');
71
     *
72
     * // get object (created already by definition) by classname/interface name
73
     * // used mostly by automatic dependency injection
74
     * $cache = $container->get(CacheInterface::class);
75
     * ```
76
     *
77
     * {@inheritDoc}
78
     */
79
    public function get($id): object
80
    {
81
        // check definition
82
        if ($this->hasDefinition($id)) {
83
            return $this->getInstance($id);
84
        }
85
        // check classmap if $id is a class/interface name
86
        if (is_object($object = $this->matchClass($id))) {
87
            return $object;
88
        }
89
        throw new NotFoundException("Object '$id' not found");
90
    }
91
92
    /**
93
     * {@inheritDoc}
94
     */
95
    public function has($id): bool
96
    {
97
        // either defined or in classmap
98
        return $this->hasDefinition($id) || NULL !== $this->hasClass($id);
99
    }
100
101
    /**
102
     * Reload & resolve all service definitions
103
     *
104
     * @return void
105
     */
106
    protected function initContainer(): void
107
    {
108
        // reserve some ids
109
        $this->reserveObject('config', $this->getConfig());
110
        $this->reserveObject('container', $this->delegator);
111
112
        // resolve all objects defined in di.services
113
        $this->autoResolve();
114
115
        // resolve all object referenced in the $config
116
        $tree = $this->getConfig()->getTree();
117
        $settings = &$tree->get('');
118
        $this->deReference($settings);
119
    }
120
121
    /**
122
     * resolve all ids like '${#cache}' mentioned in $config
123
     */
124
    protected function autoResolve(): void
125
    {
126
        // get all ids of the defined services
127
        $ids = array_keys($this->getConfig()->get($this->prefix . 'service') ?? []);
128
129
        // resolve in service definition ONLY
130
        if (!empty($ids)) {
131
            $this->resolveAllIds($ids, FALSE);
132
        }
133
134
        // try autoload classname THEN
135
        if (!empty($ids)) {
136
            $this->resolveAllIds($ids, TRUE);
137
        }
138
139
        // error
140
        if (!empty($ids)) {
141
            throw new LogicException("Container error for ID " . $ids[0]);
142
        }
143
    }
144
145
    /**
146
     * Resolve the given ids
147
     *
148
     * @param  array $ids
149
     * @param  bool  $autoClass
150
     * @return void
151
     */
152
    protected function resolveAllIds(array &$ids, bool $autoClass): void
153
    {
154
        $this->autoLoad = $autoClass;
155
        $max = count($ids) * 3;
156
        $cnt = 0;
157
        while ($id = array_shift($ids)) {
158
            if ($cnt++ > $max) {
159
                $ids[] = $id;
160
                break;
161
            }
162
            try {
163
                $this->get($id);
164
            } catch (UnresolvedClassException $e) {
165
                $ids[] = $id;
166
            }
167
        }
168
        $this->autoLoad = FALSE;
169
    }
170
}