Completed
Push — 6.0 ( 59914a...709e53 )
by liu
02:42
created

Container::getIterator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
// +----------------------------------------------------------------------
1 ignored issue
show
Coding Style introduced by
You must use "/**" style comments for a file comment
Loading history...
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 ReflectionMethod;
26
use think\exception\ClassNotFoundException;
27
28
/**
29
 * Class Container
30
 * @package think
0 ignored issues
show
Coding Style introduced by
Package name "think" is not valid; consider "Think" instead
Loading history...
31
 *
32
 * @property Route      $route
33
 * @property Config     $config
34
 * @property Cache      $cache
35
 * @property Request    $request
36
 * @property Http       $http
37
 * @property Console    $console
38
 * @property Env        $env
39
 * @property Event      $event
40
 * @property Middleware $middleware
41
 * @property Log        $log
42
 * @property Lang       $lang
43
 * @property Db         $db
44
 * @property Cookie     $cookie
45
 * @property Session    $session
46
 * @property Validate   $validate
47
 */
4 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @author tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
48
class Container implements ContainerInterface, ArrayAccess, IteratorAggregate, Countable
49
{
50
    /**
51
     * 容器对象实例
52
     * @var Container|Closure
53
     */
54
    protected static $instance;
55
56
    /**
57
     * 容器中的对象实例
58
     * @var array
59
     */
60
    protected $instances = [];
61
62
    /**
63
     * 容器绑定标识
64
     * @var array
65
     */
66
    protected $bind = [
67
        'app'                     => App::class,
68
        'cache'                   => Cache::class,
69
        'config'                  => Config::class,
70
        'console'                 => Console::class,
71
        'cookie'                  => Cookie::class,
72
        'db'                      => Db::class,
73
        'env'                     => Env::class,
74
        'event'                   => Event::class,
75
        'http'                    => Http::class,
76
        'lang'                    => Lang::class,
77
        'log'                     => Log::class,
78
        'middleware'              => Middleware::class,
79
        'request'                 => Request::class,
80
        'response'                => Response::class,
81
        'route'                   => Route::class,
82
        'session'                 => Session::class,
83
        'validate'                => Validate::class,
84
        'view'                    => View::class,
85
86
        // 接口依赖注入
87
        'Psr\Log\LoggerInterface' => Log::class,
88
    ];
89
90
    /**
91
     * 获取当前容器的实例(单例)
92
     * @access public
93
     * @return static
94
     */
95 3
    public static function getInstance()
96
    {
97 3
        if (is_null(static::$instance)) {
98 1
            static::$instance = new static;
99
        }
100
101 3
        if (static::$instance instanceof Closure) {
102 1
            return (static::$instance)();
103
        }
104
105 3
        return static::$instance;
106
    }
107
108
    /**
109
     * 设置当前容器的实例
110
     * @access public
111
     * @param object|Closure $instance
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
112
     * @return void
113
     */
114 29
    public static function setInstance($instance): void
115
    {
116 29
        static::$instance = $instance;
117 29
    }
118
119
    /**
120
     * 获取容器中的对象实例 不存在则创建
121
     * @access public
122
     * @param string     $abstract    类名或者标识
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
123
     * @param array|true $vars        变量
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
124
     * @param bool       $newInstance 是否每次创建新的实例
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
125
     * @return object
126
     */
127 1
    public static function pull(string $abstract, array $vars = [], bool $newInstance = false)
128
    {
129 1
        return static::getInstance()->make($abstract, $vars, $newInstance);
130
    }
131
132
    /**
133
     * 获取容器中的对象实例
134
     * @access public
135
     * @param string $abstract 类名或者标识
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
136
     * @return object
137
     */
138 12
    public function get($abstract)
139
    {
140 12
        if ($this->has($abstract)) {
141 11
            return $this->make($abstract);
142
        }
143
144 1
        throw new ClassNotFoundException('class not exists: ' . $abstract, $abstract);
145
    }
146
147
    /**
148
     * 绑定一个类、闭包、实例、接口实现到容器
149
     * @access public
150
     * @param string|array $abstract 类标识、接口
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
151
     * @param mixed        $concrete 要绑定的类、闭包或者实例
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
152
     * @return $this
153
     */
154 9
    public function bind($abstract, $concrete = null)
155
    {
156 9
        if (is_array($abstract)) {
157 4
            $this->bind = array_merge($this->bind, $abstract);
158 6
        } elseif ($concrete instanceof Closure) {
159 3
            $this->bind[$abstract] = $concrete;
160 4
        } elseif (is_object($concrete)) {
161 3
            $this->instance($abstract, $concrete);
162
        } else {
163 2
            $this->bind[$abstract] = $concrete;
164
        }
165
166 9
        return $this;
167
    }
168
169
    /**
170
     * 绑定一个类实例到容器
171
     * @access public
172
     * @param string $abstract 类名或者标识
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
173
     * @param object $instance 类的实例
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
174
     * @return $this
175
     */
176 22
    public function instance(string $abstract, $instance)
177
    {
178 22
        if (isset($this->bind[$abstract])) {
179 19
            $bind = $this->bind[$abstract];
180
181 19
            if (is_string($bind)) {
182 19
                return $this->instance($bind, $instance);
183
            }
184
        }
185
186 22
        $this->instances[$abstract] = $instance;
187
188 22
        return $this;
189
    }
190
191
    /**
192
     * 判断容器中是否存在类及标识
193
     * @access public
194
     * @param string $abstract 类名或者标识
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
195
     * @return bool
196
     */
197 12
    public function bound(string $abstract): bool
198
    {
199 12
        return isset($this->bind[$abstract]) || isset($this->instances[$abstract]);
200
    }
201
202
    /**
203
     * 判断容器中是否存在类及标识
204
     * @access public
205
     * @param string $name 类名或者标识
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
206
     * @return bool
207
     */
208 12
    public function has($name): bool
209
    {
210 12
        return $this->bound($name);
211
    }
212
213
    /**
214
     * 判断容器中是否存在对象实例
215
     * @access public
216
     * @param string $abstract 类名或者标识
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
217
     * @return bool
218
     */
219 4
    public function exists(string $abstract): bool
220
    {
221 4
        if (isset($this->bind[$abstract])) {
222 3
            $bind = $this->bind[$abstract];
223
224 3
            if (is_string($bind)) {
225 2
                return $this->exists($bind);
226
            }
227
        }
228
229 4
        return isset($this->instances[$abstract]);
230
    }
231
232
    /**
233
     * 创建类的实例 已经存在则直接获取
234
     * @access public
235
     * @param string $abstract    类名或者标识
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
236
     * @param array  $vars        变量
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
237
     * @param bool   $newInstance 是否每次创建新的实例
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
238
     * @return mixed
239
     */
240 16
    public function make(string $abstract, array $vars = [], bool $newInstance = false)
241
    {
242 16
        if (isset($this->instances[$abstract]) && !$newInstance) {
243 12
            return $this->instances[$abstract];
244
        }
245
246 15
        if (isset($this->bind[$abstract])) {
247 13
            $concrete = $this->bind[$abstract];
248
249 13
            if ($concrete instanceof Closure) {
250 3
                $object = $this->invokeFunction($concrete, $vars);
251
            } else {
252 13
                return $this->make($concrete, $vars, $newInstance);
253
            }
254
        } else {
255 12
            $object = $this->invokeClass($abstract, $vars);
256
        }
257
258 15
        if (!$newInstance) {
259 15
            $this->instances[$abstract] = $object;
260
        }
261
262 15
        return $object;
263
    }
264
265
    /**
266
     * 删除容器中的对象实例
267
     * @access public
268
     * @param string $name 类名或者标识
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
269
     * @return void
270
     */
271 2
    public function delete($name)
272
    {
273 2
        if (isset($this->bind[$name])) {
274 2
            $bind = $this->bind[$name];
275
276 2
            if (is_string($bind)) {
277 2
                return $this->delete($bind);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->delete($bind) targeting think\Container::delete() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
278
            }
279
        }
280
281 2
        if (isset($this->instances[$name])) {
282 2
            unset($this->instances[$name]);
283
        }
284 2
    }
285
286
    /**
287
     * 执行函数或者闭包方法 支持参数调用
288
     * @access public
289
     * @param mixed $function 函数或者闭包
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
290
     * @param array $vars     参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
291
     * @return mixed
292
     */
293 5
    public function invokeFunction($function, array $vars = [])
294
    {
295
        try {
296 5
            $reflect = new ReflectionFunction($function);
297
298 4
            $args = $this->bindParams($reflect, $vars);
299
300 4
            return call_user_func_array($function, $args);
301 1
        } catch (ReflectionException $e) {
302 1
            throw new Exception('function not exists: ' . $function . '()');
303
        }
304
    }
305
306
    /**
307
     * 调用反射执行类的方法 支持参数绑定
308
     * @access public
309
     * @param mixed $method 方法
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
310
     * @param array $vars   参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
311
     * @return mixed
312
     */
313 3
    public function invokeMethod($method, array $vars = [])
314
    {
315
        try {
316 3
            if (is_array($method)) {
317 3
                $class   = is_object($method[0]) ? $method[0] : $this->invokeClass($method[0]);
318 3
                $reflect = new ReflectionMethod($class, $method[1]);
319
            } else {
320
                // 静态方法
321 1
                $reflect = new ReflectionMethod($method);
0 ignored issues
show
Bug introduced by
The call to ReflectionMethod::__construct() has too few arguments starting with name. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

321
                $reflect = /** @scrutinizer ignore-call */ new ReflectionMethod($method);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
322
            }
323
324 2
            $args = $this->bindParams($reflect, $vars);
325
326 2
            return $reflect->invokeArgs($class ?? null, $args);
327 1
        } catch (ReflectionException $e) {
328 1
            if (is_array($method)) {
329 1
                $class    = is_object($method[0]) ? get_class($method[0]) : $method[0];
0 ignored issues
show
introduced by
The condition is_object($method[0]) is always false.
Loading history...
330 1
                $callback = $class . '::' . $method[1];
331
            } else {
332
                $callback = $method;
333
            }
334
335 1
            throw new Exception('method not exists: ' . $callback . '()');
336
        }
337
    }
338
339
    /**
340
     * 调用反射执行类的方法 支持参数绑定
341
     * @access public
342
     * @param object $instance 对象实例
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
343
     * @param mixed  $reflect  反射类
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
344
     * @param array  $vars     参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
345
     * @return mixed
346
     */
347 1
    public function invokeReflectMethod($instance, $reflect, array $vars = [])
348
    {
349 1
        $args = $this->bindParams($reflect, $vars);
350
351 1
        return $reflect->invokeArgs($instance, $args);
352
    }
353
354
    /**
355
     * 调用反射执行callable 支持参数绑定
356
     * @access public
357
     * @param mixed $callable
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
358
     * @param array $vars 参数
1 ignored issue
show
Coding Style introduced by
Expected 5 spaces after parameter name; 1 found
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
359
     * @return mixed
360
     */
361 2
    public function invoke($callable, array $vars = [])
362
    {
363 2
        if ($callable instanceof Closure) {
364 1
            return $this->invokeFunction($callable, $vars);
365
        }
366
367 2
        return $this->invokeMethod($callable, $vars);
368
    }
369
370
    /**
371
     * 调用反射执行类的实例化 支持依赖注入
372
     * @access public
373
     * @param string $class 类名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
374
     * @param array  $vars  参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
375
     * @return mixed
376
     */
377 15
    public function invokeClass(string $class, array $vars = [])
378
    {
379
        try {
380 15
            $reflect = new ReflectionClass($class);
381
382 14
            if ($reflect->hasMethod('__make')) {
383 11
                $method = new ReflectionMethod($class, '__make');
384
385 11
                if ($method->isPublic() && $method->isStatic()) {
386 11
                    $args = $this->bindParams($method, $vars);
387 11
                    return $method->invokeArgs(null, $args);
388
                }
389
            }
390
391 11
            $constructor = $reflect->getConstructor();
392
393 11
            $args = $constructor ? $this->bindParams($constructor, $vars) : [];
0 ignored issues
show
introduced by
$constructor is of type ReflectionMethod, thus it always evaluated to true.
Loading history...
394
395 11
            return $reflect->newInstanceArgs($args);
396 1
        } catch (ReflectionException $e) {
397 1
            throw new ClassNotFoundException('class not exists: ' . $class, $class);
398
        }
399
    }
400
401
    /**
402
     * 绑定参数
403
     * @access protected
404
     * @param \ReflectionMethod|\ReflectionFunction $reflect 反射类
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
405
     * @param array                                 $vars    参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
406
     * @return array
407
     */
408 17
    protected function bindParams($reflect, array $vars = []): array
409
    {
410 17
        if ($reflect->getNumberOfParameters() == 0) {
411 6
            return [];
412
        }
413
414
        // 判断数组类型 数字数组时按顺序绑定参数
415 11
        reset($vars);
416 11
        $type   = key($vars) === 0 ? 1 : 0;
417 11
        $params = $reflect->getParameters();
418 11
        $args   = [];
419
420 11
        foreach ($params as $param) {
421 11
            $name      = $param->getName();
422 11
            $lowerName = App::parseName($name);
423 11
            $class     = $param->getClass();
424
425 11
            if ($class) {
426 11
                $args[] = $this->getObjectParam($class->getName(), $vars);
427 9
            } elseif (1 == $type && !empty($vars)) {
428 1
                $args[] = array_shift($vars);
429 9
            } elseif (0 == $type && isset($vars[$name])) {
430 1
                $args[] = $vars[$name];
431 9
            } elseif (0 == $type && isset($vars[$lowerName])) {
432 1
                $args[] = $vars[$lowerName];
433 9
            } elseif ($param->isDefaultValueAvailable()) {
434 9
                $args[] = $param->getDefaultValue();
435
            } else {
436 11
                throw new InvalidArgumentException('method param miss:' . $name);
437
            }
438
        }
439
440 11
        return $args;
441
    }
442
443
    /**
444
     * 获取对象类型的参数值
445
     * @access protected
446
     * @param string $className 类名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
447
     * @param array  $vars      参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
448
     * @return mixed
449
     */
450 11
    protected function getObjectParam(string $className, array &$vars)
451
    {
452 11
        $array = $vars;
453 11
        $value = array_shift($array);
454
455 11
        if ($value instanceof $className) {
456 1
            $result = $value;
457 1
            array_shift($vars);
458
        } else {
459 11
            $result = $this->make($className);
460
        }
461
462 11
        return $result;
463
    }
464
465 1
    public function __set($name, $value)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __set()
Loading history...
466
    {
467 1
        $this->bind($name, $value);
468 1
    }
469
470 11
    public function __get($name)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __get()
Loading history...
471
    {
472 11
        return $this->get($name);
473
    }
474
475 1
    public function __isset($name): bool
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __isset()
Loading history...
476
    {
477 1
        return $this->exists($name);
478
    }
479
480 2
    public function __unset($name)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __unset()
Loading history...
481
    {
482 2
        $this->delete($name);
483 2
    }
484
485 1
    public function offsetExists($key)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function offsetExists()
Loading history...
486
    {
487 1
        return $this->exists($key);
488
    }
489
490 1
    public function offsetGet($key)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function offsetGet()
Loading history...
491
    {
492 1
        return $this->make($key);
493
    }
494
495 1
    public function offsetSet($key, $value)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function offsetSet()
Loading history...
496
    {
497 1
        $this->bind($key, $value);
498 1
    }
499
500 1
    public function offsetUnset($key)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function offsetUnset()
Loading history...
501
    {
502 1
        $this->delete($key);
503 1
    }
504
505
    //Countable
506
    public function count()
0 ignored issues
show
Coding Style introduced by
You must use "/**" style comments for a function comment
Loading history...
507
    {
508
        return count($this->instances);
509
    }
510
511
    //IteratorAggregate
512 1
    public function getIterator()
0 ignored issues
show
Coding Style introduced by
You must use "/**" style comments for a function comment
Loading history...
513
    {
514 1
        return new ArrayIterator($this->instances);
515
    }
516
}
517