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