Container::bind()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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