Completed
Push — master ( 48861e...aab42f )
by Alex
02:03
created
src/Container.php 1 patch
Indentation   +455 added lines, -455 removed lines patch added patch discarded remove patch
@@ -31,460 +31,460 @@
 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
-        try {
98
-            if (! isset($this->resolving[$abstract])) {
99
-                $this->resolving[$abstract] = $this->construct($abstract);
100
-            }
101
-
102
-            return $this->resolving[$abstract]($abstract, $parameters);
103
-        } catch (ReflectionException $e) {
104
-            throw new ContainerException("Fail while attempt to make '$abstract'", 0, $e);
105
-        }
106
-    }
107
-
108
-    /**
109
-     * Construct a class and all the dependencies using the reflection library of PHP.
110
-     *
111
-     * @param string $abstract The class name or container element name to make.
112
-     *
113
-     * @throws ReflectionException
114
-     * @return Closure
115
-     */
116
-
117
-    protected function construct(string $abstract) : Closure
118
-    {
119
-        $inspector = new ReflectionClass($abstract);
120
-
121
-        if (($constructor = $inspector->getConstructor()) && ($dependencies = $constructor->getParameters())) {
122
-
123
-            // if, and only if, a class has a constructor with parameters, we try to solve then
124
-            // creating a resolving callback that in every call will recalculate all dependencies
125
-            // for the given class, and offcourse, using a cached resolving callback if exists.
126
-
127
-            return function (string $abstract, array $parameters) use ($inspector, $dependencies) {
128
-                return $inspector->newInstanceArgs(
129
-                    $this->process($abstract, $parameters, $dependencies)
130
-                );
131
-            };
132
-        }
133
-
134
-        return function (string $abstract) {
135
-            return new $abstract;
136
-        };
137
-    }
138
-
139
-    /**
140
-     * Process all dependencies
141
-     *
142
-     * @param string $abstract     The class name or container element name to make
143
-     * @param array  $parameters   User defined parameters that must be used instead of resolved ones
144
-     * @param array  $dependencies Array of ReflectionParameter
145
-     *
146
-     * @throws ContainerException When a dependency cannot be solved.
147
-     * @return array
148
-     */
149
-
150
-    protected function process(string $abstract, array $parameters, array $dependencies) : array
151
-    {
152
-        foreach ($dependencies as &$dependency) {
153
-            if (isset($parameters[$dependency->name])) {
154
-                   $dependency = $parameters[$dependency->name];
155
-            } else $dependency = $this->resolve($abstract, $dependency);
156
-        }
157
-
158
-        return $dependencies;
159
-    }
160
-
161
-    /**
162
-     * Resolve all the given class reflected dependencies.
163
-     *
164
-     * @param string               $abstract   The class name or container element name to resolve dependencies.
165
-     * @param ReflectionParameter  $dependency The class dependency to be resolved.
166
-     *
167
-     * @throws ContainerException When a dependency cannot be solved.
168
-     * @return Object
169
-     */
170
-
171
-    protected function resolve(string $abstract, ReflectionParameter $dependency)
172
-    {
173
-        $key = $abstract.$dependency->name;
174
-
175
-        if (! isset($this->resolved[$key])) {
176
-            $this->resolved[$key] = $this->generate($abstract, $dependency);
177
-        }
178
-
179
-        return $this->resolved[$key]($this);
180
-    }
181
-
182
-    /**
183
-     * Generate the dependencies callbacks to jump some conditions in every dependency creation.
184
-     *
185
-     * @param string               $abstract   The class name or container element name to resolve dependencies.
186
-     * @param ReflectionParameter  $dependency The class dependency to be resolved.
187
-     *
188
-     * @throws ContainerException When a dependency cannot be solved.
189
-     * @return Closure
190
-     */
191
-
192
-    protected function generate(string $abstract, ReflectionParameter $dependency) : Closure
193
-    {
194
-        if ($class = $dependency->getClass()) {
195
-            return $this->build($class->name, "{$abstract}{$class->name}");
196
-        }
197
-
198
-        try {
199
-            $value = $dependency->getDefaultValue();
200
-
201
-            return function () use ($value) {
202
-                return $value;
203
-            };
204
-        } catch (ReflectionException $e) {
205
-            throw new ContainerException("Cannot resolve '$dependency->name' of '$abstract'", 0, $e);
206
-        }
207
-    }
208
-
209
-    /**
210
-     * Create a build closure for a given class
211
-     *
212
-     * @param string $classname The class that need to be build
213
-     * @param string $entry     Cache entry to search
214
-     *
215
-     * @return Closure
216
-     */
217
-
218
-    protected function build(string $classname, string $entry) : Closure
219
-    {
220
-        if (isset($this->dependencies[$entry])) {
221
-            return $this->dependencies[$entry];
222
-        }
223
-
224
-        return function () use ($classname) {
225
-            return $this->make($classname);
226
-        };
227
-    }
228
-
229
-    /**
230
-     * Reset the container, removing all the elements, cache and options.
231
-     *
232
-     * @return ContainerInterface
233
-     */
234
-
235
-    public function flush() : ContainerInterface
236
-    {
237
-        $this->collection = [];
238
-        $this->dependencies = [];
239
-        $this->resolving = [];
240
-        $this->resolved = [];
241
-
242
-        return $this;
243
-    }
244
-
245
-    /**
246
-     * Finds an entry of the container by its identifier and returns it.
247
-     *
248
-     * @param string $abstract Identifier of the entry to look for.
249
-     *
250
-     * @throws NotFoundException  No entry was found for this identifier.
251
-     * @throws ContainerException Error while retrieving the entry.
252
-     *
253
-     * @return mixed Entry.
254
-     */
255
-    public function get($abstract)
256
-    {
257
-        if (! isset($this->collection[$abstract])) {
258
-            throw new NotFoundException("Element '$abstract' not found");
259
-        }
260
-
261
-        if ($this->collection[$abstract] instanceof Closure) {
262
-            try {
263
-                return $this->collection[$abstract]($this);
264
-            } catch (Exception $e) {
265
-                throw new ContainerException("An exception was thrown while attempt to make $abstract", 0, $e);
266
-            }
267
-        }
268
-
269
-        return $this->collection[$abstract];
270
-    }
271
-
272
-    /**
273
-     * Returns true if the container can return an entry for the given identifier.
274
-     * Returns false otherwise.
275
-     *
276
-     * `has($abstract)` returning true does not mean that `get($abstract)` will not throw an exception.
277
-     * It does however mean that `get($abstract)` will not throw a `NotFoundException`.
278
-     *
279
-     * @param string $abstract Identifier of the entry to look for.
280
-     *
281
-     * @return boolean
282
-     */
283
-
284
-    public function has($abstract)
285
-    {
286
-        return isset($this->collection[$abstract]);
287
-    }
288
-
289
-    /**
290
-     * Verify if an element has a singleton instance.
291
-     *
292
-     * @param  string The class name or container element name to resolve dependencies.
293
-     *
294
-     * @throws NotFoundException When $abstract does not exists
295
-     * @return bool
296
-     */
297
-
298
-    public function isSingleton(string $abstract) : bool
299
-    {
300
-        if (! $this->has($abstract)) {
301
-            throw new NotFoundException("Element '$abstract' not found");
302
-        }
303
-
304
-        return $this->collection[$abstract] instanceof Closure === false;
305
-    }
306
-
307
-    /**
308
-     * Verify if an element is a instance of something.
309
-     *
310
-     * @param  string The class name or container element name to resolve dependencies.
311
-     *
312
-     * @throws NotFoundException When $abstract does not exists
313
-     * @return bool
314
-     */
315
-
316
-    public function isInstance(string $abstract) : bool
317
-    {
318
-        if (! $this->has($abstract)) {
319
-            throw new NotFoundException("Element '$abstract' not found");
320
-        }
321
-
322
-        return is_object($this->collection[$abstract]);
323
-    }
324
-
325
-    /**
326
-     * Bind a new element to the container.
327
-     *
328
-     * @param string                $abstract The alias name that will be used to call the element.
329
-     * @param string|closure|object $concrete The element class name, or an closure that makes the element, or the object itself.
330
-     * @param bool                  $shared   Define if the element will be a singleton instance.
331
-     *
332
-     * @return ContainerInterface
333
-     */
334
-
335
-    public function set(string $abstract, $concrete, bool $shared = false) : ContainerInterface
336
-    {
337
-        if (is_object($concrete)) {
338
-            return $this->instance($abstract, $concrete);
339
-        }
340
-
341
-        if ($concrete instanceof Closure === false) {
342
-            $concrete = function (Container $container) use ($concrete) {
343
-                return $container->make($concrete);
344
-            };
345
-        }
346
-
347
-        if ($shared === true) {
348
-               $this->collection[$abstract] = $concrete($this);
349
-        } else $this->collection[$abstract] = $concrete;
350
-
351
-        return $this;
352
-    }
353
-
354
-    /**
355
-     * Bind a new element to the container IF the element name not exists in the container.
356
-     *
357
-     * @param string         $abstract The alias name that will be used to call the element.
358
-     * @param string|closure $concrete The element class name, or an closure that makes the element.
359
-     * @param bool           $shared   Define if the element will be a singleton instance.
360
-     *
361
-     * @return ContainerInterface
362
-     */
363
-
364
-    public function setIf(string $abstract, $concrete, bool $shared = false) : ContainerInterface
365
-    {
366
-        if (! $this->has($abstract)) {
367
-            $this->set($abstract, $concrete, $shared);
368
-        }
369
-
370
-        return $this;
371
-    }
372
-
373
-    /**
374
-     * Bind an specific instance to a class dependency.
375
-     *
376
-     * @param string         $class          The class full name.
377
-     * @param string         $dependencyName The dependency full name.
378
-     * @param string|closure $dependency     The specific object class name or a classure that makes the element.
379
-     *
380
-     * @return ContainerInterface
381
-     */
382
-
383
-    public function setTo(string $class, string $dependencyName, $dependency) : ContainerInterface
384
-    {
385
-        $key = "$class$dependencyName";
386
-
387
-        if ($dependency instanceof Closure === false) {
388
-
389
-            // let's use temporarily the set method to resolve
390
-            // the $dependency if it is not a closure ready to use.
391
-
392
-            $this->set($key, $dependency);
393
-            $resolved = $this->collection[$key];
394
-
395
-            // now we already have a resolved version of $dependency
396
-            // we just need to ensure the dependencies type, a closure.
397
-
398
-            $dependency = function () use ($resolved) {
399
-                return $resolved;
400
-            };
401
-
402
-            // we have used the set method to resolve the $dependency
403
-            // now that we have done all the process let's clear the memory.
404
-
405
-            unset($resolved, $this->collection[$key]);
406
-        }
407
-
408
-        $this->dependencies[$key] = $dependency;
409
-
410
-        return $this;
411
-    }
412
-
413
-    /**
414
-     * Bind an element that will be construct only one time, and every call for the element,
415
-     * the same instance will be given.
416
-     *
417
-     * @param string         $abstract The alias name that will be used to call the element.
418
-     * @param string|closure $concrete The element class name, or an closure that makes the element.
419
-     *
420
-     * @return ContainerInterface
421
-     */
422
-
423
-    public function singleton(string $abstract, $concrete) : ContainerInterface
424
-    {
425
-        $this->set($abstract, $concrete, true);
426
-
427
-        return $this;
428
-    }
429
-
430
-    /**
431
-     * Bind an object to the container.
432
-     *
433
-     * @param string $abstract The alias name that will be used to call the object.
434
-     * @param object $instance The object that will be inserted.
435
-     *
436
-     * @throws ContainerException When $instance is not an object.
437
-     * @return ContainerInterface
438
-     */
439
-
440
-    public function instance(string $abstract, $instance) : ContainerInterface
441
-    {
442
-        if (! is_object($instance)) {
443
-            throw new ContainerException('Trying to store ' . gettype($instance) . ' as object.');
444
-        }
445
-
446
-        $this->collection[$abstract] = $instance;
447
-
448
-        return $this;
449
-    }
450
-
451
-    /**
452
-     * Modify an element with a given function that receive the old element as argument.
453
-     *
454
-     * @param string  $abstract  The alias name that will be used to call the element.
455
-     * @param closure $extension The function that receives the old element and return a new or modified one.
456
-     *
457
-     * @throws NotFoundException  When no element was found with $abstract key.
458
-     * @return ContainerInterface
459
-     */
460
-
461
-    public function extend(string $abstract, closure $extension) : ContainerInterface
462
-    {
463
-        $object = $this->get($abstract);
464
-
465
-        $this->collection[$abstract] = $extension($object, $this);
466
-
467
-        return $this;
468
-    }
469
-
470
-    /**
471
-     * Makes an resolvable element an singleton.
472
-     *
473
-     * @param  string $abstract The alias name that will be used to call the element.
474
-     *
475
-     * @throws NotFoundException  When no element was found with $abstract key.
476
-     * @throws ContainerException When the element on $abstract key is not resolvable.
477
-     *
478
-     * @return ContainerInterface
479
-     */
480
-
481
-    public function share(string $abstract) : ContainerInterface
482
-    {
483
-        $object = $this->get($abstract);
484
-
485
-        $this->collection[$abstract] = $object;
486
-
487
-        return $this;
488
-    }
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
+		try {
98
+			if (! isset($this->resolving[$abstract])) {
99
+				$this->resolving[$abstract] = $this->construct($abstract);
100
+			}
101
+
102
+			return $this->resolving[$abstract]($abstract, $parameters);
103
+		} catch (ReflectionException $e) {
104
+			throw new ContainerException("Fail while attempt to make '$abstract'", 0, $e);
105
+		}
106
+	}
107
+
108
+	/**
109
+	 * Construct a class and all the dependencies using the reflection library of PHP.
110
+	 *
111
+	 * @param string $abstract The class name or container element name to make.
112
+	 *
113
+	 * @throws ReflectionException
114
+	 * @return Closure
115
+	 */
116
+
117
+	protected function construct(string $abstract) : Closure
118
+	{
119
+		$inspector = new ReflectionClass($abstract);
120
+
121
+		if (($constructor = $inspector->getConstructor()) && ($dependencies = $constructor->getParameters())) {
122
+
123
+			// if, and only if, a class has a constructor with parameters, we try to solve then
124
+			// creating a resolving callback that in every call will recalculate all dependencies
125
+			// for the given class, and offcourse, using a cached resolving callback if exists.
126
+
127
+			return function (string $abstract, array $parameters) use ($inspector, $dependencies) {
128
+				return $inspector->newInstanceArgs(
129
+					$this->process($abstract, $parameters, $dependencies)
130
+				);
131
+			};
132
+		}
133
+
134
+		return function (string $abstract) {
135
+			return new $abstract;
136
+		};
137
+	}
138
+
139
+	/**
140
+	 * Process all dependencies
141
+	 *
142
+	 * @param string $abstract     The class name or container element name to make
143
+	 * @param array  $parameters   User defined parameters that must be used instead of resolved ones
144
+	 * @param array  $dependencies Array of ReflectionParameter
145
+	 *
146
+	 * @throws ContainerException When a dependency cannot be solved.
147
+	 * @return array
148
+	 */
149
+
150
+	protected function process(string $abstract, array $parameters, array $dependencies) : array
151
+	{
152
+		foreach ($dependencies as &$dependency) {
153
+			if (isset($parameters[$dependency->name])) {
154
+				   $dependency = $parameters[$dependency->name];
155
+			} else $dependency = $this->resolve($abstract, $dependency);
156
+		}
157
+
158
+		return $dependencies;
159
+	}
160
+
161
+	/**
162
+	 * Resolve all the given class reflected dependencies.
163
+	 *
164
+	 * @param string               $abstract   The class name or container element name to resolve dependencies.
165
+	 * @param ReflectionParameter  $dependency The class dependency to be resolved.
166
+	 *
167
+	 * @throws ContainerException When a dependency cannot be solved.
168
+	 * @return Object
169
+	 */
170
+
171
+	protected function resolve(string $abstract, ReflectionParameter $dependency)
172
+	{
173
+		$key = $abstract.$dependency->name;
174
+
175
+		if (! isset($this->resolved[$key])) {
176
+			$this->resolved[$key] = $this->generate($abstract, $dependency);
177
+		}
178
+
179
+		return $this->resolved[$key]($this);
180
+	}
181
+
182
+	/**
183
+	 * Generate the dependencies callbacks to jump some conditions in every dependency creation.
184
+	 *
185
+	 * @param string               $abstract   The class name or container element name to resolve dependencies.
186
+	 * @param ReflectionParameter  $dependency The class dependency to be resolved.
187
+	 *
188
+	 * @throws ContainerException When a dependency cannot be solved.
189
+	 * @return Closure
190
+	 */
191
+
192
+	protected function generate(string $abstract, ReflectionParameter $dependency) : Closure
193
+	{
194
+		if ($class = $dependency->getClass()) {
195
+			return $this->build($class->name, "{$abstract}{$class->name}");
196
+		}
197
+
198
+		try {
199
+			$value = $dependency->getDefaultValue();
200
+
201
+			return function () use ($value) {
202
+				return $value;
203
+			};
204
+		} catch (ReflectionException $e) {
205
+			throw new ContainerException("Cannot resolve '$dependency->name' of '$abstract'", 0, $e);
206
+		}
207
+	}
208
+
209
+	/**
210
+	 * Create a build closure for a given class
211
+	 *
212
+	 * @param string $classname The class that need to be build
213
+	 * @param string $entry     Cache entry to search
214
+	 *
215
+	 * @return Closure
216
+	 */
217
+
218
+	protected function build(string $classname, string $entry) : Closure
219
+	{
220
+		if (isset($this->dependencies[$entry])) {
221
+			return $this->dependencies[$entry];
222
+		}
223
+
224
+		return function () use ($classname) {
225
+			return $this->make($classname);
226
+		};
227
+	}
228
+
229
+	/**
230
+	 * Reset the container, removing all the elements, cache and options.
231
+	 *
232
+	 * @return ContainerInterface
233
+	 */
234
+
235
+	public function flush() : ContainerInterface
236
+	{
237
+		$this->collection = [];
238
+		$this->dependencies = [];
239
+		$this->resolving = [];
240
+		$this->resolved = [];
241
+
242
+		return $this;
243
+	}
244
+
245
+	/**
246
+	 * Finds an entry of the container by its identifier and returns it.
247
+	 *
248
+	 * @param string $abstract Identifier of the entry to look for.
249
+	 *
250
+	 * @throws NotFoundException  No entry was found for this identifier.
251
+	 * @throws ContainerException Error while retrieving the entry.
252
+	 *
253
+	 * @return mixed Entry.
254
+	 */
255
+	public function get($abstract)
256
+	{
257
+		if (! isset($this->collection[$abstract])) {
258
+			throw new NotFoundException("Element '$abstract' not found");
259
+		}
260
+
261
+		if ($this->collection[$abstract] instanceof Closure) {
262
+			try {
263
+				return $this->collection[$abstract]($this);
264
+			} catch (Exception $e) {
265
+				throw new ContainerException("An exception was thrown while attempt to make $abstract", 0, $e);
266
+			}
267
+		}
268
+
269
+		return $this->collection[$abstract];
270
+	}
271
+
272
+	/**
273
+	 * Returns true if the container can return an entry for the given identifier.
274
+	 * Returns false otherwise.
275
+	 *
276
+	 * `has($abstract)` returning true does not mean that `get($abstract)` will not throw an exception.
277
+	 * It does however mean that `get($abstract)` will not throw a `NotFoundException`.
278
+	 *
279
+	 * @param string $abstract Identifier of the entry to look for.
280
+	 *
281
+	 * @return boolean
282
+	 */
283
+
284
+	public function has($abstract)
285
+	{
286
+		return isset($this->collection[$abstract]);
287
+	}
288
+
289
+	/**
290
+	 * Verify if an element has a singleton instance.
291
+	 *
292
+	 * @param  string The class name or container element name to resolve dependencies.
293
+	 *
294
+	 * @throws NotFoundException When $abstract does not exists
295
+	 * @return bool
296
+	 */
297
+
298
+	public function isSingleton(string $abstract) : bool
299
+	{
300
+		if (! $this->has($abstract)) {
301
+			throw new NotFoundException("Element '$abstract' not found");
302
+		}
303
+
304
+		return $this->collection[$abstract] instanceof Closure === false;
305
+	}
306
+
307
+	/**
308
+	 * Verify if an element is a instance of something.
309
+	 *
310
+	 * @param  string The class name or container element name to resolve dependencies.
311
+	 *
312
+	 * @throws NotFoundException When $abstract does not exists
313
+	 * @return bool
314
+	 */
315
+
316
+	public function isInstance(string $abstract) : bool
317
+	{
318
+		if (! $this->has($abstract)) {
319
+			throw new NotFoundException("Element '$abstract' not found");
320
+		}
321
+
322
+		return is_object($this->collection[$abstract]);
323
+	}
324
+
325
+	/**
326
+	 * Bind a new element to the container.
327
+	 *
328
+	 * @param string                $abstract The alias name that will be used to call the element.
329
+	 * @param string|closure|object $concrete The element class name, or an closure that makes the element, or the object itself.
330
+	 * @param bool                  $shared   Define if the element will be a singleton instance.
331
+	 *
332
+	 * @return ContainerInterface
333
+	 */
334
+
335
+	public function set(string $abstract, $concrete, bool $shared = false) : ContainerInterface
336
+	{
337
+		if (is_object($concrete)) {
338
+			return $this->instance($abstract, $concrete);
339
+		}
340
+
341
+		if ($concrete instanceof Closure === false) {
342
+			$concrete = function (Container $container) use ($concrete) {
343
+				return $container->make($concrete);
344
+			};
345
+		}
346
+
347
+		if ($shared === true) {
348
+			   $this->collection[$abstract] = $concrete($this);
349
+		} else $this->collection[$abstract] = $concrete;
350
+
351
+		return $this;
352
+	}
353
+
354
+	/**
355
+	 * Bind a new element to the container IF the element name not exists in the container.
356
+	 *
357
+	 * @param string         $abstract The alias name that will be used to call the element.
358
+	 * @param string|closure $concrete The element class name, or an closure that makes the element.
359
+	 * @param bool           $shared   Define if the element will be a singleton instance.
360
+	 *
361
+	 * @return ContainerInterface
362
+	 */
363
+
364
+	public function setIf(string $abstract, $concrete, bool $shared = false) : ContainerInterface
365
+	{
366
+		if (! $this->has($abstract)) {
367
+			$this->set($abstract, $concrete, $shared);
368
+		}
369
+
370
+		return $this;
371
+	}
372
+
373
+	/**
374
+	 * Bind an specific instance to a class dependency.
375
+	 *
376
+	 * @param string         $class          The class full name.
377
+	 * @param string         $dependencyName The dependency full name.
378
+	 * @param string|closure $dependency     The specific object class name or a classure that makes the element.
379
+	 *
380
+	 * @return ContainerInterface
381
+	 */
382
+
383
+	public function setTo(string $class, string $dependencyName, $dependency) : ContainerInterface
384
+	{
385
+		$key = "$class$dependencyName";
386
+
387
+		if ($dependency instanceof Closure === false) {
388
+
389
+			// let's use temporarily the set method to resolve
390
+			// the $dependency if it is not a closure ready to use.
391
+
392
+			$this->set($key, $dependency);
393
+			$resolved = $this->collection[$key];
394
+
395
+			// now we already have a resolved version of $dependency
396
+			// we just need to ensure the dependencies type, a closure.
397
+
398
+			$dependency = function () use ($resolved) {
399
+				return $resolved;
400
+			};
401
+
402
+			// we have used the set method to resolve the $dependency
403
+			// now that we have done all the process let's clear the memory.
404
+
405
+			unset($resolved, $this->collection[$key]);
406
+		}
407
+
408
+		$this->dependencies[$key] = $dependency;
409
+
410
+		return $this;
411
+	}
412
+
413
+	/**
414
+	 * Bind an element that will be construct only one time, and every call for the element,
415
+	 * the same instance will be given.
416
+	 *
417
+	 * @param string         $abstract The alias name that will be used to call the element.
418
+	 * @param string|closure $concrete The element class name, or an closure that makes the element.
419
+	 *
420
+	 * @return ContainerInterface
421
+	 */
422
+
423
+	public function singleton(string $abstract, $concrete) : ContainerInterface
424
+	{
425
+		$this->set($abstract, $concrete, true);
426
+
427
+		return $this;
428
+	}
429
+
430
+	/**
431
+	 * Bind an object to the container.
432
+	 *
433
+	 * @param string $abstract The alias name that will be used to call the object.
434
+	 * @param object $instance The object that will be inserted.
435
+	 *
436
+	 * @throws ContainerException When $instance is not an object.
437
+	 * @return ContainerInterface
438
+	 */
439
+
440
+	public function instance(string $abstract, $instance) : ContainerInterface
441
+	{
442
+		if (! is_object($instance)) {
443
+			throw new ContainerException('Trying to store ' . gettype($instance) . ' as object.');
444
+		}
445
+
446
+		$this->collection[$abstract] = $instance;
447
+
448
+		return $this;
449
+	}
450
+
451
+	/**
452
+	 * Modify an element with a given function that receive the old element as argument.
453
+	 *
454
+	 * @param string  $abstract  The alias name that will be used to call the element.
455
+	 * @param closure $extension The function that receives the old element and return a new or modified one.
456
+	 *
457
+	 * @throws NotFoundException  When no element was found with $abstract key.
458
+	 * @return ContainerInterface
459
+	 */
460
+
461
+	public function extend(string $abstract, closure $extension) : ContainerInterface
462
+	{
463
+		$object = $this->get($abstract);
464
+
465
+		$this->collection[$abstract] = $extension($object, $this);
466
+
467
+		return $this;
468
+	}
469
+
470
+	/**
471
+	 * Makes an resolvable element an singleton.
472
+	 *
473
+	 * @param  string $abstract The alias name that will be used to call the element.
474
+	 *
475
+	 * @throws NotFoundException  When no element was found with $abstract key.
476
+	 * @throws ContainerException When the element on $abstract key is not resolvable.
477
+	 *
478
+	 * @return ContainerInterface
479
+	 */
480
+
481
+	public function share(string $abstract) : ContainerInterface
482
+	{
483
+		$object = $this->get($abstract);
484
+
485
+		$this->collection[$abstract] = $object;
486
+
487
+		return $this;
488
+	}
489 489
 
490 490
 }
Please login to merge, or discard this patch.