Passed
Pull Request — master (#182)
by Arman
12:44 queued 09:39
created

Di::userDependencies()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 9
rs 10
c 2
b 0
f 0
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.5
13
 */
14
15
namespace Quantum\Di;
16
17
use Quantum\Exceptions\DiException;
18
use ReflectionParameter;
19
use ReflectionException;
20
use ReflectionFunction;
21
use ReflectionMethod;
22
use ReflectionClass;
23
24
/**
25
 * Di Class
26
 *
27
 * @package Quantum
28
 * @category Di
29
 */
30
class Di
31
{
32
33
    /**
34
     * @var array
35
     */
36
    private static $dependencies = [];
37
38
    /**
39
     * @var array
40
     */
41
    private static $container = [];
42
43
    /**
44
     * Loads dependency definitions
45
     */
46
    public static function loadDefinitions()
47
    {
48
        if (empty(self::$dependencies)) {
49
            self::$dependencies = self::coreDependencies();
50
51
            foreach (self::userDependencies() as $dependency) {
52
                self::add($dependency);
53
            }
54
        }
55
    }
56
57
    /**
58
     * Creates and injects dependencies.
59
     * @param callable $entry
60
     * @param array $additional
61
     * @return array
62
     * @throws DiException
63
     * @throws ReflectionException
64
     */
65
    public static function autowire(callable $entry, array $additional = []): array
66
    {
67
        $reflection = is_closure($entry)
68
            ? new ReflectionFunction($entry)
69
            : 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

69
            : new ReflectionMethod(/** @scrutinizer ignore-type */ ...$entry);
Loading history...
70
71
        $params = [];
72
73
        foreach ($reflection->getParameters() as $param) {
74
            $params[] = self::resolveParameter($param, $additional);
75
        }
76
77
        return $params;
78
    }
79
80
    /**
81
     * Adds new dependency
82
     * @param string $dependency
83
     */
84
    public static function add(string $dependency)
85
    {
86
        if (!in_array($dependency, self::$dependencies) && class_exists($dependency)) {
87
            self::$dependencies[] = $dependency;
88
        }
89
    }
90
91
    /**
92
     * Gets the dependency from the container
93
     * @param string $dependency
94
     * @return mixed
95
     * @throws DiException
96
     * @throws ReflectionException
97
     */
98
    public static function get(string $dependency)
99
    {
100
        if (!in_array($dependency, self::$dependencies)) {
101
            throw DiException::dependencyNotDefined($dependency);
102
        }
103
104
        if (!isset(self::$container[$dependency])) {
105
            self::$container[$dependency] = self::instantiate($dependency);
106
        }
107
108
        return self::$container[$dependency];
109
110
    }
111
112
    /**
113
     * Instantiates the dependency
114
     * @param string $dependency
115
     * @return mixed
116
     * @throws DiException
117
     * @throws ReflectionException
118
     */
119
    protected static function instantiate(string $dependency)
120
    {
121
        $class = new ReflectionClass($dependency);
122
123
        $constructor = $class->getConstructor();
124
125
        $params = [];
126
127
        if ($constructor) {
128
            foreach ($constructor->getParameters() as $param) {
129
                $params[] = self::resolveParameter($param);
130
            }
131
        }
132
133
        return new $dependency(...$params);
134
    }
135
136
    /**
137
     * Checks if the class is instantiable
138
     * @param string $type
139
     * @return bool
140
     */
141
    protected static function instantiable(string $type): bool
142
    {
143
        return class_exists($type) && (new ReflectionClass($type))->isInstantiable();
144
    }
145
146
    /**
147
     * @param ReflectionParameter $param
148
     * @param array|null $additional
149
     * @return array|mixed|null
150
     * @throws DiException
151
     * @throws ReflectionException
152
     */
153
    private static function resolveParameter(ReflectionParameter $param, ?array &$additional = [])
154
    {
155
        $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

155
        $type = $param->getType() ? $param->getType()->/** @scrutinizer ignore-call */ getName() : null;
Loading history...
156
157
        if ($type && self::instantiable($type)) {
158
            return self::get($type);
159
        }
160
161
        if ($type === 'array') {
162
            return $additional;
163
        }
164
165
        if (count($additional)) {
166
            return array_shift($additional);
167
        }
168
169
        return $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null;
170
171
    }
172
173
    /**
174
     * Loads user defined dependencies
175
     * @return array
176
     */
177
    private static function userDependencies(): array
178
    {
179
        $userDependencies = base_dir() . DS . 'shared' . DS . 'config' . DS . 'dependencies.php';
180
181
        if (!file_exists($userDependencies)) {
182
            return [];
183
        }
184
185
        return (array)require_once $userDependencies;
186
    }
187
188
    /**
189
     * Gets the core dependencies
190
     * @return array
191
     */
192
    private static function coreDependencies(): array
193
    {
194
        return [
195
            \Quantum\Http\Request::class,
196
            \Quantum\Http\Response::class,
197
            \Quantum\Loader\Loader::class,
198
            \Quantum\Factory\ViewFactory::class,
199
            \Quantum\Libraries\Storage\FileSystem::class,
200
        ];
201
    }
202
203
}
204