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

Container::getArgsResolver()   B

Complexity

Conditions 10
Paths 3

Size

Total Lines 50

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 10

Importance

Changes 0
Metric Value
dl 0
loc 50
ccs 21
cts 21
cp 1
rs 7.2242
c 0
b 0
f 0
cc 10
nc 3
nop 2
crap 10

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
}