Completed
Push — master ( 54c8c3...3ed7b7 )
by maxime
12:16
created

Container::delegate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 3
crap 1
1
<?php
2
3
namespace Aidphp\Di;
4
5
use Psr\Container\ContainerInterface;
6
use Psr\Container\ContainerExceptionInterface;
7
use ReflectionClass;
8
use ReflectionMethod;
9
use ReflectionException;
10
11
class Container implements ContainerInterface
12
{
13
    protected $definitions = [];
14
    protected $factories   = [];
15
    protected $instances   = [];
16
17 5
    public function define(string $id, string $class = null, array $params = [], bool $shared = true): self
18
    {
19 5
        $this->definitions[$id] = [$class ?? $id, $params, $shared];
20 5
        return $this;
21
    }
22
23 2
    public function delegate(string $id, callable $factory, bool $shared = true): self
24
    {
25 2
        $this->factories[$id] = [$factory, $shared];
26 2
        return $this;
27
    }
28
29 20
    public function get($id)
30
    {
31 20
        if (isset($this->instances[$id]))
32
        {
33 1
            return $this->instances[$id];
34
        }
35
36 20
        if (! isset($this->factories[$id]))
37
        {
38 19
            $this->factories[$id] = isset($this->definitions[$id])
39 5
                ? [$this->createFactory($this->definitions[$id][0], $this->definitions[$id][1]), $this->definitions[$id][2]]
40 17
                : [$this->createFactory($id), false];
41
        }
42
43 15
        $instance = $this->factories[$id][0]($this);
44
45 13
        if ($this->factories[$id][1])
46
        {
47 7
            $this->instances[$id] = $instance;
48
        }
49
50 13
        return $instance;
51
    }
52
53 15
    public function has($id): bool
54
    {
55 15
        return isset($this->instances[$id])
56 15
            || isset($this->factories[$id])
57 15
            || isset($this->definitions[$id])
58 15
            || class_exists($id);
59
    }
60
61 19
    protected function createFactory(string $class, array $params = []): callable
62
    {
63
        try
64
        {
65 19
            $refClass = new ReflectionClass($class);
66
        }
67 1
        catch (ReflectionException $e)
68
        {
69 1
            throw new NotFoundException($class);
70
        }
71
72 18
        if (! $refClass->isInstantiable())
73
        {
74 6
            throw new ContainerException('The class "' . $refClass->name . '" can not be instantiate');
75
        }
76
77 14
        if (null !== ($refMethod = $refClass->getConstructor()))
78
        {
79 12
            $resolver = $this->getArgsResolver($refMethod, $params);
80
            $factory  = function ($dic) use ($class, $resolver) {return new $class(...$resolver());};
0 ignored issues
show
Unused Code introduced by
The parameter $dic is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
81
        }
82
        else
83
        {
84
            $factory = function ($dic) use ($class) {return new $class;};
0 ignored issues
show
Unused Code introduced by
The parameter $dic is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
85
        }
86
87 14
        return $factory;
88
    }
89
90 12
    protected function getArgsResolver(ReflectionMethod $method, array $params = []): callable
91
    {
92 12
        $paramsInfo = [];
93
94 12
        foreach ($method->getParameters() as $param)
95
        {
96 11
            $paramsInfo[] = [
97 11
                ($class = $param->getClass()) ? $class->name : null,
98 11
                $param
99
            ];
100
        }
101
102
        return function () use ($paramsInfo, $params) {
103 12
            $values = [];
104
105 12
            foreach ($paramsInfo as [$class, $param])
106
            {
107 11
                if ($class)
108
                {
109
                    try
110
                    {
111 7
                        $values[] = $this->get($class);
0 ignored issues
show
Bug introduced by
The variable $class does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
112
                    }
113 2
                    catch (ContainerExceptionInterface $e)
114
                    {
115 2
                        if (! $param->allowsNull())
0 ignored issues
show
Bug introduced by
The variable $param does not exist. Did you mean $params?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
116
                        {
117 1
                            throw new ParameterNotFoundException($param, $e);
0 ignored issues
show
Documentation introduced by
$e is of type object<Psr\Container\ContainerExceptionInterface>, but the function expects a null|object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
118
                        }
119
120 6
                        $values[] = null;
121
                    }
122
                }
123 4
                elseif ($params)
0 ignored issues
show
Bug Best Practice introduced by
The expression $params of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
124
                {
125 1
                    $values[] = array_shift($params);
126
                }
127 3
                elseif ($param->isOptional())
0 ignored issues
show
Bug introduced by
The variable $param does not exist. Did you mean $params?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
128
                {
129 2
                    $values[] = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null;
0 ignored issues
show
Bug introduced by
The variable $param does not exist. Did you mean $params?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
130
                }
131
                else
132
                {
133 10
                    throw new ParameterNotFoundException($param);
134
                }
135
            }
136
137 10
            return $values;
138 12
        };
139
    }
140
}