1
|
|
|
<?php |
2
|
|
|
// +---------------------------------------------------------------------- |
3
|
|
|
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
4
|
|
|
// +---------------------------------------------------------------------- |
5
|
|
|
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. |
6
|
|
|
// +---------------------------------------------------------------------- |
7
|
|
|
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
8
|
|
|
// +---------------------------------------------------------------------- |
9
|
|
|
// | Author: liu21st <[email protected]> |
10
|
|
|
// +---------------------------------------------------------------------- |
11
|
|
|
declare (strict_types = 1); |
12
|
|
|
|
13
|
|
|
namespace think; |
14
|
|
|
|
15
|
|
|
use ArrayAccess; |
16
|
|
|
use ArrayIterator; |
17
|
|
|
use Closure; |
18
|
|
|
use Countable; |
19
|
|
|
use InvalidArgumentException; |
20
|
|
|
use IteratorAggregate; |
21
|
|
|
use Psr\Container\ContainerInterface; |
22
|
|
|
use ReflectionClass; |
23
|
|
|
use ReflectionException; |
24
|
|
|
use ReflectionFunction; |
25
|
|
|
use ReflectionFunctionAbstract; |
26
|
|
|
use ReflectionMethod; |
27
|
|
|
use think\exception\ClassNotFoundException; |
28
|
|
|
use think\exception\FuncNotFoundException; |
29
|
|
|
use think\helper\Str; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* 容器管理类 支持PSR-11 |
33
|
|
|
*/ |
34
|
|
|
class Container implements ContainerInterface, ArrayAccess, IteratorAggregate, Countable |
35
|
|
|
{ |
36
|
|
|
/** |
37
|
|
|
* 容器对象实例 |
38
|
|
|
* @var Container|Closure |
39
|
|
|
*/ |
40
|
|
|
protected static $instance; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* 容器中的对象实例 |
44
|
|
|
* @var array |
45
|
|
|
*/ |
46
|
|
|
protected $instances = []; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* 容器绑定标识 |
50
|
|
|
* @var array |
51
|
|
|
*/ |
52
|
|
|
protected $bind = []; |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* 容器回调 |
56
|
|
|
* @var array |
57
|
|
|
*/ |
58
|
|
|
protected $invokeCallback = []; |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* 获取当前容器的实例(单例) |
62
|
|
|
* @access public |
63
|
|
|
* @return static |
64
|
|
|
*/ |
65
|
27 |
|
public static function getInstance() |
66
|
|
|
{ |
67
|
27 |
|
if (is_null(static::$instance)) { |
68
|
3 |
|
static::$instance = new static; |
69
|
|
|
} |
70
|
|
|
|
71
|
27 |
|
if (static::$instance instanceof Closure) { |
72
|
3 |
|
return (static::$instance)(); |
73
|
|
|
} |
74
|
|
|
|
75
|
27 |
|
return static::$instance; |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* 设置当前容器的实例 |
80
|
|
|
* @access public |
81
|
|
|
* @param object|Closure $instance |
82
|
|
|
* @return void |
83
|
|
|
*/ |
84
|
174 |
|
public static function setInstance($instance): void |
85
|
|
|
{ |
86
|
174 |
|
static::$instance = $instance; |
87
|
174 |
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* 注册一个容器对象回调 |
91
|
|
|
* |
92
|
|
|
* @param string|Closure $abstract |
93
|
|
|
* @param Closure|null $callback |
94
|
|
|
* @return void |
95
|
|
|
*/ |
96
|
3 |
|
public function resolving($abstract, Closure $callback = null): void |
97
|
|
|
{ |
98
|
3 |
|
if ($abstract instanceof Closure) { |
99
|
3 |
|
$this->invokeCallback['*'][] = $abstract; |
100
|
3 |
|
return; |
101
|
|
|
} |
102
|
|
|
|
103
|
3 |
|
$abstract = $this->getAlias($abstract); |
104
|
|
|
|
105
|
3 |
|
$this->invokeCallback[$abstract][] = $callback; |
106
|
3 |
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* 获取容器中的对象实例 不存在则创建 |
110
|
|
|
* @access public |
111
|
|
|
* @param string $abstract 类名或者标识 |
112
|
|
|
* @param array|true $vars 变量 |
113
|
|
|
* @param bool $newInstance 是否每次创建新的实例 |
114
|
|
|
* @return object |
115
|
|
|
*/ |
116
|
3 |
|
public static function pull(string $abstract, array $vars = [], bool $newInstance = false) |
117
|
|
|
{ |
118
|
3 |
|
return static::getInstance()->make($abstract, $vars, $newInstance); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* 获取容器中的对象实例 |
123
|
|
|
* @access public |
124
|
|
|
* @param string $abstract 类名或者标识 |
125
|
|
|
* @return object |
126
|
|
|
*/ |
127
|
45 |
|
public function get($abstract) |
128
|
|
|
{ |
129
|
45 |
|
if ($this->has($abstract)) { |
130
|
42 |
|
return $this->make($abstract); |
131
|
|
|
} |
132
|
|
|
|
133
|
3 |
|
throw new ClassNotFoundException('class not exists: ' . $abstract, $abstract); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* 绑定一个类、闭包、实例、接口实现到容器 |
138
|
|
|
* @access public |
139
|
|
|
* @param string|array $abstract 类标识、接口 |
140
|
|
|
* @param mixed $concrete 要绑定的类、闭包或者实例 |
141
|
|
|
* @return $this |
142
|
|
|
*/ |
143
|
27 |
|
public function bind($abstract, $concrete = null) |
144
|
|
|
{ |
145
|
27 |
|
if (is_array($abstract)) { |
146
|
9 |
|
foreach ($abstract as $key => $val) { |
147
|
7 |
|
$this->bind($key, $val); |
148
|
|
|
} |
149
|
24 |
|
} elseif ($concrete instanceof Closure) { |
150
|
9 |
|
$this->bind[$abstract] = $concrete; |
151
|
18 |
|
} elseif (is_object($concrete)) { |
152
|
12 |
|
$this->instance($abstract, $concrete); |
153
|
|
|
} else { |
154
|
9 |
|
$abstract = $this->getAlias($abstract); |
155
|
|
|
|
156
|
9 |
|
$this->bind[$abstract] = $concrete; |
157
|
|
|
} |
158
|
|
|
|
159
|
27 |
|
return $this; |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* 根据别名获取真实类名 |
164
|
|
|
* @param string $abstract |
165
|
|
|
* @return string |
166
|
|
|
*/ |
167
|
102 |
|
public function getAlias(string $abstract): string |
168
|
|
|
{ |
169
|
102 |
|
if (isset($this->bind[$abstract])) { |
170
|
69 |
|
$bind = $this->bind[$abstract]; |
171
|
|
|
|
172
|
69 |
|
if (is_string($bind)) { |
173
|
63 |
|
return $this->getAlias($bind); |
174
|
|
|
} |
175
|
|
|
} |
176
|
|
|
|
177
|
102 |
|
return $abstract; |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* 绑定一个类实例到容器 |
182
|
|
|
* @access public |
183
|
|
|
* @param string $abstract 类名或者标识 |
184
|
|
|
* @param object $instance 类的实例 |
185
|
|
|
* @return $this |
186
|
|
|
*/ |
187
|
45 |
|
public function instance(string $abstract, $instance) |
188
|
|
|
{ |
189
|
45 |
|
$abstract = $this->getAlias($abstract); |
190
|
|
|
|
191
|
45 |
|
$this->instances[$abstract] = $instance; |
192
|
|
|
|
193
|
45 |
|
return $this; |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* 判断容器中是否存在类及标识 |
198
|
|
|
* @access public |
199
|
|
|
* @param string $abstract 类名或者标识 |
200
|
|
|
* @return bool |
201
|
|
|
*/ |
202
|
45 |
|
public function bound(string $abstract): bool |
203
|
|
|
{ |
204
|
45 |
|
return isset($this->bind[$abstract]) || isset($this->instances[$abstract]); |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* 判断容器中是否存在类及标识 |
209
|
|
|
* @access public |
210
|
|
|
* @param string $name 类名或者标识 |
211
|
|
|
* @return bool |
212
|
|
|
*/ |
213
|
45 |
|
public function has($name): bool |
214
|
|
|
{ |
215
|
45 |
|
return $this->bound($name); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* 判断容器中是否存在对象实例 |
220
|
|
|
* @access public |
221
|
|
|
* @param string $abstract 类名或者标识 |
222
|
|
|
* @return bool |
223
|
|
|
*/ |
224
|
12 |
|
public function exists(string $abstract): bool |
225
|
|
|
{ |
226
|
12 |
|
$abstract = $this->getAlias($abstract); |
227
|
|
|
|
228
|
12 |
|
return isset($this->instances[$abstract]); |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* 创建类的实例 已经存在则直接获取 |
233
|
|
|
* @access public |
234
|
|
|
* @param string $abstract 类名或者标识 |
235
|
|
|
* @param array $vars 变量 |
236
|
|
|
* @param bool $newInstance 是否每次创建新的实例 |
237
|
|
|
* @return mixed |
238
|
|
|
*/ |
239
|
78 |
|
public function make(string $abstract, array $vars = [], bool $newInstance = false) |
240
|
|
|
{ |
241
|
78 |
|
$abstract = $this->getAlias($abstract); |
242
|
|
|
|
243
|
78 |
|
if (isset($this->instances[$abstract]) && !$newInstance) { |
244
|
45 |
|
return $this->instances[$abstract]; |
245
|
|
|
} |
246
|
|
|
|
247
|
72 |
|
if (isset($this->bind[$abstract]) && $this->bind[$abstract] instanceof Closure) { |
248
|
9 |
|
$object = $this->invokeFunction($this->bind[$abstract], $vars); |
249
|
|
|
} else { |
250
|
63 |
|
$object = $this->invokeClass($abstract, $vars); |
251
|
|
|
} |
252
|
|
|
|
253
|
72 |
|
if (!$newInstance) { |
254
|
72 |
|
$this->instances[$abstract] = $object; |
255
|
|
|
} |
256
|
|
|
|
257
|
72 |
|
return $object; |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
/** |
261
|
|
|
* 删除容器中的对象实例 |
262
|
|
|
* @access public |
263
|
|
|
* @param string $name 类名或者标识 |
264
|
|
|
* @return void |
265
|
|
|
*/ |
266
|
6 |
|
public function delete($name) |
267
|
|
|
{ |
268
|
6 |
|
$name = $this->getAlias($name); |
269
|
|
|
|
270
|
6 |
|
if (isset($this->instances[$name])) { |
271
|
6 |
|
unset($this->instances[$name]); |
272
|
|
|
} |
273
|
6 |
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* 执行函数或者闭包方法 支持参数调用 |
277
|
|
|
* @access public |
278
|
|
|
* @param string|Closure $function 函数或者闭包 |
279
|
|
|
* @param array $vars 参数 |
280
|
|
|
* @return mixed |
281
|
|
|
*/ |
282
|
30 |
|
public function invokeFunction($function, array $vars = []) |
283
|
|
|
{ |
284
|
|
|
try { |
285
|
30 |
|
$reflect = new ReflectionFunction($function); |
286
|
3 |
|
} catch (ReflectionException $e) { |
287
|
3 |
|
throw new FuncNotFoundException("function not exists: {$function}()", $function, $e); |
|
|
|
|
288
|
|
|
} |
289
|
|
|
|
290
|
27 |
|
$args = $this->bindParams($reflect, $vars); |
291
|
|
|
|
292
|
27 |
|
return $function(...$args); |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
/** |
296
|
|
|
* 调用反射执行类的方法 支持参数绑定 |
297
|
|
|
* @access public |
298
|
|
|
* @param mixed $method 方法 |
299
|
|
|
* @param array $vars 参数 |
300
|
|
|
* @param bool $accessible 设置是否可访问 |
301
|
|
|
* @return mixed |
302
|
|
|
*/ |
303
|
18 |
|
public function invokeMethod($method, array $vars = [], bool $accessible = false) |
304
|
|
|
{ |
305
|
18 |
|
if (is_array($method)) { |
306
|
18 |
|
[$class, $method] = $method; |
|
|
|
|
307
|
|
|
|
308
|
18 |
|
$class = is_object($class) ? $class : $this->invokeClass($class); |
|
|
|
|
309
|
|
|
} else { |
310
|
|
|
// 静态方法 |
311
|
3 |
|
[$class, $method] = explode('::', $method); |
|
|
|
|
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
try { |
315
|
18 |
|
$reflect = new ReflectionMethod($class, $method); |
|
|
|
|
316
|
3 |
|
} catch (ReflectionException $e) { |
317
|
3 |
|
$class = is_object($class) ? get_class($class) : $class; |
318
|
3 |
|
throw new FuncNotFoundException('method not exists: ' . $class . '::' . $method . '()', "{$class}::{$method}", $e); |
319
|
|
|
} |
320
|
|
|
|
321
|
15 |
|
$args = $this->bindParams($reflect, $vars); |
322
|
|
|
|
323
|
15 |
|
if ($accessible) { |
324
|
3 |
|
$reflect->setAccessible($accessible); |
325
|
|
|
} |
326
|
|
|
|
327
|
15 |
|
return $reflect->invokeArgs(is_object($class) ? $class : null, $args); |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
/** |
331
|
|
|
* 调用反射执行类的方法 支持参数绑定 |
332
|
|
|
* @access public |
333
|
|
|
* @param object $instance 对象实例 |
334
|
|
|
* @param mixed $reflect 反射类 |
335
|
|
|
* @param array $vars 参数 |
336
|
|
|
* @return mixed |
337
|
|
|
*/ |
338
|
12 |
|
public function invokeReflectMethod($instance, $reflect, array $vars = []) |
339
|
|
|
{ |
340
|
12 |
|
$args = $this->bindParams($reflect, $vars); |
341
|
|
|
|
342
|
12 |
|
return $reflect->invokeArgs($instance, $args); |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
/** |
346
|
|
|
* 调用反射执行callable 支持参数绑定 |
347
|
|
|
* @access public |
348
|
|
|
* @param mixed $callable |
349
|
|
|
* @param array $vars 参数 |
350
|
|
|
* @param bool $accessible 设置是否可访问 |
351
|
|
|
* @return mixed |
352
|
|
|
*/ |
353
|
24 |
|
public function invoke($callable, array $vars = [], bool $accessible = false) |
354
|
|
|
{ |
355
|
24 |
|
if ($callable instanceof Closure) { |
356
|
15 |
|
return $this->invokeFunction($callable, $vars); |
357
|
12 |
|
} elseif (is_string($callable) && false === strpos($callable, '::')) { |
358
|
3 |
|
return $this->invokeFunction($callable, $vars); |
359
|
|
|
} else { |
360
|
12 |
|
return $this->invokeMethod($callable, $vars, $accessible); |
361
|
|
|
} |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
/** |
365
|
|
|
* 调用反射执行类的实例化 支持依赖注入 |
366
|
|
|
* @access public |
367
|
|
|
* @param string $class 类名 |
368
|
|
|
* @param array $vars 参数 |
369
|
|
|
* @return mixed |
370
|
|
|
*/ |
371
|
105 |
|
public function invokeClass(string $class, array $vars = []) |
372
|
|
|
{ |
373
|
|
|
try { |
374
|
105 |
|
$reflect = new ReflectionClass($class); |
375
|
6 |
|
} catch (ReflectionException $e) { |
376
|
6 |
|
throw new ClassNotFoundException('class not exists: ' . $class, $class, $e); |
377
|
|
|
} |
378
|
|
|
|
379
|
102 |
|
if ($reflect->hasMethod('__make')) { |
380
|
33 |
|
$method = $reflect->getMethod('__make'); |
381
|
33 |
|
if ($method->isPublic() && $method->isStatic()) { |
382
|
33 |
|
$args = $this->bindParams($method, $vars); |
383
|
33 |
|
return $method->invokeArgs(null, $args); |
384
|
|
|
} |
385
|
|
|
} |
386
|
|
|
|
387
|
90 |
|
$constructor = $reflect->getConstructor(); |
388
|
|
|
|
389
|
90 |
|
$args = $constructor ? $this->bindParams($constructor, $vars) : []; |
390
|
|
|
|
391
|
90 |
|
$object = $reflect->newInstanceArgs($args); |
392
|
|
|
|
393
|
90 |
|
$this->invokeAfter($class, $object); |
394
|
|
|
|
395
|
90 |
|
return $object; |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
/** |
399
|
|
|
* 执行invokeClass回调 |
400
|
|
|
* @access protected |
401
|
|
|
* @param string $class 对象类名 |
402
|
|
|
* @param object $object 容器对象实例 |
403
|
|
|
* @return void |
404
|
|
|
*/ |
405
|
90 |
|
protected function invokeAfter(string $class, $object): void |
406
|
|
|
{ |
407
|
90 |
|
if (isset($this->invokeCallback['*'])) { |
408
|
3 |
|
foreach ($this->invokeCallback['*'] as $callback) { |
409
|
3 |
|
$callback($object, $this); |
410
|
|
|
} |
411
|
|
|
} |
412
|
|
|
|
413
|
90 |
|
if (isset($this->invokeCallback[$class])) { |
414
|
3 |
|
foreach ($this->invokeCallback[$class] as $callback) { |
415
|
3 |
|
$callback($object, $this); |
416
|
|
|
} |
417
|
|
|
} |
418
|
90 |
|
} |
419
|
|
|
|
420
|
|
|
/** |
421
|
|
|
* 绑定参数 |
422
|
|
|
* @access protected |
423
|
|
|
* @param ReflectionFunctionAbstract $reflect 反射类 |
424
|
|
|
* @param array $vars 参数 |
425
|
|
|
* @return array |
426
|
|
|
*/ |
427
|
114 |
|
protected function bindParams(ReflectionFunctionAbstract $reflect, array $vars = []): array |
428
|
|
|
{ |
429
|
114 |
|
if ($reflect->getNumberOfParameters() == 0) { |
430
|
66 |
|
return []; |
431
|
|
|
} |
432
|
|
|
|
433
|
|
|
// 判断数组类型 数字数组时按顺序绑定参数 |
434
|
66 |
|
reset($vars); |
435
|
66 |
|
$type = key($vars) === 0 ? 1 : 0; |
436
|
66 |
|
$params = $reflect->getParameters(); |
437
|
66 |
|
$args = []; |
438
|
|
|
|
439
|
66 |
|
foreach ($params as $param) { |
440
|
66 |
|
$name = $param->getName(); |
|
|
|
|
441
|
66 |
|
$lowerName = Str::snake($name); |
442
|
66 |
|
$class = $param->getClass(); |
443
|
|
|
|
444
|
66 |
|
if ($class) { |
445
|
60 |
|
$args[] = $this->getObjectParam($class->getName(), $vars); |
|
|
|
|
446
|
57 |
|
} elseif (1 == $type && !empty($vars)) { |
447
|
51 |
|
$args[] = array_shift($vars); |
448
|
9 |
|
} elseif (0 == $type && isset($vars[$name])) { |
449
|
3 |
|
$args[] = $vars[$name]; |
450
|
9 |
|
} elseif (0 == $type && isset($vars[$lowerName])) { |
451
|
3 |
|
$args[] = $vars[$lowerName]; |
452
|
9 |
|
} elseif ($param->isDefaultValueAvailable()) { |
453
|
9 |
|
$args[] = $param->getDefaultValue(); |
454
|
|
|
} else { |
455
|
22 |
|
throw new InvalidArgumentException('method param miss:' . $name); |
456
|
|
|
} |
457
|
|
|
} |
458
|
|
|
|
459
|
66 |
|
return $args; |
460
|
|
|
} |
461
|
|
|
|
462
|
|
|
/** |
463
|
|
|
* 创建工厂对象实例 |
464
|
|
|
* @param string $name 工厂类名 |
465
|
|
|
* @param string $namespace 默认命名空间 |
466
|
|
|
* @param array $args |
467
|
|
|
* @return mixed |
468
|
|
|
* @deprecated |
469
|
|
|
* @access public |
470
|
|
|
*/ |
471
|
3 |
|
public static function factory(string $name, string $namespace = '', ...$args) |
472
|
|
|
{ |
473
|
3 |
|
$class = false !== strpos($name, '\\') ? $name : $namespace . ucwords($name); |
474
|
|
|
|
475
|
3 |
|
return Container::getInstance()->invokeClass($class, $args); |
476
|
|
|
} |
477
|
|
|
|
478
|
|
|
/** |
479
|
|
|
* 获取对象类型的参数值 |
480
|
|
|
* @access protected |
481
|
|
|
* @param string $className 类名 |
482
|
|
|
* @param array $vars 参数 |
483
|
|
|
* @return mixed |
484
|
|
|
*/ |
485
|
60 |
|
protected function getObjectParam(string $className, array &$vars) |
486
|
|
|
{ |
487
|
60 |
|
$array = $vars; |
488
|
60 |
|
$value = array_shift($array); |
489
|
|
|
|
490
|
60 |
|
if ($value instanceof $className) { |
491
|
3 |
|
$result = $value; |
492
|
3 |
|
array_shift($vars); |
493
|
|
|
} else { |
494
|
60 |
|
$result = $this->make($className); |
495
|
|
|
} |
496
|
|
|
|
497
|
60 |
|
return $result; |
498
|
|
|
} |
499
|
|
|
|
500
|
3 |
|
public function __set($name, $value) |
501
|
|
|
{ |
502
|
3 |
|
$this->bind($name, $value); |
503
|
3 |
|
} |
504
|
|
|
|
505
|
87 |
|
public function __get($name) |
506
|
|
|
{ |
507
|
87 |
|
return $this->get($name); |
508
|
|
|
} |
509
|
|
|
|
510
|
3 |
|
public function __isset($name): bool |
511
|
|
|
{ |
512
|
3 |
|
return $this->exists($name); |
513
|
|
|
} |
514
|
|
|
|
515
|
6 |
|
public function __unset($name) |
516
|
|
|
{ |
517
|
6 |
|
$this->delete($name); |
518
|
6 |
|
} |
519
|
|
|
|
520
|
3 |
|
public function offsetExists($key) |
521
|
|
|
{ |
522
|
3 |
|
return $this->exists($key); |
523
|
|
|
} |
524
|
|
|
|
525
|
3 |
|
public function offsetGet($key) |
526
|
|
|
{ |
527
|
3 |
|
return $this->make($key); |
528
|
|
|
} |
529
|
|
|
|
530
|
3 |
|
public function offsetSet($key, $value) |
531
|
|
|
{ |
532
|
3 |
|
$this->bind($key, $value); |
533
|
3 |
|
} |
534
|
|
|
|
535
|
3 |
|
public function offsetUnset($key) |
536
|
|
|
{ |
537
|
3 |
|
$this->delete($key); |
538
|
3 |
|
} |
539
|
|
|
|
540
|
|
|
//Countable |
541
|
|
|
public function count() |
542
|
|
|
{ |
543
|
|
|
return count($this->instances); |
544
|
|
|
} |
545
|
|
|
|
546
|
|
|
//IteratorAggregate |
547
|
3 |
|
public function getIterator() |
548
|
|
|
{ |
549
|
3 |
|
return new ArrayIterator($this->instances); |
550
|
|
|
} |
551
|
|
|
} |
552
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.