Container::createFactory()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 28
ccs 9
cts 9
cp 1
rs 9.472
c 0
b 0
f 0
cc 4
nc 4
nop 2
crap 4
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 8
    public function define(string $id, string $class = null, array $params = [], bool $shared = true): self
18
    {
19 8
        unset($this->instances[$id], $this->factories[$id]);
20 8
        $this->definitions[$id] = [$class ?? $id, $params, $shared];
21 8
        return $this;
22
    }
23
24 2
    public function delegate(string $id, callable $factory, bool $shared = true): self
25
    {
26 2
        unset($this->instances[$id], $this->factories[$id]);
27 2
        $this->factories[$id] = [$factory, $shared];
28 2
        return $this;
29
    }
30
31 24
    public function get($id)
32
    {
33 24
        if (isset($this->instances[$id]))
34
        {
35 2
            return $this->instances[$id];
36
        }
37
38 24
        if (! isset($this->factories[$id]))
39
        {
40 23
            $this->factories[$id] = isset($this->definitions[$id])
41 8
                ? [$this->createFactory($this->definitions[$id][0], $this->definitions[$id][1]), $this->definitions[$id][2]]
42 21
                : [$this->createFactory($id), false];
43
        }
44
45 19
        $instance = $this->factories[$id][0]($this);
46
47 17
        if ($this->factories[$id][1])
48
        {
49 8
            $this->instances[$id] = $instance;
50
        }
51
52 17
        return $instance;
53
    }
54
55 15
    public function has($id): bool
56
    {
57 15
        return isset($this->instances[$id])
58 15
            || isset($this->factories[$id])
59 15
            || isset($this->definitions[$id])
60 15
            || class_exists($id);
61
    }
62
63 23
    protected function createFactory(string $class, array $params = []): callable
64
    {
65
        try
66
        {
67 23
            $refClass = new ReflectionClass($class);
68
        }
69 1
        catch (ReflectionException $e)
70
        {
71 1
            throw new NotFoundException($class);
72
        }
73
74 22
        if (! $refClass->isInstantiable())
75
        {
76 6
            throw new ContainerException('The class "' . $refClass->name . '" can not be instantiate');
77
        }
78
79 18
        if (null !== ($refMethod = $refClass->getConstructor()))
80
        {
81 16
            $resolver = $this->getArgsResolver($refMethod, $params);
82
            $factory  = function ($dic) use ($class, $resolver) {return new $class(...$resolver($dic));};
83
        }
84
        else
85
        {
86
            $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...
87
        }
88
89 18
        return $factory;
90
    }
91
92 16
    protected function getArgsResolver(ReflectionMethod $method, array $params = []): callable
93
    {
94 16
        $paramsInfo = [];
95
96 16
        foreach ($method->getParameters() as $param)
97
        {
98 15
            $paramsInfo[] = [
99 15
                ($class = $param->getClass()) ? $class->name : null,
100 15
                $param
101
            ];
102
        }
103
104
        return function ($dic) use ($paramsInfo, $params) {
105 16
            $values = [];
106
107 16
            foreach ($paramsInfo as [$class, $param])
108
            {
109 15
                if ($class)
110
                {
111
                    try
112
                    {
113 11
                        $values[] = $dic->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...
114
                    }
115 2
                    catch (ContainerExceptionInterface $e)
116
                    {
117 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...
118
                        {
119 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...
120
                        }
121
122 10
                        $values[] = null;
123
                    }
124
                }
125 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...
126
                {
127 1
                    $values[] = array_shift($params);
128
                }
129 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...
130
                {
131 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...
132
                }
133
                else
134
                {
135 1
                    throw new ParameterNotFoundException($param);
136
                }
137
            }
138
139 14
            return $values;
140 16
        };
141
    }
142
}