1
|
|
|
<?php declare(strict_types=1); |
2
|
|
|
|
3
|
|
|
namespace Dms\Ioc; |
4
|
|
|
|
5
|
|
|
use Dms\Core\Exception\InvalidArgumentException; |
6
|
|
|
use Dms\Core\Ioc\IIocContainer; |
7
|
|
|
use Dms\Core\Util\Debug; |
8
|
|
|
use Illuminate\Container\Container; |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* The laravel ioc container. |
12
|
|
|
* |
13
|
|
|
* @author Elliot Levin <[email protected]> |
14
|
|
|
*/ |
15
|
|
|
class IlluminateContainer implements IIocContainer |
16
|
|
|
{ |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* @var Container |
20
|
|
|
*/ |
21
|
|
|
private $container; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* @var array |
25
|
|
|
*/ |
26
|
|
|
private $cacheForHas = []; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @param Container $container |
30
|
|
|
*/ |
31
|
|
|
public function __construct(Container $container) |
32
|
|
|
{ |
33
|
|
|
$this->container = $container; |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Binds the supplied class or interface to the supplied |
39
|
|
|
* concrete class name. |
40
|
|
|
* |
41
|
|
|
* @param string $scope |
42
|
|
|
* @param string $abstract |
43
|
|
|
* @param string $concrete |
44
|
|
|
* |
45
|
|
|
* @return void |
46
|
|
|
*/ |
47
|
|
View Code Duplication |
public function bind(string $scope, string $abstract, string $concrete) |
|
|
|
|
48
|
|
|
{ |
49
|
|
|
$this->validateScope(__METHOD__, $scope); |
50
|
|
|
|
51
|
|
|
if ($scope === self::SCOPE_INSTANCE_PER_RESOLVE) { |
52
|
|
|
$this->container->bind($abstract, $concrete); |
53
|
|
|
} else { |
54
|
|
|
$this->container->singleton($abstract, $concrete); |
55
|
|
|
} |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Binds the supplied class or interface to the return value |
60
|
|
|
* of the supplied callback. |
61
|
|
|
* |
62
|
|
|
* @param string $scope |
63
|
|
|
* @param string $abstract |
64
|
|
|
* @param callable $factory |
65
|
|
|
* |
66
|
|
|
* @return void |
67
|
|
|
*/ |
68
|
|
View Code Duplication |
public function bindCallback(string $scope, string $abstract, callable $factory) |
|
|
|
|
69
|
|
|
{ |
70
|
|
|
$this->validateScope(__METHOD__, $scope); |
71
|
|
|
|
72
|
|
|
if ($scope === self::SCOPE_INSTANCE_PER_RESOLVE) { |
73
|
|
|
$this->container->bind($abstract, $factory); |
74
|
|
|
} else { |
75
|
|
|
$this->container->singleton($abstract, $factory); |
76
|
|
|
} |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Binds the supplied abstract class or interface to the supplied value. |
81
|
|
|
* |
82
|
|
|
* @param string $abstract |
83
|
|
|
* @param mixed $concrete |
84
|
|
|
* |
85
|
|
|
* @return void |
86
|
|
|
*/ |
87
|
|
|
public function bindValue(string $abstract, $concrete) |
88
|
|
|
{ |
89
|
|
|
$this->container->instance($abstract, $concrete); |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Binds the supplied class or interface to the supplied value for the duration |
94
|
|
|
* of the supplied callback. |
95
|
|
|
* |
96
|
|
|
* @param string $abstract |
97
|
|
|
* @param mixed $concrete |
98
|
|
|
* @param callable $callback |
99
|
|
|
* |
100
|
|
|
* @return mixed The value returned from the callback |
101
|
|
|
*/ |
102
|
|
|
public function bindForCallback(string $abstract, $concrete, callable $callback) |
103
|
|
|
{ |
104
|
|
|
$hasOriginal = $this->container->bound($abstract); |
105
|
|
|
$originalInstance = false; |
106
|
|
|
|
107
|
|
|
if ($hasOriginal) { |
108
|
|
|
$binding = $this->container->getBindings()[$abstract] ?? null; |
109
|
|
|
|
110
|
|
|
$hasInstance = $binding && $binding['shared'] && $this->container->resolved($abstract); |
111
|
|
|
|
112
|
|
|
if ($hasInstance) { |
113
|
|
|
$originalInstance = $this->container->make($abstract); |
114
|
|
|
} |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
$this->container->instance($abstract, $concrete); |
118
|
|
|
|
119
|
|
|
try { |
120
|
|
|
$return = $callback(); |
121
|
|
|
return $return; |
122
|
|
|
} finally { |
123
|
|
|
unset($this->container[$abstract]); |
124
|
|
|
|
125
|
|
|
if ($hasOriginal && (isset($binding) && $binding)) { |
126
|
|
|
$this->container->bind($abstract, $binding['concrete'], $binding['shared']); |
127
|
|
|
|
128
|
|
|
if ($originalInstance) { |
129
|
|
|
$this->container->instance($abstract, $originalInstance); |
130
|
|
|
} |
131
|
|
|
} |
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* {@inheritdoc} |
137
|
|
|
*/ |
138
|
|
|
public function get($id) |
139
|
|
|
{ |
140
|
|
|
return $this->container->make($id); |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* {@inheritdoc} |
145
|
|
|
*/ |
146
|
|
|
public function has($id) |
147
|
|
|
{ |
148
|
|
|
if ($this->hasIsCached($id)) { |
149
|
|
|
return $this->hasFromCache($id); |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
$has = $this->container->bound($id) || $this->isInstantiable($id); |
153
|
|
|
|
154
|
|
|
$this->cacheHas($id, $has); |
155
|
|
|
|
156
|
|
|
return $has; |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* Alias a type to a different name. |
161
|
|
|
* |
162
|
|
|
* @param string $abstract |
163
|
|
|
* @param string $alias |
164
|
|
|
* @return void |
165
|
|
|
*/ |
166
|
|
|
public function alias($abstract, $alias) |
167
|
|
|
{ |
168
|
|
|
$this->container->alias($abstract, $alias); |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* Create object based on the parameters passed |
173
|
|
|
* |
174
|
|
|
* @param string $abstract |
175
|
|
|
* @param array $parameters |
176
|
|
|
* @return mixed |
177
|
|
|
*/ |
178
|
|
|
public function makeWith($abstract, array $parameters = []) |
179
|
|
|
{ |
180
|
|
|
return $this->container->make($abstract, $parameters); |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* Get the container itself |
185
|
|
|
* |
186
|
|
|
* @return Container |
187
|
|
|
*/ |
188
|
|
|
public function getIlluminateContainer() |
189
|
|
|
{ |
190
|
|
|
return $this->container; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
private function cacheHas($id, $has) |
194
|
|
|
{ |
195
|
|
|
$this->cacheForHas[$id] = $has; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
private function hasFromCache($id) |
199
|
|
|
{ |
200
|
|
|
return $this->cacheForHas[$id]; |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
private function hasIsCached($id) |
204
|
|
|
{ |
205
|
|
|
return array_key_exists($id, $this->cacheForHas); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
private function isInstantiable($id) |
209
|
|
|
{ |
210
|
|
|
if (class_exists($id)) { |
211
|
|
|
return true; |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
try { |
215
|
|
|
$reflectionClass = new \ReflectionClass($id); |
216
|
|
|
|
217
|
|
|
return $reflectionClass->isInstantiable(); |
218
|
|
|
} catch (\ReflectionException $e) { |
219
|
|
|
return false; |
220
|
|
|
} |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
private function validateScope(string $method, string $scope) |
224
|
|
|
{ |
225
|
|
|
$validScopes = [self::SCOPE_INSTANCE_PER_RESOLVE, self::SCOPE_SINGLETON]; |
226
|
|
|
|
227
|
|
|
if (!in_array($scope, $validScopes, true)) { |
228
|
|
|
throw InvalidArgumentException::format( |
229
|
|
|
'Invalid scope supplied to %s: expecting one of (%s), \'%s\' given', |
230
|
|
|
$method, |
231
|
|
|
Debug::formatValues($validScopes), |
232
|
|
|
$scope |
233
|
|
|
); |
234
|
|
|
} |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
} |
238
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.