Completed
Push — master ( 97318c...d75021 )
by Changwan
03:35
created

Container::setAsGlobal()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 6
ccs 0
cts 4
cp 0
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
namespace Wandu\DI;
3
4
use Psr\Container\ContainerExceptionInterface;
5
use Psr\Container\ContainerInterface as PsrContainerInterface;
6
use Wandu\DI\Contracts\ResolverInterface;
7
use Wandu\DI\Exception\CannotChangeException;
8
use Wandu\DI\Exception\CannotFindParameterException;
9
use Wandu\DI\Exception\CannotResolveException;
10
use Wandu\DI\Exception\NullReferenceException;
11
use Wandu\DI\Exception\RequirePackageException;
12
use Wandu\DI\Resolvers\BindResolver;
13
use Wandu\DI\Resolvers\CallableResolver;
14
use Wandu\DI\Resolvers\InstanceResolver;
15
16
class Container implements ContainerInterface
17
{
18
    /** @var \Wandu\DI\ContainerInterface */
19
    public static $instance;
20
    
21
    /** @var \Wandu\DI\Descriptor[] */
22
    protected $descriptors = [];
23
    
24
    /** @var array */
25
    protected $caches = [];
26
27
    /** @var \Wandu\DI\ServiceProviderInterface[] */
28
    protected $providers = [];
29
30
    /** @var array */
31
    protected $aliases = [];
32
33
    /** @var array */
34
    protected $aliasIndex = [];
35
    
36
    /** @var bool */
37
    protected $isBooted = false;
38
39 80
    public function __construct()
40
    {
41 80
        $this->caches = [
42 80
            Container::class => $this,
43 80
            ContainerInterface::class => $this,
44 80
            PsrContainerInterface::class => $this,
45 80
            'container' => $this,
46
        ];
47 80
    }
48
49
    /**
50
     * @return \Wandu\DI\ContainerInterface
51
     */
52
    public function setAsGlobal()
53
    {
54
        $instance = static::$instance;
55
        static::$instance = $this;
56
        return $instance;
57
    }
58
59 3
    public function __clone()
60
    {
61 3
        $this->caches[Container::class] = $this;
62 3
        $this->caches[ContainerInterface::class] = $this;
63 3
        $this->caches[PsrContainerInterface::class] = $this;
64 3
        $this->caches['container'] = $this;
65 3
    }
66
67
    /**
68
     * {@inheritdoc}
69
     */
70
    public function __call($name, array $arguments)
71
    {
72
        return $this->call($this->get($name), $arguments);
0 ignored issues
show
Documentation introduced by
$this->get($name) is of type *, but the function expects a callable.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
73
    }
74
75
    /**
76
     * {@inheritdoc}
77
     */
78 2
    public function offsetExists($name)
79
    {
80 2
        return $this->has($name) && $this->get($name) !== null;
81
    }
82
83
    /**
84
     * {@inheritdoc}
85
     */
86 15
    public function offsetGet($name)
87
    {
88 15
        return $this->get($name);
89
    }
90
91
    /**
92
     * {@inheritdoc}
93
     */
94 2
    public function offsetSet($name, $value)
95
    {
96 2
        $this->instance($name, $value);
97 2
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102 1
    public function offsetUnset($name)
103
    {
104 1
        $this->destroy($name);
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110 49
    public function get($name)
111
    {
112 49
        while (isset($this->aliases[$name])) {
113 9
            $name = $this->aliases[$name];
114
        }
115 49
        if (array_key_exists($name, $this->caches)) {
116 5
            return $this->caches[$name];
117
        }
118
        try {
119
            try {
120 48
                $instance = $this->descriptor($name)->createInstance($this);
121 16
            } catch (NullReferenceException $e) {
122 15
                if (!class_exists($name)) {
123 1
                    throw $e;
124
                }
125 14
                $this->bind($name);
126 46
                $instance = $this->descriptor($name)->createInstance($this);
127
            }
128 6
        } catch (CannotFindParameterException $e) {
129 5
            throw new CannotResolveException($name, $e->getParameter());
130
        }
131 44
        return $instance;
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137
    public function assert(string $name, string $package)
138
    {
139
        try {
140
            return $this->get($name);
141
        } catch (ContainerExceptionInterface $e) {
142
            throw new RequirePackageException($name, $package);
143
        }
144
    }
145
    
146
    /**
147
     * {@inheritdoc}
148
     */
149 26
    public function has($name)
150
    {
151
        return
152 26
            array_key_exists($name, $this->caches) ||
153 24
            array_key_exists($name, $this->descriptors) || 
154 20
            class_exists($name) ||
155 26
            isset($this->aliases[$name]);
156
    }
157
158
    /**
159
     * {@inheritdoc}
160
     */
161 54
    public function destroy(...$names)
162
    {
163 54
        foreach ($names as $name) {
164 54
            if (array_key_exists($name, $this->caches)) {
165
                throw new CannotChangeException($name);
166
            }
167 54
            if (array_key_exists($name, $this->descriptors)) {
168 5
                if ($this->descriptors[$name]->frozen) {
169 2
                    throw new CannotChangeException($name);
170
                }
171
            }
172 54
            unset($this->descriptors[$name]);
173
        }
174 54
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179 30
    public function instance(string $name, $value): Descriptor
180
    {
181 30
        return $this->createDescriptor($name, new InstanceResolver($value), class_exists($name) ? $name : null);
182
    }
183
184
    /**
185
     * {@inheritdoc}
186
     */
187 14
    public function closure(string $name, callable $handler): Descriptor
188
    {
189 14
        return $this->createDescriptor($name, new CallableResolver($handler), class_exists($name) ? $name : null);
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     */
195 27
    public function bind(string $name, string $className = null): Descriptor
196
    {
197 27
        if (isset($className)) {
198 15
            $this->alias($className, $name);
199 15
            return $this->createDescriptor($name, new BindResolver($className), $className);
200
        }
201 18
        return $this->createDescriptor($name, new BindResolver($name), $name);
202
    }
203
204
    /**
205
     * {@inheritdoc}
206
     */
207 19
    public function alias(string $alias, string $target)
208
    {
209 19
        if (!array_key_exists($target, $this->aliasIndex)) {
210 19
            $this->aliasIndex[$target] = [];
211
        }
212 19
        $this->aliasIndex[$target][] = $alias;
213 19
        $this->aliases[$alias] = $target;
214 19
    }
215
216
    /**
217
     * {@inheritdoc}
218
     */
219 48
    public function descriptor(string $name): Descriptor
220
    {
221 48
        while (isset($this->aliases[$name])) {
222 1
            $name = $this->aliases[$name];
223
        }
224 48
        if (!array_key_exists($name, $this->descriptors)) {
225 15
            throw new NullReferenceException($name);
226
        }
227 47
        return $this->descriptors[$name];
228
    }
229
230
    /**
231
     * {@inheritdoc}
232
     */
233 3
    public function with(array $arguments = []): ContainerInterface
234
    {
235 3
        $new = clone $this;
236 3
        foreach ($arguments as $name => $argument) {
237 3
            if ($argument instanceof Descriptor) {
238
                 $new->createDescriptor($name, $argument);
0 ignored issues
show
Documentation introduced by
$argument is of type object<Wandu\DI\Descriptor>, but the function expects a object<Wandu\DI\Contracts\ResolverInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
239
            } else {
240 3
                $new->instance($name, $argument);
241
            }
242
        }
243 3
        return $new;
244
    }
245
    
246
    /**
247
     * {@inheritdoc}
248
     */
249 3
    public function create(string $className, array $arguments = [])
250
    {
251
        try {
252 3
            if (!class_exists($className)) {
253
                throw new NullReferenceException($className);
254
            }
255 3
            return (new Descriptor($className, new BindResolver($className)))->assign($arguments)->createInstance($this);
256 3
        } catch (CannotFindParameterException $e) {
257 3
            throw new CannotResolveException($className, $e->getParameter());
258
        }
259
    }
260
261
    /**
262
     * {@inheritdoc}
263
     */
264 6
    public function call(callable $callee, array $arguments = [])
265
    {
266
        try {
267 6
            return (new Descriptor(null, new CallableResolver($callee)))->assign($arguments)->createInstance($this);
268 4
        } catch (CannotFindParameterException $e) {
269 3
            throw new CannotResolveException($callee, $e->getParameter());
0 ignored issues
show
Documentation introduced by
$callee is of type callable, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
270
        }
271
    }
272
273
    /**
274
     * {@inheritdoc}
275
     */
276 8
    public function register(ServiceProviderInterface $provider)
277
    {
278 8
        $provider->register($this);
279 8
        $this->providers[] = $provider;
280 8
    }
281
282
    /**
283
     * {@inheritdoc}
284
     */
285 8
    public function boot()
286
    {
287 8
        if (!$this->isBooted) {
288 8
            foreach ($this->providers as $provider) {
289 6
                $provider->boot($this);
290
            }
291 8
            $this->isBooted = true;
292
        }
293 8
        return $this;
294
    }
295
296 54
    protected function createDescriptor($name, ResolverInterface $resolver, $className = null): Descriptor
297
    {
298 54
        $this->destroy($name);
299 54
        return $this->descriptors[$name] = new Descriptor($className, $resolver);
300
    }
301
}
302