Issues (4)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Container.php (4 issues)

Upgrade to new PHP Analysis Engine

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
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 31 and the first side effect is on line 21.

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.

Loading history...
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
Bug Best Practice introduced by
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 my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
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
Bug Best Practice introduced by
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 my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
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
Bug Best Practice introduced by
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 my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
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