Completed
Push — master ( 5d71a8...69686b )
by Arman
23s queued 17s
created

ModuleLoader::getInstance()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 7
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.7
13
 */
14
15
namespace Quantum\Module;
16
17
use Quantum\Libraries\Storage\Factories\FileSystemFactory;
18
use Quantum\Libraries\Config\Exceptions\ConfigException;
19
use Quantum\Module\Exceptions\ModuleLoaderException;
20
use Quantum\Router\Exceptions\RouteException;
21
use Quantum\Libraries\Storage\FileSystem;
22
use Quantum\Di\Exceptions\DiException;
23
use Quantum\Exceptions\BaseException;
24
use Quantum\Router\Router;
25
use Quantum\Router\Route;
26
use ReflectionException;
27
use Quantum\App\App;
28
use Closure;
29
30
/**
31
 * Class ModuleLoader
32
 * @package Quantum\Module
33
 */
34
class ModuleLoader
35
{
36
37
    /**
38
     * @var array
39
     */
40
    private static $moduleConfigs = [];
41
42
    /**
43
     * @var array<Closure>
44
     */
45
    private static $moduleRoutes = [];
46
47
    /**
48
     * @var FileSystem
49
     */
50
    private $fs;
51
52
    /**
53
     * @var ModuleLoader|null
54
     */
55
    private static $instance = null;
56
57
    /**
58
     * @throws BaseException
59
     * @throws DiException
60
     * @throws ConfigException
61
     * @throws ReflectionException
62
     */
63
    private function __construct()
64
    {
65
        $this->fs = FileSystemFactory::get();
66
    }
67
68
    /**
69
     * @return ModuleLoader
70
     */
71
    public static function getInstance(): ModuleLoader
72
    {
73
        if (self::$instance === null) {
74
            self::$instance = new self();
75
        }
76
77
        return self::$instance;
78
    }
79
80
    /**
81
     * Load Modules
82
     * @throws ModuleLoaderException
83
     * @throws RouteException
84
     */
85
    public function loadModulesRoutes()
86
    {
87
        if (empty(self::$moduleConfigs)) {
88
            $this->loadModuleConfig();
89
        }
90
91
        $modulesRoutes = [];
92
93
        foreach (self::$moduleConfigs as $module => $options) {
94
            if (!$this->isModuleEnabled($options)) {
95
                continue;
96
            }
97
98
            $modulesRoutes = array_merge($modulesRoutes, $this->getModuleRoutes($module, new Route([$module => $options])));
99
        }
100
101
        Router::setRoutes($modulesRoutes);
102
    }
103
104
    /**
105
     * @return array
106
     * @throws ModuleLoaderException
107
     */
108
    public function getModuleConfigs(): array
109
    {
110
        if (empty(self::$moduleConfigs)) {
111
            $this->loadModuleConfig();
112
        }
113
114
        return self::$moduleConfigs;
115
    }
116
117
    /**
118
     * @throws ModuleLoaderException
119
     */
120
    private function loadModuleConfig()
121
    {
122
        $configPath = App::getBaseDir() . DS . 'shared' . DS . 'config' . DS . 'modules.php';
123
124
        if (!$this->fs->exists($configPath)) {
125
            throw ModuleLoaderException::moduleConfigNotFound();
126
        }
127
128
        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

128
        /** @scrutinizer ignore-call */ 
129
        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...
129
    }
130
131
    /**
132
     * @param array $options
133
     * @return bool
134
     */
135
    private function isModuleEnabled(array $options): bool
136
    {
137
        return $options['enabled'] ?? false;
138
    }
139
140
    /**
141
     * @param string $module
142
     * @param Route $route
143
     * @return array
144
     * @throws ModuleLoaderException
145
     * @throws RouteException
146
     */
147
    private function getModuleRoutes(string $module, Route $route): array
148
    {
149
        $moduleRoutes = modules_dir() . DS . $module . DS . 'routes' . DS . 'routes.php';
150
151
        if (!$this->fs->exists($moduleRoutes)) {
152
            throw ModuleLoaderException::moduleRoutesNotFound($module);
153
        }
154
155
        if(empty(self::$moduleRoutes[$module])) {
156
            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

156
            /** @scrutinizer ignore-call */ 
157
            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...
157
        }
158
159
        if (!self::$moduleRoutes[$module] instanceof Closure) {
0 ignored issues
show
introduced by
self::moduleRoutes[$module] is always a sub-type of Closure.
Loading history...
160
            throw RouteException::notClosure();
161
        }
162
163
        self::$moduleRoutes[$module]($route);
164
165
        return $route->getRuntimeRoutes();
166
    }
167
}