Passed
Push — master ( 0080f7...0da285 )
by Runner
01:43
created

Container::offsetSet()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
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
    /**
36
     * @param $name
37
     * @param null $concrete
38
     * @param bool $share
39
     */
40 8
    public function bind($name, $concrete = null, $share = false)
41
    {
42 8
        if (is_null($concrete)) {
43 5
            $concrete = $name;
44
        }
45
46 8
        $this->bindings[$name] = $concrete;
47
48 8
        $share && $this->shares[$name] = true;
49 8
    }
50
51
    /**
52
     * @param string $name
53
     *
54
     * @throws ReflectionException
55
     *
56
     * @return object
57
     */
58 12
    public function make($name)
59
    {
60 12
        if (isset($this->instances[$name])) {
61 3
            return $this->instances[$name];
62
        }
63
64 11
        $instance = $this->build($name);
65
66 8
        if (isset($this->shares[$name])) {
67 2
            $this->instances[$name] = $instance;
68
        }
69
70 8
        return $instance;
71
    }
72
73
    /**
74
     * @param $name
75
     * @param $instance
76
     */
77
    public function instance($name, $instance)
78
    {
79
        $this->instances[$name] = $instance;
80
    }
81
82
    /**
83
     * @param $name
84
     *
85
     * @throws ReflectionException
86
     *
87
     * @return mixed|object
88
     */
89 11
    protected function build($name)
90
    {
91 11
        $concrete = $this->getConcrete($name);
92
93 11
        if ($concrete instanceof Closure) {
94 4
            return $concrete($this);
95
        }
96
97 10
        $reflector = new ReflectionClass($concrete);
98
99 9
        if (!$reflector->isInstantiable()) {
100 2
            throw new BindingResolutionException(sprintf('%s is not instantiable', $name));
101
        }
102
103 8
        $constructor = $reflector->getConstructor();
104
105 8
        if (!$constructor || !$constructor->getParameters()) {
106 8
            return $reflector->newInstance();
107
        }
108
109 3
        return $reflector->newInstanceArgs($this->getDependencies($constructor->getParameters()));
110
    }
111
112
    /**
113
     * @param ReflectionParameter[] $reflectionParameters
114
     *
115
     * @throws
116
     *
117
     * @return array
118
     */
119 3
    protected function getDependencies(array $reflectionParameters)
120
    {
121 3
        $result = [];
122 3
        foreach ($reflectionParameters as $parameter) {
123 3
            if (!is_null($parameter->getClass())) {
124
                try {
125 3
                    $result[] = $this->make($parameter->getClass()->getName());
126 2
                } catch (Exception $exception) {
127 2
                    if (!$parameter->isOptional()) {
128 1
                        throw $exception;
129
                    }
130 2
                    $result[] = $parameter->getDefaultValue();
131
                }
132
            } else {
133 1
                if (!$parameter->isDefaultValueAvailable()) {
134 1
                    throw new BindingResolutionException(
135 1
                        sprintf(
136 1
                            'parameter %s has no default value in %s',
137 1
                            $parameter->getName(),
138 1
                            $parameter->getDeclaringClass()->getName()
139
                        )
140
                    );
141
                }
142 2
                $result[] = $parameter->getDefaultValue();
143
            }
144
        }
145
146 2
        return $result;
147
    }
148
149
    /**
150
     * @param $name
151
     *
152
     * @return string|Closure
153
     */
154 11
    protected function getConcrete($name)
155
    {
156 11
        $concrete = $this->bindings[$name] ?? $name;
157
158 11
        if (!is_object($concrete) && $concrete !== $name && isset($this->bindings[$concrete])) {
159 1
            $concrete = function () use ($concrete) {
160 1
                return $this->make($concrete);
161 1
            };
162
        }
163
164 11
        return $concrete;
165
    }
166
167
    /**
168
     * @param mixed $offset
169
     *
170
     * @return bool
171
     */
172 2
    public function offsetExists($offset)
173
    {
174 2
        return isset($this->instances[$offset]) || isset($this->bindings[$offset]);
175
    }
176
177
    /**
178
     * @param mixed $offset
179
     *
180
     * @throws ReflectionException
181
     *
182
     * @return mixed|object
183
     */
184 1
    public function offsetGet($offset)
185
    {
186 1
        return $this->make($offset);
187
    }
188
189
    /**
190
     * @param mixed $offset
191
     * @param mixed $value
192
     */
193 2
    public function offsetSet($offset, $value)
194
    {
195 2
        $this->instances[$offset] = $value;
196 2
    }
197
198
    /**
199
     * @param mixed $offset
200
     */
201 1
    public function offsetUnset($offset)
202
    {
203 1
        unset($this->instances[$offset]);
204 1
    }
205
}
206