Completed
Push — 6.0 ( 2febf3...595f09 )
by liu
03:31
created

Container::get()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 2
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 Debug      $debug
40
 * @property Event      $event
41
 * @property Middleware $middleware
42
 * @property Log        $log
43
 * @property Lang       $lang
44
 * @property Db         $db
45
 * @property Cookie     $cookie
46
 * @property Session    $session
47
 * @property Validate   $validate
48
 */
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...
49
class Container implements ContainerInterface, ArrayAccess, IteratorAggregate, Countable
50
{
51
    /**
52
     * 容器对象实例
53
     * @var Container|Closure
54
     */
55
    protected static $instance;
56
57
    /**
58
     * 容器中的对象实例
59
     * @var array
60
     */
61
    protected $instances = [];
62
63
    /**
64
     * 容器绑定标识
65
     * @var array
66
     */
67
    protected $bind = [
68
        'app'                     => App::class,
69
        'cache'                   => Cache::class,
70
        'config'                  => Config::class,
71
        'console'                 => Console::class,
72
        'cookie'                  => Cookie::class,
73
        'db'                      => Db::class,
74
        'debug'                   => Debug::class,
75
        'env'                     => Env::class,
76
        'event'                   => Event::class,
77
        'http'                    => Http::class,
78
        'lang'                    => Lang::class,
79
        'log'                     => Log::class,
80
        'middleware'              => Middleware::class,
81
        'request'                 => Request::class,
82
        'response'                => Response::class,
83
        'route'                   => Route::class,
84
        'session'                 => Session::class,
85
        'validate'                => Validate::class,
86
        'view'                    => View::class,
87
88
        // 接口依赖注入
89
        'Psr\Log\LoggerInterface' => Log::class,
90
    ];
91
92
    /**
93
     * 容器标识别名
94
     * @var array
95
     */
96
    protected $alias = [];
97
98
    /**
99
     * 获取当前容器的实例(单例)
100
     * @access public
101
     * @return static
102
     */
103 3
    public static function getInstance()
104
    {
105 3
        if (is_null(static::$instance)) {
106 1
            static::$instance = new static;
107
        }
108
109 3
        if (static::$instance instanceof Closure) {
110 1
            return (static::$instance)();
111
        }
112
113 3
        return static::$instance;
114
    }
115
116
    /**
117
     * 设置当前容器的实例
118
     * @access public
119
     * @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...
120
     * @return void
121
     */
122 29
    public static function setInstance($instance): void
123
    {
124 29
        static::$instance = $instance;
125 29
    }
126
127
    /**
128
     * 获取容器中的对象实例 不存在则创建
129
     * @access public
130
     * @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...
131
     * @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...
132
     * @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...
133
     * @return object
134
     */
135 1
    public static function pull(string $abstract, array $vars = [], bool $newInstance = false)
136
    {
137 1
        return static::getInstance()->make($abstract, $vars, $newInstance);
138
    }
139
140
    /**
141
     * 获取容器中的对象实例
142
     * @access public
143
     * @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...
144
     * @return object
145
     */
146 12
    public function get($abstract)
147
    {
148 12
        if ($this->has($abstract)) {
149 11
            return $this->make($abstract);
150
        }
151
152 1
        throw new ClassNotFoundException('class not exists: ' . $abstract, $abstract);
153
    }
154
155
    /**
156
     * 绑定一个类、闭包、实例、接口实现到容器
157
     * @access public
158
     * @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...
159
     * @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...
160
     * @return $this
161
     */
162 9
    public function bind($abstract, $concrete = null)
163
    {
164 9
        if (is_array($abstract)) {
165 4
            $this->bind = array_merge($this->bind, $abstract);
166 6
        } elseif ($concrete instanceof Closure) {
167 3
            $this->bind[$abstract] = $concrete;
168 4
        } elseif (is_object($concrete)) {
169 3
            $this->instance($abstract, $concrete);
170
        } else {
171 2
            $this->bind[$abstract] = $concrete;
172
        }
173
174 9
        return $this;
175
    }
176
177
    /**
178
     * 绑定一个类实例到容器
179
     * @access public
180
     * @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...
181
     * @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...
182
     * @return $this
183
     */
184 22
    public function instance(string $abstract, $instance)
185
    {
186 22
        if (isset($this->bind[$abstract])) {
187 19
            $bind = $this->bind[$abstract];
188
189 19
            if (is_string($bind)) {
190 19
                $this->instances[$bind] = $instance;
191 19
                return $this;
192
            }
193
        }
194
195 21
        $this->instances[$abstract] = $instance;
196
197 21
        return $this;
198
    }
199
200
    /**
201
     * 判断容器中是否存在类及标识
202
     * @access public
203
     * @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...
204
     * @return bool
205
     */
206 12
    public function bound(string $abstract): bool
207
    {
208 12
        return isset($this->bind[$abstract]) || isset($this->instances[$abstract]);
209
    }
210
211
    /**
212
     * 判断容器中是否存在类及标识
213
     * @access public
214
     * @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...
215
     * @return bool
216
     */
217 12
    public function has($name): bool
218
    {
219 12
        return $this->bound($name);
220
    }
221
222
    /**
223
     * 判断容器中是否存在对象实例
224
     * @access public
225
     * @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...
226
     * @return bool
227
     */
228 4
    public function exists(string $abstract): bool
229
    {
230 4
        if (isset($this->bind[$abstract])) {
231 3
            $bind = $this->bind[$abstract];
232
233 3
            if (is_string($bind)) {
234 2
                return isset($this->instances[$bind]);
235
            }
236
        }
237
238 4
        return isset($this->instances[$abstract]);
239
    }
240
241
    /**
242
     * 创建类的实例 已经存在则直接获取
243
     * @access public
244
     * @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...
245
     * @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...
246
     * @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...
247
     * @return mixed
248
     */
249 16
    public function make(string $abstract, array $vars = [], bool $newInstance = false)
250
    {
251 16
        $abstract = $this->alias[$abstract] ?? $abstract;
252
253 16
        if (isset($this->instances[$abstract]) && !$newInstance) {
254 12
            return $this->instances[$abstract];
255
        }
256
257 15
        if (isset($this->bind[$abstract])) {
258 13
            $concrete = $this->bind[$abstract];
259
260 13
            if ($concrete instanceof Closure) {
261 3
                $object = $this->invokeFunction($concrete, $vars);
262
            } else {
263 10
                $this->alias[$abstract] = $concrete;
264 13
                return $this->make($concrete, $vars, $newInstance);
265
            }
266
        } else {
267 12
            $object = $this->invokeClass($abstract, $vars);
268
        }
269
270 15
        if (!$newInstance) {
271 15
            $this->instances[$abstract] = $object;
272
        }
273
274 15
        return $object;
275
    }
276
277
    /**
278
     * 删除容器中的对象实例
279
     * @access public
280
     * @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...
281
     * @return void
282
     */
283 2
    public function delete($abstract): void
284
    {
285 2
        foreach ((array) $abstract as $name) {
286 2
            $name = $this->alias[$name] ?? $name;
287
288 2
            if (isset($this->bind[$name])) {
289 1
                $name = $this->bind[$name];
290
            }
291
292 2
            if (isset($this->instances[$name])) {
293 2
                unset($this->instances[$name]);
294
            }
295
        }
296 2
    }
297
298
    /**
299
     * 获取容器中的对象实例
300
     * @access public
301
     * @return array
302
     */
303 1
    public function all()
304
    {
305 1
        return $this->instances;
306
    }
307
308
    /**
309
     * 清除容器中的对象实例
310
     * @access public
311
     * @return void
312
     */
313 1
    public function flush(): void
314
    {
315 1
        $this->instances = [];
316 1
        $this->bind      = [];
317 1
        $this->alias     = [];
318 1
    }
319
320
    /**
321
     * 执行函数或者闭包方法 支持参数调用
322
     * @access public
323
     * @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...
324
     * @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...
325
     * @return mixed
326
     */
327 5
    public function invokeFunction($function, array $vars = [])
328
    {
329
        try {
330 5
            $reflect = new ReflectionFunction($function);
331
332 4
            $args = $this->bindParams($reflect, $vars);
333
334 4
            return call_user_func_array($function, $args);
335 1
        } catch (ReflectionException $e) {
336 1
            throw new Exception('function not exists: ' . $function . '()');
337
        }
338
    }
339
340
    /**
341
     * 调用反射执行类的方法 支持参数绑定
342
     * @access public
343
     * @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...
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 3
    public function invokeMethod($method, array $vars = [])
348
    {
349
        try {
350 3
            if (is_array($method)) {
351 3
                $class   = is_object($method[0]) ? $method[0] : $this->invokeClass($method[0]);
352 3
                $reflect = new ReflectionMethod($class, $method[1]);
353
            } else {
354
                // 静态方法
355 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

355
                $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...
356
            }
357
358 2
            $args = $this->bindParams($reflect, $vars);
359
360 2
            return $reflect->invokeArgs($class ?? null, $args);
361 1
        } catch (ReflectionException $e) {
362 1
            if (is_array($method)) {
363 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...
364 1
                $callback = $class . '::' . $method[1];
365
            } else {
366
                $callback = $method;
367
            }
368
369 1
            throw new Exception('method not exists: ' . $callback . '()');
370
        }
371
    }
372
373
    /**
374
     * 调用反射执行类的方法 支持参数绑定
375
     * @access public
376
     * @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...
377
     * @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...
378
     * @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...
379
     * @return mixed
380
     */
381 1
    public function invokeReflectMethod($instance, $reflect, array $vars = [])
382
    {
383 1
        $args = $this->bindParams($reflect, $vars);
384
385 1
        return $reflect->invokeArgs($instance, $args);
386
    }
387
388
    /**
389
     * 调用反射执行callable 支持参数绑定
390
     * @access public
391
     * @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...
392
     * @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...
393
     * @return mixed
394
     */
395 2
    public function invoke($callable, array $vars = [])
396
    {
397 2
        if ($callable instanceof Closure) {
398 1
            return $this->invokeFunction($callable, $vars);
399
        }
400
401 2
        return $this->invokeMethod($callable, $vars);
402
    }
403
404
    /**
405
     * 调用反射执行类的实例化 支持依赖注入
406
     * @access public
407
     * @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...
408
     * @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...
409
     * @return mixed
410
     */
411 15
    public function invokeClass(string $class, array $vars = [])
412
    {
413
        try {
414 15
            $reflect = new ReflectionClass($class);
415
416 14
            if ($reflect->hasMethod('__make')) {
417 11
                $method = new ReflectionMethod($class, '__make');
418
419 11
                if ($method->isPublic() && $method->isStatic()) {
420 11
                    $args = $this->bindParams($method, $vars);
421 11
                    return $method->invokeArgs(null, $args);
422
                }
423
            }
424
425 11
            $constructor = $reflect->getConstructor();
426
427 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...
428
429 11
            return $reflect->newInstanceArgs($args);
430 1
        } catch (ReflectionException $e) {
431 1
            throw new ClassNotFoundException('class not exists: ' . $class, $class);
432
        }
433
    }
434
435
    /**
436
     * 绑定参数
437
     * @access protected
438
     * @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...
439
     * @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...
440
     * @return array
441
     */
442 17
    protected function bindParams($reflect, array $vars = []): array
443
    {
444 17
        if ($reflect->getNumberOfParameters() == 0) {
445 6
            return [];
446
        }
447
448
        // 判断数组类型 数字数组时按顺序绑定参数
449 11
        reset($vars);
450 11
        $type   = key($vars) === 0 ? 1 : 0;
451 11
        $params = $reflect->getParameters();
452 11
        $args   = [];
453
454 11
        foreach ($params as $param) {
455 11
            $name      = $param->getName();
456 11
            $lowerName = App::parseName($name);
457 11
            $class     = $param->getClass();
458
459 11
            if ($class) {
460 11
                $args[] = $this->getObjectParam($class->getName(), $vars);
461 9
            } elseif (1 == $type && !empty($vars)) {
462 1
                $args[] = array_shift($vars);
463 9
            } elseif (0 == $type && isset($vars[$name])) {
464 1
                $args[] = $vars[$name];
465 9
            } elseif (0 == $type && isset($vars[$lowerName])) {
466 1
                $args[] = $vars[$lowerName];
467 9
            } elseif ($param->isDefaultValueAvailable()) {
468 9
                $args[] = $param->getDefaultValue();
469
            } else {
470 11
                throw new InvalidArgumentException('method param miss:' . $name);
471
            }
472
        }
473
474 11
        return $args;
475
    }
476
477
    /**
478
     * 获取对象类型的参数值
479
     * @access protected
480
     * @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...
481
     * @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...
482
     * @return mixed
483
     */
484 11
    protected function getObjectParam(string $className, array &$vars)
485
    {
486 11
        $array = $vars;
487 11
        $value = array_shift($array);
488
489 11
        if ($value instanceof $className) {
490 1
            $result = $value;
491 1
            array_shift($vars);
492
        } else {
493 11
            $result = $this->make($className);
494
        }
495
496 11
        return $result;
497
    }
498
499 1
    public function __set($name, $value)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __set()
Loading history...
500
    {
501 1
        $this->bind($name, $value);
502 1
    }
503
504 11
    public function __get($name)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __get()
Loading history...
505
    {
506 11
        return $this->get($name);
507
    }
508
509 1
    public function __isset($name): bool
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __isset()
Loading history...
510
    {
511 1
        return $this->exists($name);
512
    }
513
514 2
    public function __unset($name)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __unset()
Loading history...
515
    {
516 2
        $this->delete($name);
517 2
    }
518
519 1
    public function offsetExists($key)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function offsetExists()
Loading history...
520
    {
521 1
        return $this->exists($key);
522
    }
523
524 1
    public function offsetGet($key)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function offsetGet()
Loading history...
525
    {
526 1
        return $this->make($key);
527
    }
528
529 1
    public function offsetSet($key, $value)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function offsetSet()
Loading history...
530
    {
531 1
        $this->bind($key, $value);
532 1
    }
533
534 1
    public function offsetUnset($key)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function offsetUnset()
Loading history...
535
    {
536 1
        $this->delete($key);
537 1
    }
538
539
    //Countable
540 1
    public function count()
0 ignored issues
show
Coding Style introduced by
You must use "/**" style comments for a function comment
Loading history...
541
    {
542 1
        return count($this->instances);
543
    }
544
545
    //IteratorAggregate
546 1
    public function getIterator()
0 ignored issues
show
Coding Style introduced by
You must use "/**" style comments for a function comment
Loading history...
547
    {
548 1
        return new ArrayIterator($this->instances);
549
    }
550
}
551