Passed
Push — master ( 28ea87...37d3cb )
by kill
02:19
created

Container::make()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 13
c 1
b 0
f 0
nc 4
nop 2
dl 0
loc 18
ccs 10
cts 10
cp 1
crap 4
rs 9.2
1
<?php
2
/**
3
 * Created by rozbo at 2017/3/17 上午10:37
4
 * 容器类
5
 */
6
7
namespace puck;
8
9
10
use ArrayAccess;
11
12
class Container implements ArrayAccess
13
{
14
    // 容器对象实例
15
    protected static $instance;
16
    // 容器中的对象实例
17
    protected $instances=[];
18
    // 容器中绑定的对象标识
19
    protected $bind=[];
20
21
    /**
22
     * 获取当前容器的实例(单例)
23
     * @access public
24
     * @return \puck\Container
25
     */
26 3
    public static function getInstance()
27
    {
28 3
        if (is_null(static::$instance)) {
29
            static::$instance=new static;
30
        }
31
32 3
        return static::$instance;
33
    }
34
35
36
    /**
37
     * 设置共享容器实例
38
     * @param ArrayAccess $container
39
     * @return static
40
     */
41
    public static function setInstance(ArrayAccess $container)
42
    {
43
        return static::$instance=$container;
44
    }
45
46
    /**
47
     * 绑定一个类到容器
48
     * @access public
49
     * @param string            $abstract    类标识、接口
50
     * @param string|\Closure   $concrete    要绑定的类或者闭包
51
     * @return void
52
     */
53
    public function bind($abstract, $concrete=null)
54
    {
55
        if (is_array($abstract)) {
56
            $this->bind=array_merge($this->bind, $abstract);
57
        } else {
58
            $this->bind[$abstract]=$concrete;
59
        }
60
    }
61
62
    /**
63
     * 绑定一个类实例当容器
64
     * @access public
65
     * @param string    $abstract    类名或者标识
66
     * @param object    $instance    类的实例
67
     * @return void
68
     */
69
    public function instance($abstract, $instance)
70
    {
71
        $this->instances[$abstract]=$instance;
72
    }
73
74
    /**
75
     * 判断容器中是否存在类及标识
76
     * @access public
77
     * @param string    $abstract    类名或者标识
78
     * @return bool
79
     */
80
    public function bound($abstract)
81
    {
82
        return isset($this->bind[$abstract]) || isset($this->instances[$abstract]);
83
    }
84
85
    /**
86
     * 创建类的实例
87
     * @access public
88
     * @param array     $vars     变量
89
     * @return object
90
     */
91 2
    public function make($abstract, $vars=[])
92
    {
93 2
        if (isset($this->instances[$abstract])) {
94 1
            $object=$this->instances[$abstract];
95 2
        } elseif (isset($this->bind[$abstract])) {
96 1
            $concrete=$this->bind[$abstract];
97 1
            if ($concrete instanceof \Closure) {
98
                $object=call_user_func_array($concrete, $vars);
99
            } else {
100 1
                $object=$this->make($concrete, $vars);
101
            }
102
        } else {
103 1
            $object=$this->invokeClass($abstract, $vars);
104
105 1
            $this->instances[$abstract]=$object;
106
        }
107 2
        return $object;
108
    }
109
110
    /**
111
     * 执行函数或者闭包方法 支持参数调用
112
     * @access public
113
     * @param \Closure $function 函数或者闭包
114
     * @param array                 $vars     变量
115
     * @return mixed
116
     */
117
    public function invokeFunction($function, $vars=[])
118
    {
119
        $reflect=new \ReflectionFunction($function);
120
        $args=$this->bindParams($reflect, $vars);
121
        return $reflect->invokeArgs($args);
122
    }
123
124
    /**
125
     * 调用反射执行类的方法 支持参数绑定
126
     * @access public
127
     * @param string|array $method 方法
128
     * @param array        $vars   变量
129
     * @return mixed
130
     */
131
    public function invokeMethod($method, $vars=[])
132
    {
133
        if (is_array($method)) {
134
            $class=is_object($method[0]) ? $method[0] : $this->invokeClass($method[0]);
135
            $reflect=new \ReflectionMethod($class, $method[1]);
136
        } else {
137
            // 静态方法
138
            $reflect=new \ReflectionMethod($method);
139
        }
140
        $args=$this->bindParams($reflect, $vars);
141
        return $reflect->invokeArgs(isset($class) ? $class : null, $args);
142
    }
143
144
    /**
145
     * 调用反射执行callable 支持参数绑定
146
     * @access public
147
     * @param mixed $callable
148
     * @param array $vars   变量
149
     * @return mixed
150
     */
151
    public function invoke($callable, $vars=[])
152
    {
153
        if ($callable instanceof \Closure) {
154
            $result=$this->invokeFunction($callable, $vars);
155
        } else {
156
            $result=$this->invokeMethod($callable, $vars);
157
        }
158
        return $result;
159
    }
160
161
    /**
162
     * 调用反射执行类的实例化 支持依赖注入
163
     * @access public
164
     * @param string    $class 类名
165
     * @param array     $vars  变量
166
     * @return mixed
167
     */
168 1
    public function invokeClass($class, $vars=[])
169
    {
170 1
        $reflect=new \ReflectionClass($class);
171 1
        $constructor=$reflect->getConstructor();
172 1
        if ($constructor) {
173 1
            $args=$this->bindParams($constructor, $vars);
174
        } else {
175
            $args=[];
176
        }
177 1
        return $reflect->newInstanceArgs($args);
178
    }
179
180
    /**
181
     * 绑定参数
182
     * @access protected
183
     * @param \ReflectionMethod|\ReflectionFunction $reflect 反射类
184
     * @param array                                 $vars    变量
185
     * @return array
186
     */
187 1
    protected function bindParams($reflect, $vars=[])
188
    {
189 1
        $args=[];
190 1
        if ($reflect->getNumberOfParameters() > 0) {
191
            // 判断数组类型 数字数组时按顺序绑定参数
192 1
            reset($vars);
193 1
            $type=key($vars) === 0 ? 1 : 0;
194 1
            $params=$reflect->getParameters();
195 1
            foreach ($params as $param) {
196 1
                $name=$param->getName();
197 1
                $class=$param->getClass();
198 1
                if ($class) {
199
                    $className=$class->getName();
200
                    $args[]=$this->make($className);
201 1
                } elseif (1 == $type && !empty($vars)) {
202
                    $args[]=array_shift($vars);
203 1
                } elseif (0 == $type && isset($vars[$name])) {
204
                    $args[]=$vars[$name];
205 1
                } elseif ($param->isDefaultValueAvailable()) {
206 1
                    $args[]=$param->getDefaultValue();
207
                } else {
208 1
                    throw new \InvalidArgumentException('method param miss:'.$name);
209
                }
210
            }
211
        }
212 1
        return $args;
213
    }
214
215
    public function offsetExists($key)
216
    {
217
        return $this->bound($key);
218
    }
219
220
    public function offsetGet($key)
221
    {
222
        return $this->make($key);
223
    }
224
225
    public function offsetSet($key, $value)
226
    {
227
        $this->bind($key, $value);
228
    }
229
230
    public function offsetUnset($key)
231
    {
232
        $this->__unset($key);
233
    }
234
235
    public function __set($name, $value)
236
    {
237
        $this->bind($name, $value);
238
    }
239
240
    public function __get($name)
241
    {
242
        return $this->make($name);
243
    }
244
245
    public function __isset($name)
246
    {
247
        return $this->bound($name);
248
    }
249
250
    public function __unset($name)
251
    {
252
        unset($this->bind[$name], $this->instances[$name]);
253
    }
254
255
}