Completed
Push — master ( 47a19f...e93966 )
by kill
06:04
created

Container::bind()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 2
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Created by rozbo at 2017/3/17 上午10:37
4
 * 容器类
5
 */
6
7
namespace puck;
8
9
10
class Container implements \ArrayAccess
11
{
12
    // 容器对象实例
13
    protected static $instance;
14
    // 容器中的对象实例
15
    protected $instances = [];
16
    // 容器中绑定的对象标识
17
    protected $bind = [];
18
19
    /**
20
     * 获取当前容器的实例(单例)
21
     * @access public
22
     * @return object
23
     */
24
    public static function getInstance()
25
    {
26
        if (is_null(static::$instance)) {
27
            static::$instance = new static;
28
        }
29
30
        return static::$instance;
31
    }
32
33
    /**
34
     * 绑定一个类到容器
35
     * @access public
36
     * @param string            $abstract    类标识、接口
37
     * @param string|\Closure   $concrete    要绑定的类或者闭包
38
     * @return void
39
     */
40
    public function bind($abstract, $concrete = null)
41
    {
42
        if (is_array($abstract)) {
43
            $this->bind = array_merge($this->bind, $abstract);
44
        } else {
45
            $this->bind[$abstract] = $concrete;
46
        }
47
    }
48
49
    /**
50
     * 绑定一个类实例当容器
51
     * @access public
52
     * @param string    $abstract    类名或者标识
53
     * @param object    $instance    类的实例
54
     * @return void
55
     */
56
    public function instance($abstract, $instance)
57
    {
58
        $this->instances[$abstract] = $instance;
59
    }
60
61
    /**
62
     * 判断容器中是否存在类及标识
63
     * @access public
64
     * @param string    $abstract    类名或者标识
65
     * @return bool
66
     */
67
    public function bound($abstract)
68
    {
69
        return isset($this->bind[$abstract]) || isset($this->instances[$abstract]);
70
    }
71
72
    /**
73
     * 创建类的实例
74
     * @access public
75
     * @param string    $class    类名或者标识
0 ignored issues
show
Bug introduced by
There is no parameter named $class. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
76
     * @param array     $args     变量
0 ignored issues
show
Bug introduced by
There is no parameter named $args. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
77
     * @return object
78
     */
79
    public function make($abstract, $vars = [])
80
    {
81
        if (isset($this->instances[$abstract])) {
82
            $object = $this->instances[$abstract];
83
        } elseif (isset($this->bind[$abstract])) {
84
            $concrete = $this->bind[$abstract];
85
            if ($concrete instanceof \Closure) {
86
                $object = call_user_func_array($concrete, $vars);
87
            } else {
88
                $object = $this->make($concrete, $vars);
89
            }
90
        } else {
91
            $object = $this->invokeClass($abstract, $vars);
92
93
            $this->instances[$abstract] = $object;
94
        }
95
        return $object;
96
    }
97
98
    /**
99
     * 执行函数或者闭包方法 支持参数调用
100
     * @access public
101
     * @param string|array|\Closure $function 函数或者闭包
102
     * @param array                 $vars     变量
103
     * @return mixed
104
     */
105
    public function invokeFunction($function, $vars = [])
106
    {
107
        $reflect = new \ReflectionFunction($function);
108
        $args    = $this->bindParams($reflect, $vars);
109
        return $reflect->invokeArgs($args);
110
    }
111
112
    /**
113
     * 调用反射执行类的方法 支持参数绑定
114
     * @access public
115
     * @param string|array $method 方法
116
     * @param array        $vars   变量
117
     * @return mixed
118
     */
119
    public function invokeMethod($method, $vars = [])
120
    {
121
        if (is_array($method)) {
122
            $class   = is_object($method[0]) ? $method[0] : $this->invokeClass($method[0]);
123
            $reflect = new \ReflectionMethod($class, $method[1]);
124
        } else {
125
            // 静态方法
126
            $reflect = new \ReflectionMethod($method);
127
        }
128
        $args = $this->bindParams($reflect, $vars);
129
        return $reflect->invokeArgs(isset($class) ? $class : null, $args);
130
    }
131
132
    /**
133
     * 调用反射执行callable 支持参数绑定
134
     * @access public
135
     * @param mixed $callable
136
     * @param array $vars   变量
137
     * @return mixed
138
     */
139
    public function invoke($callable, $vars = [])
140
    {
141
        if ($callable instanceof \Closure) {
142
            $result = $this->invokeFunction($callable, $vars);
143
        } else {
144
            $result = $this->invokeMethod($callable, $vars);
145
        }
146
        return $result;
147
    }
148
149
    /**
150
     * 调用反射执行类的实例化 支持依赖注入
151
     * @access public
152
     * @param string    $class 类名
153
     * @param array     $vars  变量
154
     * @return mixed
155
     */
156
    public function invokeClass($class, $vars = [])
157
    {
158
        $reflect     = new \ReflectionClass($class);
159
        $constructor = $reflect->getConstructor();
160
        if ($constructor) {
161
            $args = $this->bindParams($constructor, $vars);
162
        } else {
163
            $args = [];
164
        }
165
        return $reflect->newInstanceArgs($args);
166
    }
167
168
    /**
169
     * 绑定参数
170
     * @access protected
171
     * @param \ReflectionMethod|\ReflectionFunction $reflect 反射类
172
     * @param array                                 $vars    变量
173
     * @return array
174
     */
175
    protected function bindParams($reflect, $vars = [])
176
    {
177
        $args = [];
178
        if ($reflect->getNumberOfParameters() > 0) {
179
            // 判断数组类型 数字数组时按顺序绑定参数
180
            reset($vars);
181
            $type   = key($vars) === 0 ? 1 : 0;
182
            $params = $reflect->getParameters();
183
            foreach ($params as $param) {
184
                $name  = $param->getName();
185
                $class = $param->getClass();
186
                if ($class) {
187
                    $className = $class->getName();
188
                    $args[]    = $this->make($className);
189
                } elseif (1 == $type && !empty($vars)) {
190
                    $args[] = array_shift($vars);
191
                } elseif (0 == $type && isset($vars[$name])) {
192
                    $args[] = $vars[$name];
193
                } elseif ($param->isDefaultValueAvailable()) {
194
                    $args[] = $param->getDefaultValue();
195
                } else {
196
                    throw new \InvalidArgumentException('method param miss:' . $name);
197
                }
198
            }
199
        }
200
        return $args;
201
    }
202
203
    public function offsetExists($key)
204
    {
205
        return $this->bound($key);
206
    }
207
208
    public function offsetGet($key)
209
    {
210
        return $this->make($key);
211
    }
212
213
    public function offsetSet($key, $value)
214
    {
215
        $this->bind($key, $value);
216
    }
217
218
    public function offsetUnset($key)
219
    {
220
        $this->__unset($key);
221
    }
222
223
    public function __set($name, $value)
224
    {
225
        $this->bind($name, $value);
226
    }
227
228
    public function __get($name)
229
    {
230
        return $this->make($name);
231
    }
232
233
    public function __isset($name)
234
    {
235
        return $this->bound($name);
236
    }
237
238
    public function __unset($name)
239
    {
240
        unset($this->bind[$name], $this->instances[$name]);
241
    }
242
243
}