Completed
Push — 6.0 ( 1a394a...8864c6 )
by liu
03:14
created

Container::__unset()   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 1
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 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
     * @access public
95
     * @return static
96
     */
97 3
    public static function getInstance()
98
    {
99 3
        if (is_null(static::$instance)) {
100 1
            static::$instance = new static;
101
        }
102
103 3
        if (static::$instance instanceof Closure) {
104 1
            return (static::$instance)();
105
        }
106
107 3
        return static::$instance;
108
    }
109
110
    /**
111
     * 设置当前容器的实例
112
     * @access public
113
     * @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...
114
     * @return void
115
     */
116 29
    public static function setInstance($instance): void
117
    {
118 29
        static::$instance = $instance;
119 29
    }
120
121
    /**
122
     * 获取容器中的对象实例 不存在则创建
123
     * @access public
124
     * @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...
125
     * @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...
126
     * @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...
127
     * @return object
128
     */
129 1
    public static function pull(string $abstract, array $vars = [], bool $newInstance = false)
130
    {
131 1
        return static::getInstance()->make($abstract, $vars, $newInstance);
132
    }
133
134
    /**
135
     * 获取容器中的对象实例
136
     * @access public
137
     * @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...
138
     * @return object
139
     */
140 12
    public function get($abstract)
141
    {
142 12
        if ($this->has($abstract)) {
143 11
            return $this->make($abstract);
144
        }
145
146 1
        throw new ClassNotFoundException('class not exists: ' . $abstract, $abstract);
147
    }
148
149
    /**
150
     * 绑定一个类、闭包、实例、接口实现到容器
151
     * @access public
152
     * @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...
153
     * @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...
154
     * @return $this
155
     */
156 9
    public function bind($abstract, $concrete = null)
157
    {
158 9
        if (is_array($abstract)) {
159 4
            $this->bind = array_merge($this->bind, $abstract);
160 6
        } elseif ($concrete instanceof Closure) {
161 3
            $this->bind[$abstract] = $concrete;
162 4
        } elseif (is_object($concrete)) {
163 3
            $this->instance($abstract, $concrete);
164
        } else {
165 2
            $this->bind[$abstract] = $concrete;
166
        }
167
168 9
        return $this;
169
    }
170
171
    /**
172
     * 绑定一个类实例到容器
173
     * @access public
174
     * @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...
175
     * @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...
176
     * @return $this
177
     */
178 22
    public function instance(string $abstract, $instance)
179
    {
180 22
        if (isset($this->bind[$abstract])) {
181 19
            $bind = $this->bind[$abstract];
182
183 19
            if (is_string($bind)) {
184 19
                $this->instances[$bind] = $instance;
185 19
                return $this;
186
            }
187
        }
188
189 21
        $this->instances[$abstract] = $instance;
190
191 21
        return $this;
192
    }
193
194
    /**
195
     * 判断容器中是否存在类及标识
196
     * @access public
197
     * @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...
198
     * @return bool
199
     */
200 12
    public function bound(string $abstract): bool
201
    {
202 12
        return isset($this->bind[$abstract]) || isset($this->instances[$abstract]);
203
    }
204
205
    /**
206
     * 判断容器中是否存在类及标识
207
     * @access public
208
     * @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...
209
     * @return bool
210
     */
211 12
    public function has($name): bool
212
    {
213 12
        return $this->bound($name);
214
    }
215
216
    /**
217
     * 判断容器中是否存在对象实例
218
     * @access public
219
     * @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...
220
     * @return bool
221
     */
222 4
    public function exists(string $abstract): bool
223
    {
224 4
        if (isset($this->bind[$abstract])) {
225 3
            $bind = $this->bind[$abstract];
226
227 3
            if (is_string($bind)) {
228 2
                return isset($this->instances[$bind]);
229
            }
230
        }
231
232 4
        return isset($this->instances[$abstract]);
233
    }
234
235
    /**
236
     * 创建类的实例 已经存在则直接获取
237
     * @access public
238
     * @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...
239
     * @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...
240
     * @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...
241
     * @return mixed
242
     */
243 16
    public function make(string $abstract, array $vars = [], bool $newInstance = false)
244
    {
245 16
        if (isset($this->instances[$abstract]) && !$newInstance) {
246 12
            return $this->instances[$abstract];
247
        }
248
249 15
        if (isset($this->bind[$abstract])) {
250 13
            $concrete = $this->bind[$abstract];
251
252 13
            if ($concrete instanceof Closure) {
253 3
                $object = $this->invokeFunction($concrete, $vars);
254
            } else {
255 13
                return $this->make($concrete, $vars, $newInstance);
256
            }
257
        } else {
258 12
            $object = $this->invokeClass($abstract, $vars);
259
        }
260
261 15
        if (!$newInstance) {
262 15
            $this->instances[$abstract] = $object;
263
        }
264
265 15
        return $object;
266
    }
267
268
    /**
269
     * 删除容器中的对象实例
270
     * @access public
271
     * @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...
272
     * @return void
273
     */
274 2
    public function delete($abstract): void
275
    {
276 2
        foreach ((array) $abstract as $name) {
277 2
            if (isset($this->bind[$name])) {
278 2
                $name = $this->bind[$name];
279
            }
280
281 2
            if (isset($this->instances[$name])) {
282 2
                unset($this->instances[$name]);
283
            }
284
        }
285 2
    }
286
287
    /**
288
     * 获取容器中的对象实例
289
     * @access public
290
     * @return array
291
     */
292 1
    public function all()
293
    {
294 1
        return $this->instances;
295
    }
296
297
    /**
298
     * 清除容器中的对象实例
299
     * @access public
300
     * @return void
301
     */
302 1
    public function flush(): void
303
    {
304 1
        $this->instances = [];
305 1
        $this->bind      = [];
306
307 1
    }
308
309
    /**
310
     * 执行函数或者闭包方法 支持参数调用
311
     * @access public
312
     * @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...
313
     * @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...
314
     * @return mixed
315
     */
316 5
    public function invokeFunction($function, array $vars = [])
317
    {
318
        try {
319 5
            $reflect = new ReflectionFunction($function);
320
321 4
            $args = $this->bindParams($reflect, $vars);
322
323 4
            return call_user_func_array($function, $args);
324 1
        } catch (ReflectionException $e) {
325 1
            throw new Exception('function not exists: ' . $function . '()');
326
        }
327
    }
328
329
    /**
330
     * 调用反射执行类的方法 支持参数绑定
331
     * @access public
332
     * @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...
333
     * @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...
334
     * @return mixed
335
     */
336 3
    public function invokeMethod($method, array $vars = [])
337
    {
338
        try {
339 3
            if (is_array($method)) {
340 3
                $class   = is_object($method[0]) ? $method[0] : $this->invokeClass($method[0]);
341 3
                $reflect = new ReflectionMethod($class, $method[1]);
342
            } else {
343
                // 静态方法
344 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

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