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;};
|
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);
|
114
|
|
|
}
|
115
|
2 |
|
catch (ContainerExceptionInterface $e)
|
116
|
|
|
{
|
117
|
2 |
|
if (! $param->allowsNull())
|
118
|
|
|
{
|
119
|
1 |
|
throw new ParameterNotFoundException($param, $e);
|
120
|
|
|
}
|
121
|
|
|
|
122
|
10 |
|
$values[] = null;
|
123
|
|
|
}
|
124
|
|
|
}
|
125
|
4 |
|
elseif ($params)
|
|
|
|
|
126
|
|
|
{
|
127
|
1 |
|
$values[] = array_shift($params);
|
128
|
|
|
}
|
129
|
3 |
|
elseif ($param->isOptional())
|
130
|
|
|
{
|
131
|
2 |
|
$values[] = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null;
|
132
|
|
|
}
|
133
|
|
|
else
|
134
|
|
|
{
|
135
|
1 |
|
throw new ParameterNotFoundException($param);
|
136
|
|
|
}
|
137
|
|
|
}
|
138
|
|
|
|
139
|
14 |
|
return $values;
|
140
|
16 |
|
};
|
141
|
|
|
}
|
142
|
|
|
} |
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.