Di   A
last analyzed

Complexity

Total Complexity 35

Size/Duplication

Total Lines 238
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 56
dl 0
loc 238
rs 9.6
c 2
b 0
f 0
wmc 35

14 Methods

Rating   Name   Duplication   Size   Complexity  
A resolve() 0 11 3
A create() 0 7 2
A instantiate() 0 9 2
A get() 0 7 2
A reset() 0 4 1
A register() 0 8 3
A instantiable() 0 3 2
B resolveParameter() 0 17 7
A isRegistered() 0 3 1
A userDependencies() 0 9 2
A resolveParameters() 0 9 2
A autowire() 0 7 2
A registerDependencies() 0 11 5
A coreDependencies() 0 6 1
1
<?php
2
3
/**
4
 * Quantum PHP Framework
5
 *
6
 * An open source software development framework for PHP
7
 *
8
 * @package Quantum
9
 * @author Arman Ag. <[email protected]>
10
 * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
11
 * @link http://quantum.softberg.org/
12
 * @since 2.9.7
13
 */
14
15
namespace Quantum\Di;
16
17
use Quantum\Di\Exceptions\DiException;
18
use ReflectionException;
19
use ReflectionParameter;
20
use ReflectionFunction;
21
use ReflectionMethod;
22
use Quantum\App\App;
23
use ReflectionClass;
24
25
/**
26
 * Di Class
27
 * @package Quantum/Di
28
 */
29
class Di
30
{
31
32
    /**
33
     * @var array
34
     */
35
    private static $dependencies = [];
36
37
    /**
38
     * @var array
39
     */
40
    private static $container = [];
41
42
    /**
43
     * Loads dependency definitions
44
     */
45
    public static function registerDependencies()
46
    {
47
        foreach (self::coreDependencies() as $dependency) {
48
            if (!self::isRegistered($dependency)) {
49
                self::register($dependency);
50
            }
51
        }
52
53
        foreach (self::userDependencies() as $dependency) {
54
            if (!self::isRegistered($dependency)) {
55
                self::register($dependency);
56
            }
57
        }
58
    }
59
60
    /**
61
     * Registers new dependency
62
     * @param string $dependency
63
     * @return bool
64
     */
65
    public static function register(string $dependency): bool
66
    {
67
        if (!in_array($dependency, self::$dependencies) && class_exists($dependency)) {
68
            self::$dependencies[] = $dependency;
69
            return true;
70
        }
71
72
        return false;
73
    }
74
75
    /**
76
     * Checks if a dependency registered
77
     * @param string $dependency
78
     * @return bool
79
     */
80
    public static function isRegistered(string $dependency): bool
81
    {
82
        return in_array($dependency, self::$dependencies);
83
    }
84
85
    /**
86
     * Retrieves a shared instance of the given dependency.
87
     * @param string $dependency
88
     * @param array $args
89
     * @return mixed
90
     * @throws DiException
91
     * @throws ReflectionException
92
     */
93
    public static function get(string $dependency, array $args = [])
94
    {
95
        if (!self::isRegistered($dependency)) {
96
            throw DiException::dependencyNotRegistered($dependency);
97
        }
98
99
        return self::resolve($dependency, $args, true);
100
    }
101
102
    /**
103
     * Creates new instance of the given dependency.
104
     * @param string $dependency
105
     * @param array $args
106
     * @return mixed
107
     * @throws DiException
108
     * @throws ReflectionException
109
     */
110
    public static function create(string $dependency, array $args = [])
111
    {
112
        if (!self::isRegistered($dependency)) {
113
            self::register($dependency);
114
        }
115
116
        return self::resolve($dependency, $args, false);
117
    }
118
119
    /**
120
     * Automatically resolves and injects parameters for a callable.
121
     * @param callable $entry
122
     * @param array $args
123
     * @return array
124
     * @throws DiException
125
     * @throws ReflectionException
126
     */
127
    public static function autowire(callable $entry, array $args = []): array
128
    {
129
        $reflection = is_closure($entry)
130
            ? new ReflectionFunction($entry)
131
            : new ReflectionMethod(...$entry);
0 ignored issues
show
Bug introduced by
$entry is expanded, but the parameter $objectOrMethod of ReflectionMethod::__construct() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

131
            : new ReflectionMethod(/** @scrutinizer ignore-type */ ...$entry);
Loading history...
132
133
        return self::resolveParameters($reflection->getParameters(), $args);
134
    }
135
136
    /**
137
     * @return void
138
     */
139
    public static function reset(): void
140
    {
141
        self::$dependencies = [];
142
        self::$container = [];
143
    }
144
145
    /**
146
     * Resolves the dependency
147
     * @param string $dependency
148
     * @param array $args
149
     * @param bool $singleton
150
     * @return mixed
151
     * @throws DiException
152
     * @throws ReflectionException
153
     */
154
    private static function resolve(string $dependency, array $args = [], bool $singleton = true)
155
    {
156
        if ($singleton) {
157
            if (!isset(self::$container[$dependency])) {
158
                self::$container[$dependency] = self::instantiate($dependency, $args);
159
            }
160
161
            return self::$container[$dependency];
162
        }
163
164
        return self::instantiate($dependency, $args);
165
    }
166
167
    /**
168
     * Instantiates the dependency
169
     * @param string $dependency
170
     * @param array $args
171
     * @return mixed
172
     * @throws DiException
173
     * @throws ReflectionException
174
     */
175
    private static function instantiate(string $dependency, array $args = [])
176
    {
177
        $class = new ReflectionClass($dependency);
178
179
        $constructor = $class->getConstructor();
180
181
        $params = $constructor ? self::resolveParameters($constructor->getParameters(), $args) : [];
182
183
        return new $dependency(...$params);
184
    }
185
186
    /**
187
     * Resolves all parameters
188
     * @param array $parameters
189
     * @param array $args
190
     * @return array
191
     * @throws DiException
192
     * @throws ReflectionException
193
     */
194
    private static function resolveParameters(array $parameters, array &$args = []): array
195
    {
196
        $resolved = [];
197
198
        foreach ($parameters as $param) {
199
            $resolved[] = self::resolveParameter($param, $args);
200
        }
201
202
        return $resolved;
203
    }
204
205
    /**
206
     * Resolves the parameter
207
     * @param ReflectionParameter $param
208
     * @param array|null $args
209
     * @return array|mixed|null
210
     * @throws DiException
211
     * @throws ReflectionException
212
     */
213
    private static function resolveParameter(ReflectionParameter $param, ?array &$args = [])
214
    {
215
        $type = $param->getType() ? $param->getType()->getName() : null;
0 ignored issues
show
Bug introduced by
The method getName() does not exist on ReflectionType. It seems like you code against a sub-type of ReflectionType such as ReflectionNamedType. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

215
        $type = $param->getType() ? $param->getType()->/** @scrutinizer ignore-call */ getName() : null;
Loading history...
216
217
        if ($type && self::instantiable($type)) {
218
            return self::create($type);
219
        }
220
221
        if ($type === 'array') {
222
            return $args;
223
        }
224
225
        if (!empty($args)) {
226
            return array_shift($args);
227
        }
228
229
        return $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null;
230
    }
231
232
    /**
233
     * Checks if the class is instantiable
234
     * @param string $type
235
     * @return bool
236
     */
237
    protected static function instantiable(string $type): bool
238
    {
239
        return class_exists($type) && (new ReflectionClass($type))->isInstantiable();
240
    }
241
242
    /**
243
     * Loads user defined dependencies
244
     * @return array
245
     */
246
    private static function userDependencies(): array
247
    {
248
        $userDependencies = App::getBaseDir() . DS . 'shared' . DS . 'config' . DS . 'dependencies.php';
249
250
        if (!file_exists($userDependencies)) {
251
            return [];
252
        }
253
254
        return (array)require_once $userDependencies;
255
    }
256
257
    /**
258
     * Loads the core dependencies
259
     * @return array
260
     */
261
    private static function coreDependencies(): array
262
    {
263
        return [
264
            \Quantum\Loader\Loader::class,
265
            \Quantum\Http\Request::class,
266
            \Quantum\Http\Response::class,
267
        ];
268
    }
269
}
270