Completed
Push — master ( ca5a00...0b72e8 )
by Runner
22:52 queued 19:59
created

Container::offsetExists()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * @author: RunnerLee
4
 * @email: [email protected]
5
 * @time: 2019-04
6
 */
7
8
namespace Runner\Container;
9
10
use ArrayAccess;
11
use Closure;
12
use Exception;
13
use ReflectionClass;
14
use ReflectionException;
15
use ReflectionParameter;
16
use Runner\Container\Exceptions\BindingResolutionException;
17
18
class Container implements ArrayAccess
19
{
20
    /**
21
     * @var array
22
     */
23
    protected $bindings = [];
24
25
    /**
26
     * @var array
27
     */
28
    protected $instances = [];
29
30
    /**
31
     * @var array
32
     */
33
    protected $shares = [];
34
35
    protected $contextual = [];
36
37
    /**
38
     * @param $name
39
     * @param null $concrete
40
     * @param bool $share
41
     */
42 10
    public function bind($name, $concrete = null, $share = false)
43
    {
44 10
        if (is_null($concrete)) {
45 5
            $concrete = $name;
46
        }
47
48 10
        $this->bindings[$name] = $concrete;
49
50 10
        $share && $this->shares[$name] = true;
51 10
    }
52
53
    /**
54
     * @param $concretes
55
     * @param $parameter
56
     * @param $implementation
57
     */
58 2
    public function bindContext($concretes, $parameter, $implementation)
59
    {
60 2
        foreach ((array) $concretes as $concrete) {
61 2
            $this->contextual[$concrete][$parameter] = $implementation;
62
        }
63 2
    }
64
65
    /**
66
     * @param $name
67
     * @param $instance
68
     */
69 1
    public function instance($name, $instance)
70
    {
71 1
        $this->instances[$name] = $instance;
72 1
    }
73
74
    /**
75
     * @param string $name
76
     *
77
     * @throws ReflectionException
78
     *
79
     * @return object
80
     */
81 15
    public function make($name)
82
    {
83 15
        if (isset($this->instances[$name])) {
84 4
            return $this->instances[$name];
85
        }
86
87 13
        $instance = $this->build($name);
88
89 10
        if (isset($this->shares[$name])) {
90 2
            $this->instances[$name] = $instance;
91
        }
92
93 10
        return $instance;
94
    }
95
96
    /**
97
     * @param $name
98
     *
99
     * @throws ReflectionException
100
     *
101
     * @return mixed|object
102
     */
103 13
    protected function build($name)
104
    {
105 13
        $concrete = $this->getConcrete($name);
106
107 13
        if ($concrete instanceof Closure) {
108 6
            return $concrete($this);
109
        }
110
111 12
        $reflector = new ReflectionClass($concrete);
112
113 11
        if (!$reflector->isInstantiable()) {
114 2
            throw new BindingResolutionException(sprintf('%s is not instantiable', $name));
115
        }
116
117 10
        $constructor = $reflector->getConstructor();
118
119 10
        if (!$constructor || !$constructor->getParameters()) {
120 10
            return $reflector->newInstance();
121
        }
122
123 5
        return $reflector->newInstanceArgs($this->getDependencies($concrete, $constructor->getParameters()));
124
    }
125
126
    /**
127
     * @param $name
128
     *
129
     * @return bool
130
     */
131 2
    public function isBound($name)
132
    {
133 2
        return isset($this->instances[$name]) || isset($this->bindings[$name]);
134
    }
135
136
    /**
137
     * @param $concrete
138
     * @param ReflectionParameter[] $reflectionParameters
139
     *
140
     * @throws Exception
141
     *
142
     * @return array
143
     */
144 5
    protected function getDependencies($concrete, array $reflectionParameters)
145
    {
146 5
        $result = [];
147 5
        foreach ($reflectionParameters as $parameter) {
148 5
            if (!is_null($parameter->getClass())) {
149
                try {
150 5
                    $class = $parameter->getClass()->getName();
151 5
                    if (isset($this->contextual[$concrete][$class])) {
152 2
                        $result[] = $this->buildContextualBinding($this->contextual[$concrete][$class]);
153
                    } else {
154 5
                        $result[] = $this->make($class);
155
                    }
156 2
                } catch (Exception $exception) {
157 2
                    if (!$parameter->isOptional()) {
158 1
                        throw $exception;
159
                    }
160 4
                    $result[] = $parameter->getDefaultValue();
161
                }
162
            } else {
163 1
                if (!$parameter->isDefaultValueAvailable()) {
164 1
                    throw new BindingResolutionException(
165 1
                        sprintf(
166 1
                            'parameter %s has no default value in %s',
167 1
                            $parameter->getName(),
168 1
                            $parameter->getDeclaringClass()->getName()
169
                        )
170
                    );
171
                }
172 4
                $result[] = $parameter->getDefaultValue();
173
            }
174
        }
175
176 4
        return $result;
177
    }
178
179
    /**
180
     * @param $implementation
181
     *
182
     * @throws ReflectionException
183
     *
184
     * @return mixed|object
185
     */
186 2
    protected function buildContextualBinding($implementation)
187
    {
188 2
        if ($implementation instanceof Closure) {
189 2
            return $implementation($this);
190
        }
191
192 2
        return $this->make($implementation);
193
    }
194
195
    /**
196
     * @param $name
197
     *
198
     * @return string|Closure
199
     */
200 13
    protected function getConcrete($name)
201
    {
202 13
        $concrete = $this->bindings[$name] ?? $name;
203
204 13
        if (!is_object($concrete) && $concrete !== $name && isset($this->bindings[$concrete])) {
205 1
            $concrete = function () use ($concrete) {
206 1
                return $this->make($concrete);
207 1
            };
208
        }
209
210 13
        return $concrete;
211
    }
212
213
    /**
214
     * @param mixed $offset
215
     *
216
     * @return bool
217
     */
218 2
    public function offsetExists($offset)
219
    {
220 2
        return $this->isBound($offset);
221
    }
222
223
    /**
224
     * @param mixed $offset
225
     *
226
     * @throws ReflectionException
227
     *
228
     * @return mixed|object
229
     */
230 1
    public function offsetGet($offset)
231
    {
232 1
        return $this->make($offset);
233
    }
234
235
    /**
236
     * @param mixed $offset
237
     * @param mixed $value
238
     */
239 2
    public function offsetSet($offset, $value)
240
    {
241 2
        $this->instances[$offset] = $value;
242 2
    }
243
244
    /**
245
     * @param mixed $offset
246
     */
247 1
    public function offsetUnset($offset)
248
    {
249 1
        unset($this->instances[$offset]);
250 1
    }
251
}
252