Completed
Pull Request — master (#16)
by Johann
04:20
created

Container::has()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
namespace DICIT;
3
4
use \DICIT\Util\Arrays;
5
6
class Container
7
{
8
    /**
9
     *
10
     * @var mixed[]
11
     */
12
    protected $config = array();
13
14
    /**
15
     *
16
     * @var \DICIT\ArrayResolver
17
     */
18
    protected $parameters;
19
20
    /**
21
     *
22
     * @var \DICIT\ArrayResolver
23
     */
24
    protected $classes;
25
26
    /**
27
     *
28
     * @var \DICIT\Registry
29
     */
30
    protected $registry = null;
31
32
    /**
33
     *
34
     * @var \DICIT\ActivatorFactory
35
     */
36
    protected $activatorFactory = null;
37
38
    /**
39
     *
40
     * @var \DICIT\InjectorFactory
41
     */
42
    protected $injectorFactory = null;
43
44
    /**
45
     *
46
     * @var \DICIT\EncapsulatorFactory
47
     */
48
    protected $encapsulatorFactory = null;
49
50
    /**
51
     *
52
     * @var \DICIT\ReferenceResolver
53
     */
54
    protected $referenceResolver = null;
55
56
    /**
57
     *
58
     * @param Config\AbstractConfig $cfg
59
     * @param ActivatorFactory $activatorFactory
60
     * @param InjectorFactory $injectorFactory
61
     */
62
    public function __construct(Config\AbstractConfig $cfg,
63
        ActivatorFactory $activatorFactory = null, InjectorFactory $injectorFactory = null)
64
    {
65
        $this->registry = new Registry();
66
        $this->config = new ArrayResolver($cfg->load());
0 ignored issues
show
Documentation Bug introduced by
It seems like new \DICIT\ArrayResolver($cfg->load()) of type object<DICIT\ArrayResolver> is incompatible with the declared type array<integer,*> of property $config.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
67
68
        $this->parameters = $this->config->resolve('parameters', array());
69
        $this->classes = $this->config->resolve('classes', array());
70
71
        $this->activatorFactory = $activatorFactory ?: new ActivatorFactory();
72
        $this->injectorFactory = $injectorFactory ?: new InjectorFactory();
73
        $this->encapsulatorFactory = new EncapsulatorFactory();
74
        $this->referenceResolver = new ReferenceResolver($this);
75
    }
76
77
    /**
78
     * Binds an existing object or an object definition to a key in the container.
79
     * @param string $key The key to which the new object/definition should be bound.
80
     * @param mixed $item An array or an object to bind.
81
     * If $item is an object, it will be registered as a singleton in the
82
     * object registry. Otherwise, $item will be handled as an object definition.
83
     */
84
    public function bind($key, $item)
85
    {
86
        if (is_array($item)) {
87
            $this->classes[$key] = $item;
88
        } else {
89
            $this->registry->set($key, $item);
90
        }
91
    }
92
93
    /**
94
     * Set a parameter in the container on any key
95
     * @param [type] $key   [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
96
     * @param [type] $value [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
97
     */
98
    public function setParameter($key, $value)
99
    {
100
        $path = explode('.', $key);
101
102
        $this->validateParameter($key, $value);
103
104
105
        $root = array();
106
        $r = &$root;
107
        foreach ($path as $subNode) {
108
            $r[$subNode] = array();
109
            $r = &$r[$subNode];
110
        }
111
        $r = $value;
112
        $r = &$root;
113
114
        if ($this->parameters) {
115
            $parameters = $this->parameters->extract();
116
        } else {
117
            $parameters = array();
118
        }
119
120
        $this->parameters = new ArrayResolver(Arrays::mergeRecursiveUnique($parameters, $r));
121
        return $this;
122
    }
123
124
    /**
125
     * Retrieve the parameter value configured in the container
126
     * @param  string $parameterName
127
     * @return mixed
128
     */
129
    public function getParameter($parameterName)
130
    {
131
        $value = $this->parameters->resolve($parameterName);
132
133
        if ($value instanceof ArrayResolver) {
134
            return $value->extract();
135
        }
136
137
        return $value;
138
    }
139
140
    public function has($serviceName)
141
    {
142
        $serviceConfig = $this->classes->resolve($serviceName, null);
143
        if ($serviceConfig) {
144
            return true;
145
        }
146
        return false;
147
    }
148
149
150
    /**
151
     * Retrieve a class configured in the container
152
     * @param  string $serviceName
153
     * @return object
154
     */
155
    public function get($serviceName)
156
    {
157
        if ($this->registry->has($serviceName)) {
158
            return $this->registry->get($serviceName);
159
        }
160
161
        $serviceConfig = $this->classes->resolve($serviceName, null);
162
163
        if ($serviceConfig == null) {
164
            throw new \DICIT\UnknownDefinitionException($serviceName);
165
        }
166
167
        try {
168
            return $this->loadService($serviceName, $serviceConfig->extract());
169
        } catch (\DICIT\UnknownDefinitionException $ex) {
170
            throw new \RuntimeException(
171
                sprintf("Dependency '%s' not found while trying to build '%s'.",
172
                    $ex->getServiceName(), $serviceName));
173
        }
174
    }
175
176
177
    public function resolve($reference)
178
    {
179
        return $this->referenceResolver->resolve($reference);
180
    }
181
182
    /**
183
     * Resolves an array of references.
184
     * @param array $references
185
     * @return array containing all the resolved references
186
     */
187
    public function resolveMany(array $references = null)
188
    {
189
        if ($references === null) {
190
            return array();
191
        }
192
193
        return $this->referenceResolver->resolveMany($references);
194
    }
195
196
    /**
197
     * Flush the registry
198
     * @return Container
199
     */
200
    public function flushRegistry()
201
    {
202
        $this->registry->flush();
203
        return $this;
204
    }
205
206
    /**
207
     * Chain of command of the class loader
208
     * @param  array $serviceConfig
209
     * @param string $serviceName
210
     * @return object
211
     */
212
    protected function loadService($serviceName, $serviceConfig)
213
    {
214
        $isSingleton = false;
215
216
        if (array_key_exists('singleton', $serviceConfig)) {
217
            $isSingleton = (bool)$serviceConfig['singleton'];
218
        }
219
220
        $class = $this->activate($serviceName, $serviceConfig);
221
222
        if ($isSingleton) {
223
            // Only store if singleton'ed to spare memory
224
            $this->registry->set($serviceName, $class);
225
        }
226
227
        $this->inject($class, $serviceConfig);
228
        $class = $this->encapsulate($class, $serviceConfig);
229
230
        return $class;
231
    }
232
233
    /**
234
     * Handles class instanciation
235
     * @param  array $serviceConfig
236
     * @param string $serviceName
237
     * @return object
238
     */
239
    protected function activate($serviceName, $serviceConfig)
240
    {
241
        $activator = $this->activatorFactory->getActivator($serviceName, $serviceConfig);
242
243
        return $activator->createInstance($this, $serviceName, $serviceConfig);
244
    }
245
246
    /**
247
     * Handle method invocations in the class
248
     * @param  object $class
249
     * @param  array $serviceConfig
250
     * @return boolean
251
     */
252
    protected function inject($class, $serviceConfig)
253
    {
254
        $injectors = $this->injectorFactory->getInjectors();
255
256
        foreach ($injectors as $injector) {
257
            $injector->inject($this, $class, $serviceConfig);
258
        }
259
260
        return true;
261
    }
262
263
    /**
264
     * Interceptor handler
265
     * @param  object $class
266
     * @param  array $serviceConfig
267
     * @return object
268
     */
269
    protected function encapsulate($class, $serviceConfig)
270
    {
271
        $encapsulators = $this->encapsulatorFactory->getEncapsulators();
272
273
        foreach ($encapsulators as $encapsulator) {
274
            $class = $encapsulator->encapsulate($this, $class, $serviceConfig);
275
        }
276
277
        return $class;
278
    }
279
280
    /**
281
     * Check that the value to bind is a scalar, or an array multi-dimensional of scalars
282
     * @param  string $key
283
     * @param  mixed $value
284
     * @return boolean
285
     *
286
     * @throws IllegalTypeException
287
     *
288
     */
289
    protected function validateParameter($key, $value)
290
    {
291
        if (is_scalar($value)) {
292
            return true;
293
        }
294
295
        if (is_object($value)) {
296
            throw new IllegalTypeException(sprintf("Can't bind parameter %s with a callable", $key));
297
        }
298
299
        if (is_array($value)) {
300
            array_walk_recursive($value, function ($item, $k) use ($key) {
301
                if (!is_scalar($item)) {
302
                    throw new IllegalTypeException(
303
                        sprintf("Can't bind parameter, unauthorized value on key '%s' of '%s'", $k, $key));
304
                }
305
            });
306
        }
307
308
        return true;
309
    }
310
}
311