Passed
Push — dev ( 79ab41...849c07 )
by 世昌
02:49
created

ApplicationLoader::loadModule()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 15
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 20
rs 9.7666
1
<?php
2
3
namespace suda\application\loader;
4
5
use suda\framework\Cache;
6
use suda\framework\Config;
7
use suda\application\Module;
8
use suda\application\Resource;
9
use suda\application\ModuleBag;
10
use suda\application\Application;
11
use suda\application\database\Database;
12
use suda\database\exception\SQLException;
13
use suda\framework\filesystem\FileSystem;
14
use suda\application\builder\ModuleBuilder;
15
use suda\framework\loader\Loader;
16
use suda\framework\runnable\Runnable;
17
18
/**
19
 * 应用程序
20
 */
21
class ApplicationLoader
22
{
23
    /**
24
     * 应用程序
25
     *
26
     * @var Application
27
     */
28
    protected $application;
29
30
    /**
31
     * 模块加载器
32
     *
33
     * @var ModuleLoader[]
34
     */
35
    protected $moduleLoader;
36
37
38
    /**
39
     * @var array
40
     */
41
    protected $actionableModules;
42
43
    /**
44
     * @var array
45
     */
46
    protected $reachableModules;
47
48
    public function __construct(Application $application)
49
    {
50
        $this->application = $application;
51
        $this->actionableModules = [];
52
        $this->reachableModules = [];
53
    }
54
55
    /**
56
     * 加载APP
57
     */
58
    public function load()
59
    {
60
        $this->loadVendorIfExist();
61
        $this->loadGlobalConfig();
62
        $this->loadModule();
63
        $this->prepareModule();
64
        $this->activeModule();
65
    }
66
67
    /**
68
     * 加载模块
69
     */
70
    protected function loadModule()
71
    {
72
        $name = 'application-module';
73
        $this->application->debug()->time($name);
74
        // 调试模式不缓存
75
        if (static::isDebug()) {
76
            $this->registerModule();
77
            $this->setModuleStatus();
78
        } else {
79
            if ($this->application->cache()->has($name)) {
80
                $module = $this->application->cache()->get($name);
81
                $this->application->setModule($module);
82
                $this->application->debug()->info('load modules from cache');
83
            } else {
84
                $this->registerModule();
85
                $this->setModuleStatus();
86
                $this->application->cache()->set($name, $this->application->getModules());
87
            }
88
        }
89
        $this->application->debug()->timeEnd($name);
90
    }
91
92
    /**
93
     * 调试模式
94
     *
95
     * @return bool
96
     */
97
    public static function isDebug()
98
    {
99
        return boolval(defined('SUDA_DEBUG') ? constant('SUDA_DEBUG') : false);
100
    }
101
102
    /**
103
     * 加载路由
104
     */
105
    public function loadRoute()
106
    {
107
        $name = 'application-route';
108
        $this->application->debug()->time($name);
109
        if (static::isDebug()) {
110
            $this->loadRouteFromModules();
111
        } elseif ($this->application->cache()->has($name.'-route')) {
112
            $route = $this->application->cache()->get($name.'-route');
113
            $runnable = $this->application->cache()->get($name.'-runnable');
114
            $this->application->getRoute()->setRouteCollection($route);
115
            $this->application->getRoute()->setRunnable($runnable);
116
            $this->application->debug()->info('load route from cache');
117
        } else {
118
            $this->loadRouteFromModules();
119
            if ($this->application->getRoute()->isContainClosure()) {
120
                $this->application->debug()->warning('route contain closure, route prepare cannot be cacheables');
121
            } else {
122
                $this->application->cache()->set($name.'-route', $this->application->getRoute()->getRouteCollection());
123
                $this->application->cache()->set($name.'-runnable', $this->application->getRoute()->getRunnable());
124
            }
125
        }
126
        $this->application->debug()->timeEnd($name);
127
    }
128
129
    protected function loadRouteFromModules()
130
    {
131
        foreach ($this->application->getModules() as $name => $module) {
132
            if ($module->getStatus() === Module::REACHABLE) {
133
                call_user_func([$this->moduleLoader[$name], 'toReachable']);
134
            }
135
        }
136
    }
137
138
    /**
139
     * 加载全局配置
140
     */
141
    public function loadGlobalConfig()
142
    {
143
        $resource = $this->application->getResource();
144
        if ($configPath = $resource->getConfigResourcePath('config/config')) {
145
            $this->application->getConfig()->load($configPath);
146
        }
147
        if ($listenerPath = $resource->getConfigResourcePath('config/listener')) {
148
            $this->application->loadEvent($listenerPath);
149
        }
150
    }
151
152
    /**
153
     * 加载额外vendor
154
     */
155
    public function loadVendorIfExist()
156
    {
157
        $vendorAutoload = $this->application->getPath() . '/vendor/autoload.php';
158
        if (FileSystem::exist($vendorAutoload)) {
159
            Loader::requireOnce($vendorAutoload);
160
        }
161
    }
162
163
    /**
164
     * 准备数据源
165
     *
166
     * @throws SQLException
167
     */
168
    public function loadDataSource()
169
    {
170
        Database::loadApplication($this->application);
171
        $dataSource = Database::getDefaultDataSource();
172
        $this->application->setDataSource($dataSource);
173
    }
174
175
    /**
176
     * 准备模块
177
     */
178
    protected function prepareModule()
179
    {
180
        foreach ($this->application->getModules()->all() as $name => $module) {
181
            $this->moduleLoader[$name] = new ModuleLoader($this->application, $module);
182
            $this->moduleLoader[$name]->toLoad(); // 切换到加载状态
183
            $this->moduleLoader[$name]->loadExtraModuleResourceLibrary(); // 加载二外的模块源
184
        }
185
    }
186
187
    /**
188
     * 激活模块
189
     */
190
    protected function activeModule()
191
    {
192
        foreach ($this->application->getModules()->all() as $name => $module) {
193
            if ($module->getStatus() !== Module::LOADED) {
194
                $this->moduleLoader[$name]->toActive();
195
            }
196
        }
197
    }
198
199
    /**
200
     * 注册模块
201
     */
202
    protected function registerModule()
203
    {
204
        $extractPath = $this->application->getDataPath() . '/extract-module';
205
        FileSystem::make($extractPath);
206
        foreach ($this->application->getModulePaths() as $path) {
207
            $this->registerModuleFrom($path, $extractPath);
208
        }
209
    }
210
211
    /**
212
     * 设置模块状态
213
     */
214
    protected function setModuleStatus()
215
    {
216
        $active = $this->application->getManifest('module.active', $this->actionableModules);
217
        $reachable = $this->application->getManifest('module.reachable', $this->reachableModules);
218
        $this->setModuleActive($this->application->getModules(), $active);
219
        $this->setModuleReachable($this->application->getModules(), $reachable);
220
    }
221
222
    /**
223
     * 注册模块下的模块
224
     *
225
     * @param string $path
226
     * @param string $extractPath
227
     */
228
    protected function registerModuleFrom(string $path, string $extractPath)
229
    {
230
        $modules = new ModuleBag;
231
        foreach (ModuleBuilder::scan($path, $extractPath) as $module) {
232
            $modules->add($module);
233
        }
234
        $this->prepareModuleConfig($path, $modules);
235
    }
236
237
    /**
238
     * 获取模块的配置
239
     *
240
     * @param string $path
241
     * @param ModuleBag $modules
242
     */
243
    protected function prepareModuleConfig(string $path, ModuleBag $modules)
244
    {
245
        $config = $this->getModuleDirectoryConfig($path);
246
        $moduleNames = array_keys($modules->all());
247
        // 获取模块文件夹模块配置
248
        $load = $config['load'] ?? $moduleNames;
249
        $active = $config['active'] ?? $load;
250
        $reachable = $config['reachable'] ?? $active;
251
        // 获取允许加载的模块
252
        $load = $this->application->getManifest('module.load', $load);
253
        $this->loadModuleFromBag($modules, $load);
254
        $this->actionableModules = array_merge($this->actionableModules, $active);
255
        $this->reachableModules = array_merge($this->reachableModules, $reachable);
256
    }
257
258
    /**
259
     * 获取目录的模板配置
260
     *
261
     * @param string $path
262
     * @return array
263
     */
264
    protected function getModuleDirectoryConfig(string $path)
265
    {
266
        $resource = new Resource([$path]);
267
        $configPath = $resource->getConfigResourcePath('config');
268
        if ($configPath) {
269
            return Config::loadConfig($configPath, $this->application->getConfig()) ?? [];
270
        }
271
        return [];
272
    }
273
274
    protected function loadModuleFromBag(ModuleBag $bag, array $load)
275
    {
276
        foreach ($load as $moduleName) {
277
            if ($module = $bag->get($moduleName)) {
278
                $module->setStatus(Module::LOADED);
279
                $this->application->add($module);
280
            }
281
        }
282
    }
283
284
    protected function setModuleActive(ModuleBag $bag, array $active)
285
    {
286
        foreach ($active as $moduleName) {
287
            if ($module = $bag->get($moduleName)) {
288
                $module->setStatus(Module::ACTIVE);
289
            }
290
        }
291
    }
292
293
    protected function setModuleReachable(ModuleBag $bag, array $reachable)
294
    {
295
        foreach ($reachable as $moduleName) {
296
            if ($module = $bag->get($moduleName)) {
297
                $module->setStatus(Module::REACHABLE);
298
            }
299
        }
300
    }
301
}
302