Passed
Pull Request — master (#322)
by Arman
03:30
created

ModuleLoader::loadModulesDependencies()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 4
nop 0
dl 0
loc 13
rs 10
c 0
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.8
13
 */
14
15
namespace Quantum\Module;
16
17
use Quantum\Libraries\Storage\Factories\FileSystemFactory;
18
use Quantum\Config\Exceptions\ConfigException;
19
use Quantum\Module\Exceptions\ModuleException;
20
use Quantum\Router\Exceptions\RouteException;
21
use Quantum\App\Exceptions\BaseException;
22
use Quantum\Libraries\Storage\FileSystem;
23
use Quantum\Di\Exceptions\DiException;
24
use Quantum\Router\Route;
25
use ReflectionException;
26
use Quantum\App\App;
27
use Closure;
28
29
/**
30
 * Class ModuleLoader
31
 * @package Quantum\Module
32
 */
33
class ModuleLoader
34
{
35
36
    /**
37
     * @var array
38
     */
39
    private static $moduleDependencies = [];
40
41
    /**
42
     * @var array
43
     */
44
    private static $moduleConfigs = [];
45
46
    /**
47
     * @var array<Closure>
48
     */
49
    private static $moduleRoutes = [];
50
51
    /**
52
     * @var FileSystem
53
     */
54
    private $fs;
55
56
    /**
57
     * @var ModuleLoader|null
58
     */
59
    private static $instance = null;
60
61
    /**
62
     * @throws BaseException
63
     * @throws DiException
64
     * @throws ConfigException
65
     * @throws ReflectionException
66
     */
67
    private function __construct()
68
    {
69
        $this->fs = FileSystemFactory::get();
70
    }
71
72
    /**
73
     * @return ModuleLoader
74
     */
75
    public static function getInstance(): ModuleLoader
76
    {
77
        if (self::$instance === null) {
78
            self::$instance = new self();
79
        }
80
81
        return self::$instance;
82
    }
83
84
    /**
85
     * Load modules routes
86
     * @return array
87
     * @throws ModuleException
88
     * @throws RouteException
89
     */
90
    public function loadModulesRoutes(): array
91
    {
92
        if (empty(self::$moduleConfigs)) {
93
            $this->loadModuleConfig();
94
        }
95
96
        $modulesRoutes = [];
97
98
        foreach (self::$moduleConfigs as $module => $options) {
99
            if (!$this->isModuleEnabled($options)) {
100
                continue;
101
            }
102
103
            $modulesRoutes = array_merge($modulesRoutes, $this->getModuleRoutes($module, new Route([$module => $options])));
104
        }
105
106
        return $modulesRoutes;
107
    }
108
109
    /**
110
     * @return array
111
     * @throws ModuleException
112
     */
113
    public function loadModulesDependencies(): array
114
    {
115
        if (empty(self::$moduleConfigs)) {
116
            $this->loadModuleConfig();
117
        }
118
119
        $modulesDependencies = [];
120
121
        foreach (self::$moduleConfigs as $module => $options) {
122
            $modulesDependencies = array_merge($modulesDependencies, $this->getModuleDependencies($module));
123
        }
124
125
        return $modulesDependencies;
126
    }
127
128
    /**
129
     * @param string $module
130
     * @return array
131
     */
132
    public function getModuleDependencies(string $module): array
133
    {
134
        if (!isset(self::$moduleDependencies[$module])) {
135
            $file = modules_dir() . DS . $module . DS . 'config' . DS . 'dependencies.php';
136
137
            if ($this->fs->exists($file)) {
138
                $deps = $this->fs->require($file);
0 ignored issues
show
Unused Code introduced by
The call to Quantum\Libraries\Storage\FileSystem::require() has too many arguments starting with $file. ( Ignorable by Annotation )

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

138
                /** @scrutinizer ignore-call */ 
139
                $deps = $this->fs->require($file);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
139
140
                self::$moduleDependencies[$module] = is_array($deps) ? $deps : [];
141
            } else {
142
                self::$moduleDependencies[$module] = [];
143
            }
144
        }
145
146
        return self::$moduleDependencies[$module];
147
    }
148
149
    /**
150
     * @return array
151
     * @throws ModuleException
152
     */
153
    public function getModuleConfigs(): array
154
    {
155
        if (empty(self::$moduleConfigs)) {
156
            $this->loadModuleConfig();
157
        }
158
159
        return self::$moduleConfigs;
160
    }
161
162
    /**
163
     * @throws ModuleException
164
     */
165
    private function loadModuleConfig()
166
    {
167
        $configPath = App::getBaseDir() . DS . 'shared' . DS . 'config' . DS . 'modules.php';
168
169
        if (!$this->fs->exists($configPath)) {
170
            throw ModuleException::moduleConfigNotFound();
171
        }
172
173
        self::$moduleConfigs = $this->fs->require($configPath);
0 ignored issues
show
Unused Code introduced by
The call to Quantum\Libraries\Storage\FileSystem::require() has too many arguments starting with $configPath. ( Ignorable by Annotation )

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

173
        /** @scrutinizer ignore-call */ 
174
        self::$moduleConfigs = $this->fs->require($configPath);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
174
    }
175
176
    /**
177
     * @param array $options
178
     * @return bool
179
     */
180
    private function isModuleEnabled(array $options): bool
181
    {
182
        return $options['enabled'] ?? false;
183
    }
184
185
    /**
186
     * @param string $module
187
     * @param Route $route
188
     * @return array
189
     * @throws ModuleException
190
     * @throws RouteException
191
     */
192
    private function getModuleRoutes(string $module, Route $route): array
193
    {
194
        $moduleRoutes = modules_dir() . DS . $module . DS . 'routes' . DS . 'routes.php';
195
196
        if (!$this->fs->exists($moduleRoutes)) {
197
            throw ModuleException::moduleRoutesNotFound($module);
198
        }
199
200
        if(empty(self::$moduleRoutes[$module])) {
201
            self::$moduleRoutes[$module] = $this->fs->require($moduleRoutes, true);
0 ignored issues
show
Unused Code introduced by
The call to Quantum\Libraries\Storage\FileSystem::require() has too many arguments starting with $moduleRoutes. ( Ignorable by Annotation )

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

201
            /** @scrutinizer ignore-call */ 
202
            self::$moduleRoutes[$module] = $this->fs->require($moduleRoutes, true);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
202
        }
203
204
        if (!self::$moduleRoutes[$module] instanceof Closure) {
0 ignored issues
show
introduced by
self::moduleRoutes[$module] is always a sub-type of Closure.
Loading history...
205
            throw RouteException::notClosure();
206
        }
207
208
        self::$moduleRoutes[$module]($route);
209
210
        return $route->getRuntimeRoutes();
211
    }
212
}