This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
0 ignored issues
–
show
|
|||
2 | |||
3 | /** |
||
4 | * Codeburner Framework. |
||
5 | * |
||
6 | * @author Alex Rohleder <[email protected]> |
||
7 | * @copyright 2015 Alex Rohleder |
||
8 | * @license http://opensource.org/licenses/MIT |
||
9 | */ |
||
10 | |||
11 | namespace Codeburner\Container; |
||
12 | |||
13 | use Closure, Exception, ReflectionClass, ReflectionException, ReflectionFunction, ReflectionParameter; |
||
14 | use Codeburner\Container\Exceptions\{ContainerException, NotFoundException}; |
||
15 | |||
16 | /** |
||
17 | * Explicit Avoiding autoload for the container interface, |
||
18 | * because it is aways needed. |
||
19 | */ |
||
20 | |||
21 | 1 | require_once __DIR__ . '/ContainerInterface.php'; |
|
22 | |||
23 | /** |
||
24 | * The container class is reponsable to construct all objects |
||
25 | * of the project automatically, with total abstraction of dependencies. |
||
26 | * |
||
27 | * @author Alex Rohleder <[email protected]> |
||
28 | * @version 1.0.0 |
||
29 | */ |
||
30 | |||
31 | class Container implements ContainerInterface |
||
32 | { |
||
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 | 3 | public function call($function, array $parameters = []) |
|
76 | { |
||
77 | 3 | $inspector = new ReflectionFunction($function); |
|
78 | |||
79 | 3 | $dependencies = $inspector->getParameters(); |
|
80 | 3 | $dependencies = $this->process('', $parameters, $dependencies); |
|
81 | |||
82 | 3 | 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 | 21 | public function make(string $abstract, array $parameters = []) |
|
96 | { |
||
97 | try { |
||
98 | 21 | if (! isset($this->resolving[$abstract])) { |
|
99 | 21 | $this->resolving[$abstract] = $this->construct($abstract); |
|
100 | } |
||
101 | |||
102 | 20 | return $this->resolving[$abstract]($abstract, $parameters); |
|
103 | 2 | } catch (ReflectionException $e) { |
|
104 | 1 | 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 | 21 | protected function construct(string $abstract) : Closure |
|
118 | { |
||
119 | 21 | $inspector = new ReflectionClass($abstract); |
|
120 | |||
121 | 20 | 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 | 10 | return $inspector->newInstanceArgs( |
|
129 | 10 | $this->process($abstract, $parameters, $dependencies) |
|
130 | ); |
||
131 | 10 | }; |
|
132 | } |
||
133 | |||
134 | return function (string $abstract) { |
||
135 | 17 | return new $abstract; |
|
136 | 17 | }; |
|
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 | 12 | protected function process(string $abstract, array $parameters, array $dependencies) : array |
|
151 | { |
||
152 | 12 | foreach ($dependencies as &$dependency) { |
|
153 | 11 | if (isset($parameters[$dependency->name])) { |
|
154 | 1 | $dependency = $parameters[$dependency->name]; |
|
155 | 11 | } else $dependency = $this->resolve($abstract, $dependency); |
|
156 | } |
||
157 | |||
158 | 11 | 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 | 10 | protected function resolve(string $abstract, ReflectionParameter $dependency) |
|
172 | { |
||
173 | 10 | $key = $abstract.$dependency->name; |
|
174 | |||
175 | 10 | if (! isset($this->resolved[$key])) { |
|
176 | 10 | $this->resolved[$key] = $this->generate($abstract, $dependency); |
|
177 | } |
||
178 | |||
179 | 9 | 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 | 10 | protected function generate(string $abstract, ReflectionParameter $dependency) : Closure |
|
193 | { |
||
194 | 10 | if ($class = $dependency->getClass()) { |
|
195 | 9 | return $this->build($class->name, "{$abstract}{$class->name}"); |
|
196 | } |
||
197 | |||
198 | try { |
||
199 | 2 | $value = $dependency->getDefaultValue(); |
|
200 | |||
201 | return function () use ($value) { |
||
202 | 1 | return $value; |
|
203 | 1 | }; |
|
204 | 1 | } catch (ReflectionException $e) { |
|
205 | 1 | 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 | 9 | protected function build(string $classname, string $entry) : Closure |
|
219 | { |
||
220 | 9 | if (isset($this->dependencies[$entry])) { |
|
221 | 3 | return $this->dependencies[$entry]; |
|
222 | } |
||
223 | |||
224 | return function () use ($classname) { |
||
225 | 6 | return $this->make($classname); |
|
226 | 6 | }; |
|
227 | } |
||
228 | |||
229 | /** |
||
230 | * Reset the container, removing all the elements, cache and options. |
||
231 | * |
||
232 | * @return ContainerInterface |
||
233 | */ |
||
234 | |||
235 | 1 | public function flush() : ContainerInterface |
|
236 | { |
||
237 | 1 | $this->collection = []; |
|
238 | 1 | $this->dependencies = []; |
|
239 | 1 | $this->resolving = []; |
|
240 | 1 | $this->resolved = []; |
|
241 | |||
242 | 1 | 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 | 11 | public function get($abstract) |
|
256 | { |
||
257 | 11 | if (! isset($this->collection[$abstract])) { |
|
258 | 3 | throw new NotFoundException("Element '$abstract' not found"); |
|
259 | } |
||
260 | |||
261 | 8 | if ($this->collection[$abstract] instanceof Closure) { |
|
262 | try { |
||
263 | 5 | return $this->collection[$abstract]($this); |
|
264 | 1 | } catch (Exception $e) { |
|
265 | 1 | throw new ContainerException("An exception was thrown while attempt to make $abstract", 0, $e); |
|
266 | } |
||
267 | } |
||
268 | |||
269 | 4 | 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 | 10 | public function has($abstract) |
|
285 | { |
||
286 | 10 | 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 | 6 | public function isSingleton(string $abstract) : bool |
|
299 | { |
||
300 | 6 | if (! $this->has($abstract)) { |
|
301 | 1 | throw new NotFoundException("Element '$abstract' not found"); |
|
302 | } |
||
303 | |||
304 | 5 | 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 | 2 | public function isInstance(string $abstract) : bool |
|
317 | { |
||
318 | 2 | if (! $this->has($abstract)) { |
|
319 | 1 | throw new NotFoundException("Element '$abstract' not found"); |
|
320 | } |
||
321 | |||
322 | 1 | 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 | 15 | public function set(string $abstract, $concrete, bool $shared = false) : ContainerInterface |
|
336 | { |
||
337 | 15 | if (is_object($concrete)) { |
|
338 | 4 | return $this->instance($abstract, $concrete); |
|
339 | } |
||
340 | |||
341 | 11 | if ($concrete instanceof Closure === false) { |
|
342 | $concrete = function (Container $container) use ($concrete) { |
||
343 | 8 | return $container->make($concrete); |
|
344 | 11 | }; |
|
345 | } |
||
346 | |||
347 | 11 | if ($shared === true) { |
|
348 | 5 | $this->collection[$abstract] = $concrete($this); |
|
349 | 6 | } else $this->collection[$abstract] = $concrete; |
|
350 | |||
351 | 11 | 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 | 1 | public function setIf(string $abstract, $concrete, bool $shared = false) : ContainerInterface |
|
365 | { |
||
366 | 1 | if (! $this->has($abstract)) { |
|
367 | 1 | $this->set($abstract, $concrete, $shared); |
|
368 | } |
||
369 | |||
370 | 1 | return $this; |
|
0 ignored issues
–
show
The return type of
return $this; (Codeburner\Container\Container ) is incompatible with the return type declared by the interface Codeburner\Container\ContainerInterface::setIf of type self .
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function ![]() |
|||
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 | 3 | public function setTo(string $class, string $dependencyName, $dependency) : ContainerInterface |
|
384 | { |
||
385 | 3 | $key = "$class$dependencyName"; |
|
386 | |||
387 | 3 | 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 | 2 | $this->set($key, $dependency); |
|
393 | 2 | $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 | 2 | $dependency = function () use ($resolved) { |
|
399 | 2 | return $resolved; |
|
400 | 2 | }; |
|
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 | 2 | unset($resolved, $this->collection[$key]); |
|
406 | } |
||
407 | |||
408 | 3 | $this->dependencies[$key] = $dependency; |
|
409 | |||
410 | 3 | 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 | 3 | public function singleton(string $abstract, $concrete) : ContainerInterface |
|
424 | { |
||
425 | 3 | $this->set($abstract, $concrete, true); |
|
426 | |||
427 | 3 | return $this; |
|
0 ignored issues
–
show
The return type of
return $this; (Codeburner\Container\Container ) is incompatible with the return type declared by the interface Codeburner\Container\ContainerInterface::singleton of type self .
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function ![]() |
|||
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 | 7 | public function instance(string $abstract, $instance) : ContainerInterface |
|
441 | { |
||
442 | 7 | if (! is_object($instance)) { |
|
443 | 1 | throw new ContainerException('Trying to store ' . gettype($instance) . ' as object.'); |
|
444 | } |
||
445 | |||
446 | 6 | $this->collection[$abstract] = $instance; |
|
447 | |||
448 | 6 | return $this; |
|
0 ignored issues
–
show
The return type of
return $this; (Codeburner\Container\Container ) is incompatible with the return type declared by the interface Codeburner\Container\ContainerInterface::instance of type self .
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function ![]() |
|||
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 | 3 | public function extend(string $abstract, closure $extension) : ContainerInterface |
|
462 | { |
||
463 | 3 | $object = $this->get($abstract); |
|
464 | |||
465 | 2 | $this->collection[$abstract] = $extension($object, $this); |
|
466 | |||
467 | 2 | 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 | 2 | public function share(string $abstract) : ContainerInterface |
|
482 | { |
||
483 | 2 | $object = $this->get($abstract); |
|
484 | |||
485 | 1 | $this->collection[$abstract] = $object; |
|
486 | |||
487 | 1 | return $this; |
|
488 | } |
||
489 | |||
490 | } |
||
491 |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.