Passed
Push — master ( ab0d76...0f78eb )
by Alex
02:02
created
src/Container.php 1 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.