Passed
Push — master ( ab0d76...0f78eb )
by Alex
02:02
created
src/ContainerAwareInterface.php 1 patch
Indentation   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -19,18 +19,18 @@
 block discarded – undo
19 19
 interface ContainerAwareInterface
20 20
 {
21 21
 
22
-    /**
23
-     * Inject the container instance into the class.
24
-     *
25
-     * @param \Codeburner\Container\Container $container
26
-     */
27
-    public function setContainer(Container $container);
22
+	/**
23
+	 * Inject the container instance into the class.
24
+	 *
25
+	 * @param \Codeburner\Container\Container $container
26
+	 */
27
+	public function setContainer(Container $container);
28 28
 
29
-    /**
30
-     * get the container instance.
31
-     *
32
-     * @return \Codeburner\Container\Container $container
33
-     */
34
-    public function getContainer();
29
+	/**
30
+	 * get the container instance.
31
+	 *
32
+	 * @return \Codeburner\Container\Container $container
33
+	 */
34
+	public function getContainer();
35 35
 
36 36
 }
Please login to merge, or discard this patch.
src/ContainerAwareTrait.php 1 patch
Indentation   +29 added lines, -29 removed lines patch added patch discarded remove patch
@@ -19,34 +19,34 @@
 block discarded – undo
19 19
 trait ContainerAwareTrait
20 20
 {
21 21
 
22
-    /**
23
-     * The instance of container.
24
-     *
25
-     * @var \Codeburner\Container\Container
26
-     */
27
-    protected $container;
28
-
29
-    /**
30
-     * Set a container.
31
-     *
32
-     * @param \Codeburner\Container\Container $container
33
-     * @return mixed
34
-     */
35
-    public function setContainer(Container $container)
36
-    {
37
-        $this->container = $container;
38
-
39
-        return $this;
40
-    }
41
-
42
-    /**
43
-     * Get the container.
44
-     *
45
-     * @return \Codeburner\Container\Container
46
-     */
47
-    public function getContainer()
48
-    {
49
-        return $this->container;
50
-    }
22
+	/**
23
+	 * The instance of container.
24
+	 *
25
+	 * @var \Codeburner\Container\Container
26
+	 */
27
+	protected $container;
28
+
29
+	/**
30
+	 * Set a container.
31
+	 *
32
+	 * @param \Codeburner\Container\Container $container
33
+	 * @return mixed
34
+	 */
35
+	public function setContainer(Container $container)
36
+	{
37
+		$this->container = $container;
38
+
39
+		return $this;
40
+	}
41
+
42
+	/**
43
+	 * Get the container.
44
+	 *
45
+	 * @return \Codeburner\Container\Container
46
+	 */
47
+	public function getContainer()
48
+	{
49
+		return $this->container;
50
+	}
51 51
 
52 52
 }
Please login to merge, or discard this patch.
src/Container.php 4 patches
Unused Use Statements   +6 added lines, -1 removed lines patch added patch discarded remove patch
@@ -10,8 +10,13 @@
 block discarded – undo
10 10
 
11 11
 namespace Codeburner\Container;
12 12
 
13
-use Closure, Exception, ReflectionClass, ReflectionException, ReflectionFunction, ReflectionParameter;
13
+use Closure;
14
+use Exception;
14 15
 use Psr\Container\ContainerInterface;
16
+use ReflectionClass;
17
+use ReflectionException;
18
+use ReflectionFunction;
19
+use ReflectionParameter;
15 20
 use Codeburner\Container\Exceptions\{ContainerException, NotFoundException};
16 21
 
17 22
 /**
Please login to merge, or discard this patch.
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -146,7 +146,7 @@  discard block
 block discarded – undo
146 146
      * @param array  $dependencies Array of ReflectionParameter
147 147
      *
148 148
      * @throws ContainerException When a dependency cannot be solved.
149
-     * @return array
149
+     * @return callable|null
150 150
      */
151 151
 
152 152
     protected function process(string $abstract, array $parameters, array $dependencies) : array
@@ -430,7 +430,7 @@  discard block
 block discarded – undo
430 430
      * Modify an element with a given function that receive the old element as argument.
431 431
      *
432 432
      * @param string  $abstract  The alias name that will be used to call the element.
433
-     * @param closure $extension The function that receives the old element and return a new or modified one.
433
+     * @param Closure $extension The function that receives the old element and return a new or modified one.
434 434
      *
435 435
      * @throws NotFoundException  When no element was found with $abstract key.
436 436
      * @return self
Please login to merge, or discard this patch.
Spacing   +17 added lines, -17 removed lines patch added patch discarded remove patch
@@ -19,7 +19,7 @@  discard block
 block discarded – undo
19 19
  * because it is aways needed.
20 20
  */
21 21
 
22
-require_once __DIR__ . '/ContainerInterface.php';
22
+require_once __DIR__.'/ContainerInterface.php';
23 23
 
24 24
 /**
25 25
  * The container class is reponsable to construct all objects
@@ -126,14 +126,14 @@  discard block
 block discarded – undo
126 126
             // creating a resolving callback that in every call will recalculate all dependencies
127 127
             // for the given class, and offcourse, using a cached resolving callback if exists.
128 128
 
129
-            return function (string $abstract, array $parameters) use ($inspector, $dependencies) {
129
+            return function(string $abstract, array $parameters) use ($inspector, $dependencies) {
130 130
                 return $inspector->newInstanceArgs(
131 131
                     $this->process($abstract, $parameters, $dependencies)
132 132
                 );
133 133
             };
134 134
         }
135 135
 
136
-        return function (string $abstract) {
136
+        return function(string $abstract) {
137 137
             return new $abstract;
138 138
         };
139 139
     }
@@ -174,7 +174,7 @@  discard block
 block discarded – undo
174 174
     {
175 175
         $key = $abstract.$dependency->name;
176 176
 
177
-        if (! isset($this->resolved[$key])) {
177
+        if (!isset($this->resolved[$key])) {
178 178
             $this->resolved[$key] = $this->generate($abstract, $dependency);
179 179
         }
180 180
 
@@ -200,7 +200,7 @@  discard block
 block discarded – undo
200 200
         try {
201 201
             $value = $dependency->getDefaultValue();
202 202
 
203
-            return function () use ($value) {
203
+            return function() use ($value) {
204 204
                 return $value;
205 205
             };
206 206
         } catch (ReflectionException $e) {
@@ -223,7 +223,7 @@  discard block
 block discarded – undo
223 223
             return $this->dependencies[$entry];
224 224
         }
225 225
 
226
-        return function () use ($classname) {
226
+        return function() use ($classname) {
227 227
             return $this->make($classname);
228 228
         };
229 229
     }
@@ -256,7 +256,7 @@  discard block
 block discarded – undo
256 256
      */
257 257
     public function get($abstract)
258 258
     {
259
-        if (! isset($this->collection[$abstract])) {
259
+        if (!isset($this->collection[$abstract])) {
260 260
             throw new NotFoundException("Element '$abstract' not found");
261 261
         }
262 262
 
@@ -328,7 +328,7 @@  discard block
 block discarded – undo
328 328
         }
329 329
 
330 330
         if ($concrete instanceof Closure === false) {
331
-            $concrete = function (Container $container) use ($concrete) {
331
+            $concrete = function(Container $container) use ($concrete) {
332 332
                 return $container->make($concrete);
333 333
             };
334 334
         }
@@ -352,7 +352,7 @@  discard block
 block discarded – undo
352 352
 
353 353
     public function setIf(string $abstract, $concrete, bool $shared = false) : self
354 354
     {
355
-        if (! isset($this->collection[$abstract])) {
355
+        if (!isset($this->collection[$abstract])) {
356 356
             $this->set($abstract, $concrete, $shared);
357 357
         }
358 358
 
@@ -373,11 +373,11 @@  discard block
 block discarded – undo
373 373
     {
374 374
         if ($dependency instanceof Closure === false) {
375 375
             if (is_object($dependency)) {
376
-                $dependency = function () use ($dependency) {
376
+                $dependency = function() use ($dependency) {
377 377
                     return $dependency;
378 378
                 };
379 379
             } else {
380
-                $dependency = function () use ($dependency) {
380
+                $dependency = function() use ($dependency) {
381 381
                     return $this->get($dependency);
382 382
                 };
383 383
             }
@@ -417,8 +417,8 @@  discard block
 block discarded – undo
417 417
 
418 418
     public function instance(string $abstract, $instance) : self
419 419
     {
420
-        if (! is_object($instance)) {
421
-            throw new ContainerException('Trying to store ' . gettype($type) . ' as object.');
420
+        if (!is_object($instance)) {
421
+            throw new ContainerException('Trying to store '.gettype($type).' as object.');
422 422
         }
423 423
 
424 424
         $this->collection[$abstract] = $instance;
@@ -438,14 +438,14 @@  discard block
 block discarded – undo
438 438
 
439 439
     public function extend(string $abstract, closure $extension) : self
440 440
     {
441
-        if (! isset($this->collection[$abstract])) {
441
+        if (!isset($this->collection[$abstract])) {
442 442
             throw new NotFoundException($abstract);
443 443
         }
444 444
 
445 445
         $object = $this->collection[$abstract];
446 446
 
447 447
         if ($object instanceof Closure) {
448
-            $this->collection[$abstract] = function () use ($object, $extension) {
448
+            $this->collection[$abstract] = function() use ($object, $extension) {
449 449
                 return $extension($object($this), $this);
450 450
             };
451 451
         } else {
@@ -468,11 +468,11 @@  discard block
 block discarded – undo
468 468
 
469 469
     public function share(string $abstract) : self
470 470
     {
471
-        if (! isset($this->collection[$abstract])) {
471
+        if (!isset($this->collection[$abstract])) {
472 472
             throw new NotFoundException("Element '$abstract' not found");
473 473
         }
474 474
 
475
-        if (! $this->collection[$abstract] instanceof Closure) {
475
+        if (!$this->collection[$abstract] instanceof Closure) {
476 476
             throw new ContainerException("'$abstract' must be a resolvable element");
477 477
         }
478 478
 
Please login to merge, or discard this patch.
Indentation   +448 added lines, -448 removed lines patch added patch discarded remove patch
@@ -31,453 +31,453 @@
 block discarded – undo
31 31
 class Container implements ContainerInterface
32 32
 {
33 33
 
34
-    /**
35
-     * Holds all resolved or resolvable instances into the container.
36
-     *
37
-     * @var array
38
-     */
39
-
40
-    protected $collection;
41
-
42
-    /**
43
-     * Class specific defined dependencies.
44
-     *
45
-     * @var array
46
-     */
47
-
48
-    protected $dependencies;
49
-
50
-    /**
51
-     * Cache of classes inspector and resolver.
52
-     *
53
-     * @var array
54
-     */
55
-
56
-    protected $resolving;
57
-
58
-    /**
59
-     * Cache of classes dependencies in callbacks ready for resolution.
60
-     *
61
-     * @var array
62
-     */
63
-
64
-    protected $resolved;
65
-
66
-    /**
67
-     * Call a user function injecting the dependencies.
68
-     *
69
-     * @param string|Closure $function   The function or the user function name.
70
-     * @param array          $parameters The predefined dependencies.
71
-     *
72
-     * @return mixed
73
-     */
74
-
75
-    public function call($function, array $parameters = [])
76
-    {
77
-        $inspector = new ReflectionFunction($function);
78
-
79
-        $dependencies = $inspector->getParameters();
80
-        $dependencies = $this->process('', $parameters, $dependencies);
81
-
82
-        return call_user_func_array($function, $dependencies);
83
-    }
84
-
85
-    /**
86
-     * Makes an element or class injecting automatically all the dependencies.
87
-     *
88
-     * @param string $abstract   The class name or container element name to make.
89
-     * @param array  $parameters Specific parameters definition.
90
-     *
91
-     * @throws ContainerException
92
-     * @return object|null
93
-     */
94
-
95
-    public function make(string $abstract, array $parameters = [])
96
-    {
97
-        if (isset($this->resolving[$abstract])) {
98
-            return $this->resolving[$abstract]($abstract, $parameters);
99
-        }
100
-
101
-        try {
102
-            $this->resolving[$abstract] = $this->construct($abstract);
103
-            return $this->resolving[$abstract]($abstract, $parameters);
104
-        } catch (ReflectionException $e) {
105
-            throw new ContainerException("Fail while attempt to make '$abstract'", 0, $e);
106
-        }
107
-    }
108
-
109
-    /**
110
-     * Construct a class and all the dependencies using the reflection library of PHP.
111
-     *
112
-     * @param string $abstract The class name or container element name to make.
113
-     *
114
-     * @throws ReflectionException
115
-     * @return Closure
116
-     */
117
-
118
-    protected function construct(string $abstract) : Closure
119
-    {
120
-        $inspector = new ReflectionClass($abstract);
121
-
122
-        if (($constructor = $inspector->getConstructor()) && ($dependencies = $constructor->getParameters())) {
123
-
124
-            // if, and only if, a class has a constructor with parameters, we try to solve then
125
-            // creating a resolving callback that in every call will recalculate all dependencies
126
-            // for the given class, and offcourse, using a cached resolving callback if exists.
127
-
128
-            return function (string $abstract, array $parameters) use ($inspector, $dependencies) {
129
-                return $inspector->newInstanceArgs(
130
-                    $this->process($abstract, $parameters, $dependencies)
131
-                );
132
-            };
133
-        }
134
-
135
-        return function (string $abstract) {
136
-            return new $abstract;
137
-        };
138
-    }
139
-
140
-    /**
141
-     * Process all dependencies
142
-     *
143
-     * @param string $abstract     The class name or container element name to make
144
-     * @param array  $parameters   User defined parameters that must be used instead of resolved ones
145
-     * @param array  $dependencies Array of ReflectionParameter
146
-     *
147
-     * @throws ContainerException When a dependency cannot be solved.
148
-     * @return array
149
-     */
150
-
151
-    protected function process(string $abstract, array $parameters, array $dependencies) : array
152
-    {
153
-        foreach ($dependencies as &$dependency) {
154
-            if (isset($parameters[$dependency->name])) {
155
-                   $dependency = $parameters[$dependency->name];
156
-            } else $dependency = $this->resolve($abstract, $dependency);
157
-        }
158
-
159
-        return $dependencies;
160
-    }
161
-
162
-    /**
163
-     * Resolve all the given class reflected dependencies.
164
-     *
165
-     * @param string               $abstract   The class name or container element name to resolve dependencies.
166
-     * @param ReflectionParameter  $dependency The class dependency to be resolved.
167
-     *
168
-     * @throws ContainerException When a dependency cannot be solved.
169
-     * @return Object
170
-     */
171
-
172
-    protected function resolve(string $abstract, ReflectionParameter $dependency)
173
-    {
174
-        $key = $abstract.$dependency->name;
175
-
176
-        if (! isset($this->resolved[$key])) {
177
-            $this->resolved[$key] = $this->generate($abstract, $dependency);
178
-        }
179
-
180
-        return $this->resolved[$key]($this);
181
-    }
182
-
183
-    /**
184
-     * Generate the dependencies callbacks to jump some conditions in every dependency creation.
185
-     *
186
-     * @param string               $abstract   The class name or container element name to resolve dependencies.
187
-     * @param ReflectionParameter  $dependency The class dependency to be resolved.
188
-     *
189
-     * @throws ContainerException When a dependency cannot be solved.
190
-     * @return Closure
191
-     */
192
-
193
-    protected function generate(string $abstract, ReflectionParameter $dependency) : Closure
194
-    {
195
-        if ($class = $dependency->getClass()) {
196
-            return $this->build($class->name, "{$abstract}{$class->name}");
197
-        }
198
-
199
-        try {
200
-            $value = $dependency->getDefaultValue();
201
-
202
-            return function () use ($value) {
203
-                return $value;
204
-            };
205
-        } catch (ReflectionException $e) {
206
-            throw new ContainerException("Cannot resolve '$dependency->name' of '$abstract'", 0, $e);
207
-        }
208
-    }
209
-
210
-    /**
211
-     * Create a build closure for a given class
212
-     *
213
-     * @param string $classname The class that need to be build
214
-     * @param string $entry     Cache entry to search
215
-     *
216
-     * @return Closure
217
-     */
218
-
219
-    protected function build(string $classname, string $entry) : Closure
220
-    {
221
-        if (isset($this->dependencies[$entry])) {
222
-            return $this->dependencies[$entry];
223
-        }
224
-
225
-        return function () use ($classname) {
226
-            return $this->make($classname);
227
-        };
228
-    }
229
-
230
-    /**
231
-     * Reset the container, removing all the elements, cache and options.
232
-     *
233
-     * @return ContainerInterface
234
-     */
235
-
236
-    public function flush() : ContainerInterface
237
-    {
238
-        $this->collection = [];
239
-        $this->dependencies = [];
240
-        $this->resolving = [];
241
-        $this->resolved = [];
242
-
243
-        return $this;
244
-    }
245
-
246
-    /**
247
-     * Finds an entry of the container by its identifier and returns it.
248
-     *
249
-     * @param string $abstract Identifier of the entry to look for.
250
-     *
251
-     * @throws NotFoundException  No entry was found for this identifier.
252
-     * @throws ContainerException Error while retrieving the entry.
253
-     *
254
-     * @return mixed Entry.
255
-     */
256
-    public function get($abstract)
257
-    {
258
-        if (! isset($this->collection[$abstract])) {
259
-            throw new NotFoundException("Element '$abstract' not found");
260
-        }
261
-
262
-        if ($this->collection[$abstract] instanceof Closure) {
263
-            try {
264
-                return $this->collection[$abstract]($this);
265
-            } catch (Exception $e) {
266
-                throw new ContainerException("An exception was thrown while attempt to make $abstract", 0, $e);
267
-            }
268
-        }
269
-
270
-        return $this->collection[$abstract];
271
-    }
272
-
273
-    /**
274
-     * Returns true if the container can return an entry for the given identifier.
275
-     * Returns false otherwise.
276
-     *
277
-     * `has($abstract)` returning true does not mean that `get($abstract)` will not throw an exception.
278
-     * It does however mean that `get($abstract)` will not throw a `NotFoundException`.
279
-     *
280
-     * @param string $abstract Identifier of the entry to look for.
281
-     *
282
-     * @return boolean
283
-     */
284
-
285
-    public function has($abstract)
286
-    {
287
-        return isset($this->collection[$abstract]);
288
-    }
289
-
290
-    /**
291
-     * Verify if an element has a singleton instance.
292
-     *
293
-     * @param  string The class name or container element name to resolve dependencies.
294
-     * @return bool
295
-     */
296
-
297
-    public function isSingleton(string $abstract) : bool
298
-    {
299
-        return isset($this->collection[$abstract]) && $this->collection[$abstract] instanceof Closure === false;
300
-    }
301
-
302
-    /**
303
-     * Verify if an element is a instance of something.
304
-     *
305
-     * @param  string The class name or container element name to resolve dependencies.
306
-     * @return bool
307
-     */
308
-    public function isInstance(string $abstract) : bool
309
-    {
310
-        return isset($this->collection[$abstract]) && is_object($this->collection[$abstract]);
311
-    }
312
-
313
-    /**
314
-     * Bind a new element to the container.
315
-     *
316
-     * @param string                $abstract The alias name that will be used to call the element.
317
-     * @param string|closure|object $concrete The element class name, or an closure that makes the element, or the object itself.
318
-     * @param bool                  $shared   Define if the element will be a singleton instance.
319
-     *
320
-     * @return ContainerInterface
321
-     */
322
-
323
-    public function set(string $abstract, $concrete, bool $shared = false) : ContainerInterface
324
-    {
325
-        if (is_object($concrete)) {
326
-            return $this->instance($abstract, $concrete);
327
-        }
328
-
329
-        if ($concrete instanceof Closure === false) {
330
-            $concrete = function (Container $container) use ($concrete) {
331
-                return $container->make($concrete);
332
-            };
333
-        }
334
-
335
-        if ($shared === true) {
336
-               $this->collection[$abstract] = $concrete($this);
337
-        } else $this->collection[$abstract] = $concrete;
338
-
339
-        return $this;
340
-    }
341
-
342
-    /**
343
-     * Bind a new element to the container IF the element name not exists in the container.
344
-     *
345
-     * @param string         $abstract The alias name that will be used to call the element.
346
-     * @param string|closure $concrete The element class name, or an closure that makes the element.
347
-     * @param bool           $shared   Define if the element will be a singleton instance.
348
-     *
349
-     * @return ContainerInterface
350
-     */
351
-
352
-    public function setIf(string $abstract, $concrete, bool $shared = false) : ContainerInterface
353
-    {
354
-        if (! isset($this->collection[$abstract])) {
355
-            $this->set($abstract, $concrete, $shared);
356
-        }
357
-
358
-        return $this;
359
-    }
360
-
361
-    /**
362
-     * Bind an specific instance to a class dependency.
363
-     *
364
-     * @param string         $class          The class full name.
365
-     * @param string         $dependencyName The dependency full name.
366
-     * @param string|closure $dependency     The specific object class name or a classure that makes the element.
367
-     *
368
-     * @return ContainerInterface
369
-     */
370
-
371
-    public function setTo(string $class, string $dependencyName, $dependency) : ContainerInterface
372
-    {
373
-        if ($dependency instanceof Closure === false) {
374
-            if (is_object($dependency)) {
375
-                $dependency = function () use ($dependency) {
376
-                    return $dependency;
377
-                };
378
-            } else {
379
-                $dependency = function () use ($dependency) {
380
-                    return $this->get($dependency);
381
-                };
382
-            }
383
-        }
384
-
385
-        $this->dependencies[$class.$dependencyName] = $dependency;
386
-
387
-        return $this;
388
-    }
389
-
390
-    /**
391
-     * Bind an element that will be construct only one time, and every call for the element,
392
-     * the same instance will be given.
393
-     *
394
-     * @param string         $abstract The alias name that will be used to call the element.
395
-     * @param string|closure $concrete The element class name, or an closure that makes the element.
396
-     *
397
-     * @return ContainerInterface
398
-     */
399
-
400
-    public function singleton(string $abstract, $concrete) : ContainerInterface
401
-    {
402
-        $this->set($abstract, $concrete, true);
403
-
404
-        return $this;
405
-    }
406
-
407
-    /**
408
-     * Bind an object to the container.
409
-     *
410
-     * @param string $abstract The alias name that will be used to call the object.
411
-     * @param object $instance The object that will be inserted.
412
-     *
413
-     * @throws ContainerException When $instance is not an object.
414
-     * @return ContainerInterface
415
-     */
416
-
417
-    public function instance(string $abstract, $instance) : ContainerInterface
418
-    {
419
-        if (! is_object($instance)) {
420
-            throw new ContainerException('Trying to store ' . gettype($type) . ' as object.');
421
-        }
422
-
423
-        $this->collection[$abstract] = $instance;
424
-
425
-        return $this;
426
-    }
427
-
428
-    /**
429
-     * Modify an element with a given function that receive the old element as argument.
430
-     *
431
-     * @param string  $abstract  The alias name that will be used to call the element.
432
-     * @param closure $extension The function that receives the old element and return a new or modified one.
433
-     *
434
-     * @throws NotFoundException  When no element was found with $abstract key.
435
-     * @return ContainerInterface
436
-     */
437
-
438
-    public function extend(string $abstract, closure $extension) : ContainerInterface
439
-    {
440
-        if (! isset($this->collection[$abstract])) {
441
-            throw new NotFoundException($abstract);
442
-        }
443
-
444
-        $object = $this->collection[$abstract];
445
-
446
-        if ($object instanceof Closure) {
447
-            $this->collection[$abstract] = function () use ($object, $extension) {
448
-                return $extension($object($this), $this);
449
-            };
450
-        } else {
451
-            $this->collection[$abstract] = $extension($object, $this);
452
-        }
453
-
454
-        return $this;
455
-    }
456
-
457
-    /**
458
-     * Makes an resolvable element an singleton.
459
-     *
460
-     * @param  string $abstract The alias name that will be used to call the element.
461
-     *
462
-     * @throws NotFoundException  When no element was found with $abstract key.
463
-     * @throws ContainerException When the element on $abstract key is not resolvable.
464
-     *
465
-     * @return ContainerInterface
466
-     */
467
-
468
-    public function share(string $abstract) : ContainerInterface
469
-    {
470
-        if (! isset($this->collection[$abstract])) {
471
-            throw new NotFoundException("Element '$abstract' not found");
472
-        }
473
-
474
-        if (! $this->collection[$abstract] instanceof Closure) {
475
-            throw new ContainerException("'$abstract' must be a resolvable element");
476
-        }
477
-
478
-        $this->collection[$abstract] = $this->collection[$abstract]($this);
479
-
480
-        return $this;
481
-    }
34
+	/**
35
+	 * Holds all resolved or resolvable instances into the container.
36
+	 *
37
+	 * @var array
38
+	 */
39
+
40
+	protected $collection;
41
+
42
+	/**
43
+	 * Class specific defined dependencies.
44
+	 *
45
+	 * @var array
46
+	 */
47
+
48
+	protected $dependencies;
49
+
50
+	/**
51
+	 * Cache of classes inspector and resolver.
52
+	 *
53
+	 * @var array
54
+	 */
55
+
56
+	protected $resolving;
57
+
58
+	/**
59
+	 * Cache of classes dependencies in callbacks ready for resolution.
60
+	 *
61
+	 * @var array
62
+	 */
63
+
64
+	protected $resolved;
65
+
66
+	/**
67
+	 * Call a user function injecting the dependencies.
68
+	 *
69
+	 * @param string|Closure $function   The function or the user function name.
70
+	 * @param array          $parameters The predefined dependencies.
71
+	 *
72
+	 * @return mixed
73
+	 */
74
+
75
+	public function call($function, array $parameters = [])
76
+	{
77
+		$inspector = new ReflectionFunction($function);
78
+
79
+		$dependencies = $inspector->getParameters();
80
+		$dependencies = $this->process('', $parameters, $dependencies);
81
+
82
+		return call_user_func_array($function, $dependencies);
83
+	}
84
+
85
+	/**
86
+	 * Makes an element or class injecting automatically all the dependencies.
87
+	 *
88
+	 * @param string $abstract   The class name or container element name to make.
89
+	 * @param array  $parameters Specific parameters definition.
90
+	 *
91
+	 * @throws ContainerException
92
+	 * @return object|null
93
+	 */
94
+
95
+	public function make(string $abstract, array $parameters = [])
96
+	{
97
+		if (isset($this->resolving[$abstract])) {
98
+			return $this->resolving[$abstract]($abstract, $parameters);
99
+		}
100
+
101
+		try {
102
+			$this->resolving[$abstract] = $this->construct($abstract);
103
+			return $this->resolving[$abstract]($abstract, $parameters);
104
+		} catch (ReflectionException $e) {
105
+			throw new ContainerException("Fail while attempt to make '$abstract'", 0, $e);
106
+		}
107
+	}
108
+
109
+	/**
110
+	 * Construct a class and all the dependencies using the reflection library of PHP.
111
+	 *
112
+	 * @param string $abstract The class name or container element name to make.
113
+	 *
114
+	 * @throws ReflectionException
115
+	 * @return Closure
116
+	 */
117
+
118
+	protected function construct(string $abstract) : Closure
119
+	{
120
+		$inspector = new ReflectionClass($abstract);
121
+
122
+		if (($constructor = $inspector->getConstructor()) && ($dependencies = $constructor->getParameters())) {
123
+
124
+			// if, and only if, a class has a constructor with parameters, we try to solve then
125
+			// creating a resolving callback that in every call will recalculate all dependencies
126
+			// for the given class, and offcourse, using a cached resolving callback if exists.
127
+
128
+			return function (string $abstract, array $parameters) use ($inspector, $dependencies) {
129
+				return $inspector->newInstanceArgs(
130
+					$this->process($abstract, $parameters, $dependencies)
131
+				);
132
+			};
133
+		}
134
+
135
+		return function (string $abstract) {
136
+			return new $abstract;
137
+		};
138
+	}
139
+
140
+	/**
141
+	 * Process all dependencies
142
+	 *
143
+	 * @param string $abstract     The class name or container element name to make
144
+	 * @param array  $parameters   User defined parameters that must be used instead of resolved ones
145
+	 * @param array  $dependencies Array of ReflectionParameter
146
+	 *
147
+	 * @throws ContainerException When a dependency cannot be solved.
148
+	 * @return array
149
+	 */
150
+
151
+	protected function process(string $abstract, array $parameters, array $dependencies) : array
152
+	{
153
+		foreach ($dependencies as &$dependency) {
154
+			if (isset($parameters[$dependency->name])) {
155
+				   $dependency = $parameters[$dependency->name];
156
+			} else $dependency = $this->resolve($abstract, $dependency);
157
+		}
158
+
159
+		return $dependencies;
160
+	}
161
+
162
+	/**
163
+	 * Resolve all the given class reflected dependencies.
164
+	 *
165
+	 * @param string               $abstract   The class name or container element name to resolve dependencies.
166
+	 * @param ReflectionParameter  $dependency The class dependency to be resolved.
167
+	 *
168
+	 * @throws ContainerException When a dependency cannot be solved.
169
+	 * @return Object
170
+	 */
171
+
172
+	protected function resolve(string $abstract, ReflectionParameter $dependency)
173
+	{
174
+		$key = $abstract.$dependency->name;
175
+
176
+		if (! isset($this->resolved[$key])) {
177
+			$this->resolved[$key] = $this->generate($abstract, $dependency);
178
+		}
179
+
180
+		return $this->resolved[$key]($this);
181
+	}
182
+
183
+	/**
184
+	 * Generate the dependencies callbacks to jump some conditions in every dependency creation.
185
+	 *
186
+	 * @param string               $abstract   The class name or container element name to resolve dependencies.
187
+	 * @param ReflectionParameter  $dependency The class dependency to be resolved.
188
+	 *
189
+	 * @throws ContainerException When a dependency cannot be solved.
190
+	 * @return Closure
191
+	 */
192
+
193
+	protected function generate(string $abstract, ReflectionParameter $dependency) : Closure
194
+	{
195
+		if ($class = $dependency->getClass()) {
196
+			return $this->build($class->name, "{$abstract}{$class->name}");
197
+		}
198
+
199
+		try {
200
+			$value = $dependency->getDefaultValue();
201
+
202
+			return function () use ($value) {
203
+				return $value;
204
+			};
205
+		} catch (ReflectionException $e) {
206
+			throw new ContainerException("Cannot resolve '$dependency->name' of '$abstract'", 0, $e);
207
+		}
208
+	}
209
+
210
+	/**
211
+	 * Create a build closure for a given class
212
+	 *
213
+	 * @param string $classname The class that need to be build
214
+	 * @param string $entry     Cache entry to search
215
+	 *
216
+	 * @return Closure
217
+	 */
218
+
219
+	protected function build(string $classname, string $entry) : Closure
220
+	{
221
+		if (isset($this->dependencies[$entry])) {
222
+			return $this->dependencies[$entry];
223
+		}
224
+
225
+		return function () use ($classname) {
226
+			return $this->make($classname);
227
+		};
228
+	}
229
+
230
+	/**
231
+	 * Reset the container, removing all the elements, cache and options.
232
+	 *
233
+	 * @return ContainerInterface
234
+	 */
235
+
236
+	public function flush() : ContainerInterface
237
+	{
238
+		$this->collection = [];
239
+		$this->dependencies = [];
240
+		$this->resolving = [];
241
+		$this->resolved = [];
242
+
243
+		return $this;
244
+	}
245
+
246
+	/**
247
+	 * Finds an entry of the container by its identifier and returns it.
248
+	 *
249
+	 * @param string $abstract Identifier of the entry to look for.
250
+	 *
251
+	 * @throws NotFoundException  No entry was found for this identifier.
252
+	 * @throws ContainerException Error while retrieving the entry.
253
+	 *
254
+	 * @return mixed Entry.
255
+	 */
256
+	public function get($abstract)
257
+	{
258
+		if (! isset($this->collection[$abstract])) {
259
+			throw new NotFoundException("Element '$abstract' not found");
260
+		}
261
+
262
+		if ($this->collection[$abstract] instanceof Closure) {
263
+			try {
264
+				return $this->collection[$abstract]($this);
265
+			} catch (Exception $e) {
266
+				throw new ContainerException("An exception was thrown while attempt to make $abstract", 0, $e);
267
+			}
268
+		}
269
+
270
+		return $this->collection[$abstract];
271
+	}
272
+
273
+	/**
274
+	 * Returns true if the container can return an entry for the given identifier.
275
+	 * Returns false otherwise.
276
+	 *
277
+	 * `has($abstract)` returning true does not mean that `get($abstract)` will not throw an exception.
278
+	 * It does however mean that `get($abstract)` will not throw a `NotFoundException`.
279
+	 *
280
+	 * @param string $abstract Identifier of the entry to look for.
281
+	 *
282
+	 * @return boolean
283
+	 */
284
+
285
+	public function has($abstract)
286
+	{
287
+		return isset($this->collection[$abstract]);
288
+	}
289
+
290
+	/**
291
+	 * Verify if an element has a singleton instance.
292
+	 *
293
+	 * @param  string The class name or container element name to resolve dependencies.
294
+	 * @return bool
295
+	 */
296
+
297
+	public function isSingleton(string $abstract) : bool
298
+	{
299
+		return isset($this->collection[$abstract]) && $this->collection[$abstract] instanceof Closure === false;
300
+	}
301
+
302
+	/**
303
+	 * Verify if an element is a instance of something.
304
+	 *
305
+	 * @param  string The class name or container element name to resolve dependencies.
306
+	 * @return bool
307
+	 */
308
+	public function isInstance(string $abstract) : bool
309
+	{
310
+		return isset($this->collection[$abstract]) && is_object($this->collection[$abstract]);
311
+	}
312
+
313
+	/**
314
+	 * Bind a new element to the container.
315
+	 *
316
+	 * @param string                $abstract The alias name that will be used to call the element.
317
+	 * @param string|closure|object $concrete The element class name, or an closure that makes the element, or the object itself.
318
+	 * @param bool                  $shared   Define if the element will be a singleton instance.
319
+	 *
320
+	 * @return ContainerInterface
321
+	 */
322
+
323
+	public function set(string $abstract, $concrete, bool $shared = false) : ContainerInterface
324
+	{
325
+		if (is_object($concrete)) {
326
+			return $this->instance($abstract, $concrete);
327
+		}
328
+
329
+		if ($concrete instanceof Closure === false) {
330
+			$concrete = function (Container $container) use ($concrete) {
331
+				return $container->make($concrete);
332
+			};
333
+		}
334
+
335
+		if ($shared === true) {
336
+			   $this->collection[$abstract] = $concrete($this);
337
+		} else $this->collection[$abstract] = $concrete;
338
+
339
+		return $this;
340
+	}
341
+
342
+	/**
343
+	 * Bind a new element to the container IF the element name not exists in the container.
344
+	 *
345
+	 * @param string         $abstract The alias name that will be used to call the element.
346
+	 * @param string|closure $concrete The element class name, or an closure that makes the element.
347
+	 * @param bool           $shared   Define if the element will be a singleton instance.
348
+	 *
349
+	 * @return ContainerInterface
350
+	 */
351
+
352
+	public function setIf(string $abstract, $concrete, bool $shared = false) : ContainerInterface
353
+	{
354
+		if (! isset($this->collection[$abstract])) {
355
+			$this->set($abstract, $concrete, $shared);
356
+		}
357
+
358
+		return $this;
359
+	}
360
+
361
+	/**
362
+	 * Bind an specific instance to a class dependency.
363
+	 *
364
+	 * @param string         $class          The class full name.
365
+	 * @param string         $dependencyName The dependency full name.
366
+	 * @param string|closure $dependency     The specific object class name or a classure that makes the element.
367
+	 *
368
+	 * @return ContainerInterface
369
+	 */
370
+
371
+	public function setTo(string $class, string $dependencyName, $dependency) : ContainerInterface
372
+	{
373
+		if ($dependency instanceof Closure === false) {
374
+			if (is_object($dependency)) {
375
+				$dependency = function () use ($dependency) {
376
+					return $dependency;
377
+				};
378
+			} else {
379
+				$dependency = function () use ($dependency) {
380
+					return $this->get($dependency);
381
+				};
382
+			}
383
+		}
384
+
385
+		$this->dependencies[$class.$dependencyName] = $dependency;
386
+
387
+		return $this;
388
+	}
389
+
390
+	/**
391
+	 * Bind an element that will be construct only one time, and every call for the element,
392
+	 * the same instance will be given.
393
+	 *
394
+	 * @param string         $abstract The alias name that will be used to call the element.
395
+	 * @param string|closure $concrete The element class name, or an closure that makes the element.
396
+	 *
397
+	 * @return ContainerInterface
398
+	 */
399
+
400
+	public function singleton(string $abstract, $concrete) : ContainerInterface
401
+	{
402
+		$this->set($abstract, $concrete, true);
403
+
404
+		return $this;
405
+	}
406
+
407
+	/**
408
+	 * Bind an object to the container.
409
+	 *
410
+	 * @param string $abstract The alias name that will be used to call the object.
411
+	 * @param object $instance The object that will be inserted.
412
+	 *
413
+	 * @throws ContainerException When $instance is not an object.
414
+	 * @return ContainerInterface
415
+	 */
416
+
417
+	public function instance(string $abstract, $instance) : ContainerInterface
418
+	{
419
+		if (! is_object($instance)) {
420
+			throw new ContainerException('Trying to store ' . gettype($type) . ' as object.');
421
+		}
422
+
423
+		$this->collection[$abstract] = $instance;
424
+
425
+		return $this;
426
+	}
427
+
428
+	/**
429
+	 * Modify an element with a given function that receive the old element as argument.
430
+	 *
431
+	 * @param string  $abstract  The alias name that will be used to call the element.
432
+	 * @param closure $extension The function that receives the old element and return a new or modified one.
433
+	 *
434
+	 * @throws NotFoundException  When no element was found with $abstract key.
435
+	 * @return ContainerInterface
436
+	 */
437
+
438
+	public function extend(string $abstract, closure $extension) : ContainerInterface
439
+	{
440
+		if (! isset($this->collection[$abstract])) {
441
+			throw new NotFoundException($abstract);
442
+		}
443
+
444
+		$object = $this->collection[$abstract];
445
+
446
+		if ($object instanceof Closure) {
447
+			$this->collection[$abstract] = function () use ($object, $extension) {
448
+				return $extension($object($this), $this);
449
+			};
450
+		} else {
451
+			$this->collection[$abstract] = $extension($object, $this);
452
+		}
453
+
454
+		return $this;
455
+	}
456
+
457
+	/**
458
+	 * Makes an resolvable element an singleton.
459
+	 *
460
+	 * @param  string $abstract The alias name that will be used to call the element.
461
+	 *
462
+	 * @throws NotFoundException  When no element was found with $abstract key.
463
+	 * @throws ContainerException When the element on $abstract key is not resolvable.
464
+	 *
465
+	 * @return ContainerInterface
466
+	 */
467
+
468
+	public function share(string $abstract) : ContainerInterface
469
+	{
470
+		if (! isset($this->collection[$abstract])) {
471
+			throw new NotFoundException("Element '$abstract' not found");
472
+		}
473
+
474
+		if (! $this->collection[$abstract] instanceof Closure) {
475
+			throw new ContainerException("'$abstract' must be a resolvable element");
476
+		}
477
+
478
+		$this->collection[$abstract] = $this->collection[$abstract]($this);
479
+
480
+		return $this;
481
+	}
482 482
 
483 483
 }
Please login to merge, or discard this patch.
src/ContainerInterface.php 1 patch
Indentation   +70 added lines, -70 removed lines patch added patch discarded remove patch
@@ -22,75 +22,75 @@
 block discarded – undo
22 22
 
23 23
 interface ContainerInterface extends PsrContainerInterface {
24 24
 
25
-    /**
26
-     * Call a user function injecting the dependencies.
27
-     *
28
-     * @param string|Closure $function   The function or the user function name.
29
-     * @param array          $parameters The predefined dependencies.
30
-     *
31
-     * @return mixed
32
-     */
33
-
34
-    public function call($function, array $parameters = []);
35
-
36
-    /**
37
-     * Makes an element or class injecting automatically all the dependencies.
38
-     *
39
-     * @param string $abstract   The class name or container element name to make.
40
-     * @param array  $parameters Specific parameters definition.
41
-     *
42
-     * @throws ContainerException
43
-     * @return object|null
44
-     */
45
-
46
-    public function make(string $abstract, array $parameters = []);
47
-
48
-    /**
49
-     * Bind a new element to the container.
50
-     *
51
-     * @param string                $abstract The alias name that will be used to call the element.
52
-     * @param string|closure|object $concrete The element class name, or an closure that makes the element, or the object itself.
53
-     * @param bool                  $shared   Define if the element will be a singleton instance.
54
-     *
55
-     * @return self
56
-     */
57
-
58
-    public function set(string $abstract, $concrete, bool $shared = false) : self;
59
-
60
-    /**
61
-     * Bind a new element to the container IF the element name not exists in the container.
62
-     *
63
-     * @param string         $abstract The alias name that will be used to call the element.
64
-     * @param string|closure $concrete The element class name, or an closure that makes the element.
65
-     * @param bool           $shared   Define if the element will be a singleton instance.
66
-     *
67
-     * @return self
68
-     */
69
-
70
-    public function setIf(string $abstract, $concrete, bool $shared = false) : self;
71
-
72
-    /**
73
-     * Bind an element that will be construct only one time, and every call for the element,
74
-     * the same instance will be given.
75
-     *
76
-     * @param string         $abstract The alias name that will be used to call the element.
77
-     * @param string|closure $concrete The element class name, or an closure that makes the element.
78
-     *
79
-     * @return self
80
-     */
81
-
82
-    public function singleton(string $abstract, $concrete) : self;
83
-
84
-    /**
85
-     * Bind an object to the container.
86
-     *
87
-     * @param string $abstract The alias name that will be used to call the object.
88
-     * @param object $instance The object that will be inserted.
89
-     *
90
-     * @throws ContainerException When $instance is not an object.
91
-     * @return self
92
-     */
93
-
94
-    public function instance(string $abstract, $instance) : self;
25
+	/**
26
+	 * Call a user function injecting the dependencies.
27
+	 *
28
+	 * @param string|Closure $function   The function or the user function name.
29
+	 * @param array          $parameters The predefined dependencies.
30
+	 *
31
+	 * @return mixed
32
+	 */
33
+
34
+	public function call($function, array $parameters = []);
35
+
36
+	/**
37
+	 * Makes an element or class injecting automatically all the dependencies.
38
+	 *
39
+	 * @param string $abstract   The class name or container element name to make.
40
+	 * @param array  $parameters Specific parameters definition.
41
+	 *
42
+	 * @throws ContainerException
43
+	 * @return object|null
44
+	 */
45
+
46
+	public function make(string $abstract, array $parameters = []);
47
+
48
+	/**
49
+	 * Bind a new element to the container.
50
+	 *
51
+	 * @param string                $abstract The alias name that will be used to call the element.
52
+	 * @param string|closure|object $concrete The element class name, or an closure that makes the element, or the object itself.
53
+	 * @param bool                  $shared   Define if the element will be a singleton instance.
54
+	 *
55
+	 * @return self
56
+	 */
57
+
58
+	public function set(string $abstract, $concrete, bool $shared = false) : self;
59
+
60
+	/**
61
+	 * Bind a new element to the container IF the element name not exists in the container.
62
+	 *
63
+	 * @param string         $abstract The alias name that will be used to call the element.
64
+	 * @param string|closure $concrete The element class name, or an closure that makes the element.
65
+	 * @param bool           $shared   Define if the element will be a singleton instance.
66
+	 *
67
+	 * @return self
68
+	 */
69
+
70
+	public function setIf(string $abstract, $concrete, bool $shared = false) : self;
71
+
72
+	/**
73
+	 * Bind an element that will be construct only one time, and every call for the element,
74
+	 * the same instance will be given.
75
+	 *
76
+	 * @param string         $abstract The alias name that will be used to call the element.
77
+	 * @param string|closure $concrete The element class name, or an closure that makes the element.
78
+	 *
79
+	 * @return self
80
+	 */
81
+
82
+	public function singleton(string $abstract, $concrete) : self;
83
+
84
+	/**
85
+	 * Bind an object to the container.
86
+	 *
87
+	 * @param string $abstract The alias name that will be used to call the object.
88
+	 * @param object $instance The object that will be inserted.
89
+	 *
90
+	 * @throws ContainerException When $instance is not an object.
91
+	 * @return self
92
+	 */
93
+
94
+	public function instance(string $abstract, $instance) : self;
95 95
 
96 96
 }
Please login to merge, or discard this patch.