Container::regexBind()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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