Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 184 |
|
0.00% |
0 / 24 |
CRAP | |
0.00% |
0 / 1 |
ModuleManager | |
0.00% |
0 / 184 |
|
0.00% |
0 / 24 |
6642 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getLanguageFiles | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
12 | |||
getUriLoad | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
12 | |||
getActiveModules | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
72 | |||
isInstalled | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isActive | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isRunning | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getAllModules | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
30 | |||
getInstalledModules | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
42 | |||
loadInfo | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
deactivate | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
20 | |||
deactivateModule | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
activate | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
20 | |||
activateModule | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
reInit | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
install | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
72 | |||
uninstall | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
90 | |||
installModule | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
installProviding | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
get | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
initModuleController | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
getModuleInstance | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
30 | |||
initRequestModules | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getRoutedModules | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | /** |
3 | * Jingga |
4 | * |
5 | * PHP Version 8.1 |
6 | * |
7 | * @package phpOMS\Module |
8 | * @copyright Dennis Eichhorn |
9 | * @license OMS License 2.0 |
10 | * @version 1.0.0 |
11 | * @link https://jingga.app |
12 | */ |
13 | declare(strict_types=1); |
14 | |
15 | namespace phpOMS\Module; |
16 | |
17 | use phpOMS\Application\ApplicationAbstract; |
18 | use phpOMS\Application\ApplicationInfo; |
19 | use phpOMS\Autoloader; |
20 | use phpOMS\DataStorage\Database\Query\Builder; |
21 | use phpOMS\Message\RequestAbstract; |
22 | use phpOMS\Module\Exception\InvalidModuleException; |
23 | |
24 | /** |
25 | * Module manager class. |
26 | * |
27 | * General module functionality such as listings and initialization. |
28 | * |
29 | * @package phpOMS\Module |
30 | * @license OMS License 2.0 |
31 | * @link https://jingga.app |
32 | * @since 1.0.0 |
33 | */ |
34 | final class ModuleManager |
35 | { |
36 | /** |
37 | * All modules that are running on this uri. |
38 | * |
39 | * @var \phpOMS\Module\ModuleAbstract[] |
40 | * @since 1.0.0 |
41 | */ |
42 | private array $running = []; |
43 | |
44 | /** |
45 | * Application instance. |
46 | * |
47 | * @var ApplicationAbstract |
48 | * @since 1.0.0 |
49 | */ |
50 | private ApplicationAbstract $app; |
51 | |
52 | /** |
53 | * Installed modules. |
54 | * |
55 | * @var array<string, ModuleInfo> |
56 | * @since 1.0.0 |
57 | */ |
58 | private array $installed = []; |
59 | |
60 | /** |
61 | * All active modules (on all pages not just the ones that are running now). |
62 | * |
63 | * @var array<string, array> |
64 | * @since 1.0.0 |
65 | */ |
66 | private array $active = []; |
67 | |
68 | /** |
69 | * Module path. |
70 | * |
71 | * @var string |
72 | * @since 1.0.0 |
73 | */ |
74 | private string $modulePath; |
75 | |
76 | /** |
77 | * All modules in the module directory. |
78 | * |
79 | * @var array<string, ModuleInfo> |
80 | * @since 1.0.0 |
81 | */ |
82 | private array $all = []; |
83 | |
84 | /** |
85 | * To load based on request uri. |
86 | * |
87 | * @var array<string, array> |
88 | * @since 1.0.0 |
89 | */ |
90 | private array $uriLoad = []; |
91 | |
92 | /** |
93 | * Constructor. |
94 | * |
95 | * @param ApplicationAbstract $app Application |
96 | * @param string $modulePath Path to modules (must end with '/') |
97 | * |
98 | * @since 1.0.0 |
99 | */ |
100 | public function __construct(ApplicationAbstract $app, string $modulePath = '') |
101 | { |
102 | $this->app = $app; |
103 | $this->modulePath = $modulePath; |
104 | } |
105 | |
106 | /** |
107 | * Get language files. |
108 | * |
109 | * @param RequestAbstract $request Request |
110 | * @param null|string $app App name |
111 | * |
112 | * @return string[] |
113 | * |
114 | * @since 1.0.0 |
115 | */ |
116 | public function getLanguageFiles(RequestAbstract $request, string $app = null) : array |
117 | { |
118 | $files = $this->getUriLoad($request); |
119 | if (!isset($files['5'])) { |
120 | return []; |
121 | } |
122 | |
123 | $lang = []; |
124 | foreach ($files['5'] as $module) { |
125 | $lang[] = '/Modules/' |
126 | . $module['module_load_from'] |
127 | . '/Theme/' |
128 | . ($app ?? $this->app->appName) |
129 | . '/Lang/' |
130 | . $module['module_load_file']; |
131 | } |
132 | |
133 | return $lang; |
134 | } |
135 | |
136 | /** |
137 | * Get modules that run on this page. |
138 | * |
139 | * @param RequestAbstract $request Request |
140 | * |
141 | * @return array<int|string, array> |
142 | * |
143 | * @since 1.0.0 |
144 | */ |
145 | public function getUriLoad(RequestAbstract $request) : array |
146 | { |
147 | if (empty($this->uriLoad)) { |
148 | $uriHash = $request->getHash(); |
149 | |
150 | $query = new Builder($this->app->dbPool->get('select')); |
151 | $sth = $query->select('module_load.module_load_type', 'module_load.*') |
152 | ->from('module_load') |
153 | ->innerJoin('module')->on('module_load.module_load_from', '=', 'module.module_id')->orOn('module_load.module_load_for', '=', 'module.module_id') |
154 | ->whereIn('module_load.module_load_pid', $uriHash) |
155 | ->andWhere('module.module_status', '=', ModuleStatus::ACTIVE) |
156 | ->execute(); |
157 | |
158 | if ($sth === null) { |
159 | return []; |
160 | } |
161 | |
162 | $this->uriLoad = $sth->fetchAll(\PDO::FETCH_GROUP); |
163 | } |
164 | |
165 | return $this->uriLoad; |
166 | } |
167 | |
168 | /** |
169 | * Get all installed modules that are active (not just on this uri). |
170 | * |
171 | * @param bool $useCache Use Cache or load new |
172 | * |
173 | * @return array<string, array> |
174 | * |
175 | * @since 1.0.0 |
176 | */ |
177 | public function getActiveModules(bool $useCache = true) : array |
178 | { |
179 | if (empty($this->active) || !$useCache) { |
180 | $query = new Builder($this->app->dbPool->get('select')); |
181 | $sth = $query->select('module.module_path') |
182 | ->from('module') |
183 | ->where('module.module_status', '=', ModuleStatus::ACTIVE) |
184 | ->execute(); |
185 | |
186 | if ($sth === null) { |
187 | return []; |
188 | } |
189 | |
190 | $active = $sth->fetchAll(\PDO::FETCH_COLUMN); |
191 | |
192 | foreach ($active as $module) { |
193 | $path = $this->modulePath . $module . '/info.json'; |
194 | |
195 | if (!\is_file($path)) { |
196 | continue; |
197 | } |
198 | |
199 | $content = \file_get_contents($path); |
200 | |
201 | $json = \json_decode($content === false ? '[]' : $content, true); |
202 | if (!\is_array($json)) { |
203 | return $this->active; |
204 | } |
205 | |
206 | /** @var array{name:array{internal:string}} $json */ |
207 | $this->active[$json['name']['internal']] = $json; |
208 | } |
209 | } |
210 | |
211 | return $this->active; |
212 | } |
213 | |
214 | /** |
215 | * Is module installed |
216 | * |
217 | * @param string $module Module name |
218 | * |
219 | * @return bool |
220 | * |
221 | * @since 1.0.0 |
222 | */ |
223 | public function isInstalled(string $module) : bool |
224 | { |
225 | return isset($this->getInstalledModules(false)[$module]); |
226 | } |
227 | |
228 | /** |
229 | * Is module active |
230 | * |
231 | * @param string $module Module name |
232 | * |
233 | * @return bool |
234 | * |
235 | * @since 1.0.0 |
236 | */ |
237 | public function isActive(string $module) : bool |
238 | { |
239 | return isset($this->getActiveModules(false)[$module]); |
240 | } |
241 | |
242 | /** |
243 | * Is module active |
244 | * |
245 | * @param string $module Module name |
246 | * @param null|string $ctlName Controller name |
247 | * |
248 | * @return bool |
249 | * |
250 | * @since 1.0.0 |
251 | */ |
252 | public function isRunning(string $module, string $ctlName = null) : bool |
253 | { |
254 | $name = '\\Modules\\' . $module . '\\Controller\\' . ($ctlName ?? $this->app->appName) . 'Controller'; |
255 | |
256 | return isset($this->running[$name]); |
257 | } |
258 | |
259 | /** |
260 | * Get all modules in the module directory. |
261 | * |
262 | * @return array<string, ModuleInfo> |
263 | * |
264 | * @since 1.0.0 |
265 | */ |
266 | public function getAllModules() : array |
267 | { |
268 | if (empty($this->all)) { |
269 | \chdir($this->modulePath); |
270 | $files = \glob('*', \GLOB_ONLYDIR); |
271 | |
272 | if ($files === false) { |
273 | return $this->all; // @codeCoverageIgnore |
274 | } |
275 | |
276 | $c = \count($files); |
277 | for ($i = 0; $i < $c; ++$i) { |
278 | $info = $this->loadInfo($files[$i]); |
279 | |
280 | if ($info !== null) { |
281 | $this->all[$files[$i]] = $info; |
282 | } |
283 | } |
284 | } |
285 | |
286 | return $this->all; |
287 | } |
288 | |
289 | /** |
290 | * Get modules that are available from official resources. |
291 | * |
292 | * @return array |
293 | * |
294 | * @since 1.0.0 |
295 | */ |
296 | /* |
297 | public function getAvailableModules() : array |
298 | { |
299 | return []; |
300 | } |
301 | */ |
302 | |
303 | /** |
304 | * Get all installed modules. |
305 | * |
306 | * @param bool $useCache Use Cache |
307 | * |
308 | * @return array<string, ModuleInfo> |
309 | * |
310 | * @since 1.0.0 |
311 | */ |
312 | public function getInstalledModules(bool $useCache = true) : array |
313 | { |
314 | if (empty($this->installed) || !$useCache) { |
315 | $query = new Builder($this->app->dbPool->get()); |
316 | $sth = $query->select('module.module_path') |
317 | ->from('module') |
318 | ->where('module_status', '!=', ModuleStatus::AVAILABLE) |
319 | ->execute(); |
320 | |
321 | if ($sth === null) { |
322 | return $this->installed; |
323 | } |
324 | |
325 | /** @var string[] $installed */ |
326 | $installed = $sth->fetchAll(\PDO::FETCH_COLUMN); |
327 | |
328 | foreach ($installed as $module) { |
329 | $info = $this->loadInfo($module); |
330 | |
331 | if ($info !== null) { |
332 | $this->installed[$module] = $info; |
333 | } |
334 | } |
335 | } |
336 | |
337 | return $this->installed; |
338 | } |
339 | |
340 | /** |
341 | * Load info of module. |
342 | * |
343 | * @param string $module Module name |
344 | * |
345 | * @return null|ModuleInfo |
346 | * |
347 | * @since 1.0.0 |
348 | */ |
349 | public function loadInfo(string $module) : ?ModuleInfo |
350 | { |
351 | $path = \realpath($this->modulePath . $module . '/info.json'); |
352 | if ($path === false) { |
353 | return null; |
354 | } |
355 | |
356 | $info = new ModuleInfo($path); |
357 | $info->load(); |
358 | |
359 | return $info; |
360 | } |
361 | |
362 | /** |
363 | * Deactivate module. |
364 | * |
365 | * @param string $module Module name |
366 | * |
367 | * @return bool |
368 | * |
369 | * @since 1.0.0 |
370 | */ |
371 | public function deactivate(string $module) : bool |
372 | { |
373 | $installed = $this->getInstalledModules(false); |
374 | if (!isset($installed[$module])) { |
375 | return false; |
376 | } |
377 | |
378 | try { |
379 | $info = $this->loadInfo($module); |
380 | if ($info === null) { |
381 | return false; // @codeCoverageIgnore |
382 | } |
383 | |
384 | $this->deactivateModule($info); |
385 | |
386 | return true; |
387 | } catch (\Exception $_) { |
388 | return false; // @codeCoverageIgnore |
389 | } |
390 | } |
391 | |
392 | /** |
393 | * Deactivate module. |
394 | * |
395 | * @param ModuleInfo $info Module info |
396 | * |
397 | * @return void |
398 | * |
399 | * @throws InvalidModuleException Throws this exception in case the deactiviation doesn't exist |
400 | * |
401 | * @since 1.0.0 |
402 | */ |
403 | private function deactivateModule(ModuleInfo $info) : void |
404 | { |
405 | $class = '\\Modules\\' . $info->getDirectory() . '\\Admin\\Status'; |
406 | if (!Autoloader::exists($class)) { |
407 | throw new InvalidModuleException($info->getDirectory()); |
408 | } |
409 | |
410 | /** @var $class DeactivateAbstract */ |
411 | $class::deactivate($this->app, $info); |
412 | } |
413 | |
414 | /** |
415 | * Deactivate module. |
416 | * |
417 | * @param string $module Module name |
418 | * |
419 | * @return bool |
420 | * |
421 | * @since 1.0.0 |
422 | */ |
423 | public function activate(string $module) : bool |
424 | { |
425 | $installed = $this->getInstalledModules(false); |
426 | if (!isset($installed[$module])) { |
427 | return false; |
428 | } |
429 | |
430 | try { |
431 | $info = $this->loadInfo($module); |
432 | if ($info === null) { |
433 | return false; // @codeCoverageIgnore |
434 | } |
435 | |
436 | $this->activateModule($info); |
437 | |
438 | return true; |
439 | } catch (\Exception $_) { |
440 | return false; // @codeCoverageIgnore |
441 | } |
442 | } |
443 | |
444 | /** |
445 | * Activate module. |
446 | * |
447 | * @param ModuleInfo $info Module info |
448 | * |
449 | * @return void |
450 | * |
451 | * @throws InvalidModuleException Throws this exception in case the activation doesn't exist |
452 | * |
453 | * @since 1.0.0 |
454 | */ |
455 | private function activateModule(ModuleInfo $info) : void |
456 | { |
457 | $class = '\\Modules\\' . $info->getDirectory() . '\\Admin\\Status'; |
458 | if (!Autoloader::exists($class)) { |
459 | throw new InvalidModuleException($info->getDirectory()); |
460 | } |
461 | |
462 | /** @var $class ActivateAbstract */ |
463 | $class::activate($this->app, $info); |
464 | } |
465 | |
466 | /** |
467 | * Re-init module. |
468 | * |
469 | * @param string $module Module name |
470 | * @param ApplicationInfo $appInfo Application info |
471 | * |
472 | * @return void |
473 | * |
474 | * @throws InvalidModuleException Throws this exception in case the installer doesn't exist |
475 | * |
476 | * @since 1.0.0 |
477 | */ |
478 | public function reInit(string $module, ApplicationInfo $appInfo = null) : void |
479 | { |
480 | $info = $this->loadInfo($module); |
481 | if ($info === null) { |
482 | return; |
483 | } |
484 | |
485 | $class = '\\Modules\\' . $info->getDirectory() . '\\Admin\\Installer'; |
486 | |
487 | if (!Autoloader::exists($class)) { |
488 | throw new InvalidModuleException($info->getDirectory()); |
489 | } |
490 | |
491 | /** @var $class InstallerAbstract */ |
492 | $class::reInit($info, $appInfo); |
493 | } |
494 | |
495 | /** |
496 | * Install module. |
497 | * |
498 | * @param string $module Module name |
499 | * |
500 | * @return bool |
501 | * |
502 | * @since 1.0.0 |
503 | */ |
504 | public function install(string $module) : bool |
505 | { |
506 | $installed = $this->getInstalledModules(false); |
507 | if (isset($installed[$module])) { |
508 | return true; |
509 | } |
510 | |
511 | if (!\is_file($this->modulePath . $module . '/Admin/Installer.php')) { |
512 | return false; |
513 | } |
514 | |
515 | try { |
516 | $info = $this->loadInfo($module); |
517 | if ($info === null) { |
518 | return false; // @codeCoverageIgnore |
519 | } |
520 | |
521 | $this->installed[$module] = $info; |
522 | $this->installModule($info); |
523 | |
524 | /* Install providing but only if receiving module is already installed */ |
525 | $providing = $info->getProviding(); |
526 | foreach ($providing as $key => $_) { |
527 | if (isset($installed[$key])) { |
528 | $this->installProviding('/Modules/' . $module, $key); |
529 | } |
530 | } |
531 | |
532 | /* Install receiving and applications */ |
533 | foreach ($this->installed as $key => $_) { |
534 | $this->installProviding('/Modules/' . $key, $module); |
535 | } |
536 | |
537 | return true; |
538 | } catch (\Throwable $_) { |
539 | return false; // @codeCoverageIgnore |
540 | } |
541 | } |
542 | |
543 | /** |
544 | * Uninstall module. |
545 | * |
546 | * @param string $module Module name |
547 | * |
548 | * @return bool |
549 | * |
550 | * @throws InvalidModuleException |
551 | * |
552 | * @since 1.0.0 |
553 | */ |
554 | public function uninstall(string $module) : bool |
555 | { |
556 | $installed = $this->getInstalledModules(false); |
557 | if (!isset($installed[$module])) { |
558 | return false; |
559 | } |
560 | |
561 | if (!\is_file($this->modulePath . $module . '/Admin/Uninstaller.php')) { |
562 | return false; |
563 | } |
564 | |
565 | try { |
566 | $info = $this->loadInfo($module); |
567 | if ($info === null) { |
568 | return false; // @codeCoverageIgnore |
569 | } |
570 | |
571 | $this->installed[$module] = $info; |
572 | // uninstall dependencies if not used by others |
573 | // uninstall providing for |
574 | // uninstall receiving from? no? |
575 | // uninstall module |
576 | |
577 | $class = '\\Modules\\' . $info->getDirectory() . '\\Admin\\Uninstaller'; |
578 | if (!Autoloader::exists($class)) { |
579 | throw new InvalidModuleException($info->getDirectory()); |
580 | } |
581 | |
582 | /** @var $class UninstallerAbstract */ |
583 | $class::uninstall($this->app, $info); |
584 | |
585 | if (isset($this->installed[$module])) { |
586 | unset($this->installed[$module]); |
587 | } |
588 | |
589 | if (isset($this->running[$module])) { |
590 | unset($this->running[$module]); |
591 | } |
592 | |
593 | if (isset($this->active[$module])) { |
594 | unset($this->active[$module]); |
595 | } |
596 | |
597 | return true; |
598 | } catch (\Throwable $_) { |
599 | return false; // @codeCoverageIgnore |
600 | } |
601 | } |
602 | |
603 | /** |
604 | * Install module itself. |
605 | * |
606 | * @param ModuleInfo $info Module info |
607 | * |
608 | * @return void |
609 | * |
610 | * @throws InvalidModuleException Throws this exception in case the installer doesn't exist |
611 | * |
612 | * @since 1.0.0 |
613 | */ |
614 | private function installModule(ModuleInfo $info) : void |
615 | { |
616 | $class = '\\Modules\\' . $info->getDirectory() . '\\Admin\\Installer'; |
617 | if (!Autoloader::exists($class)) { |
618 | throw new InvalidModuleException($info->getDirectory()); |
619 | } |
620 | |
621 | /** @var InstallerAbstract $class */ |
622 | $class::install($this->app, $info, $this->app->appSettings); |
623 | } |
624 | |
625 | /** |
626 | * Install providing. |
627 | * |
628 | * Installing additional functionality for another module |
629 | * |
630 | * @param string $from From path |
631 | * @param string $for For module |
632 | * |
633 | * @return void |
634 | * |
635 | * @since 1.0.0 |
636 | */ |
637 | public function installProviding(string $from, string $for) : void |
638 | { |
639 | if (!\is_file(__DIR__ . '/../..' . $from . '/Admin/Install/' . $for . '.php')) { |
640 | return; |
641 | } |
642 | |
643 | $from = \strtr($from, '/', '\\'); |
644 | |
645 | $class = $from . '\\Admin\\Install\\' . $for; |
646 | $class::install($this->app, $this->modulePath); |
647 | } |
648 | |
649 | /** |
650 | * Get module instance. |
651 | * |
652 | * This also returns inactive or uninstalled modules if they are still in the modules directory. |
653 | * |
654 | * @param string $module Module name |
655 | * @param string $ctlName Controller name (null = current) |
656 | * |
657 | * @return object|\phpOMS\Module\ModuleAbstract |
658 | * |
659 | * @todo Remove docblock type hint hack "object". |
660 | * The return type object is only used to stop the annoying warning that a method doesn't exist |
661 | * if you chain call the methods part of the returned ModuleAbstract implementation. |
662 | * Remove it once alternative inline type hinting is possible for the specific returned implementation. |
663 | * This also causes phpstan type inspection errors, which we have to live with or ignore in the settings |
664 | * |
665 | * @since 1.0.0 |
666 | */ |
667 | public function get(string $module, string $ctlName = null) : ModuleAbstract |
668 | { |
669 | $name = '\\Modules\\' . $module . '\\Controller\\' . ($ctlName ?? $this->app->appName) . 'Controller'; |
670 | if (!isset($this->running[$name])) { |
671 | $this->initModuleController($module, $ctlName); |
672 | } |
673 | |
674 | /* @phpstan-ignore-next-line */ |
675 | return $this->running[$name] ?? new NullModule(); |
676 | } |
677 | |
678 | /** |
679 | * Initialize module. |
680 | * |
681 | * Also registers controller in the dispatcher |
682 | * |
683 | * @param string $module Module |
684 | * @param string $ctlName Controller name (null = current app) |
685 | * |
686 | * @return void |
687 | * |
688 | * @since 1.0.0 |
689 | */ |
690 | private function initModuleController(string $module, string $ctlName = null) : void |
691 | { |
692 | $name = '\\Modules\\' . $module . '\\Controller\\' . ($ctlName ?? $this->app->appName) . 'Controller'; |
693 | $this->running[$name] = $this->getModuleInstance($module, $ctlName); |
694 | |
695 | if ($this->app->dispatcher !== null) { |
696 | $this->app->dispatcher->set($this->running[$name], $name); |
697 | } |
698 | } |
699 | |
700 | /** |
701 | * Gets and initializes modules. |
702 | * |
703 | * @param string $module Module ID |
704 | * @param string $ctlName Controller name (null = current app) |
705 | * |
706 | * @return ModuleAbstract |
707 | * |
708 | * @since 1.0.0 |
709 | */ |
710 | public function getModuleInstance(string $module, string $ctlName = null) : ModuleAbstract |
711 | { |
712 | $class = '\\Modules\\' . $module . '\\Controller\\' . ($ctlName ?? $this->app->appName) . 'Controller'; |
713 | |
714 | if (!isset($this->running[$class])) { |
715 | if (Autoloader::exists($class) |
716 | || Autoloader::exists($class = '\\Modules\\' . $module . '\\Controller\\Controller') |
717 | ) { |
718 | try { |
719 | /** @var ModuleAbstract $obj */ |
720 | $obj = new $class($this->app); |
721 | $this->running[$class] = $obj; |
722 | } catch (\Throwable $_) { |
723 | $this->running[$class] = new NullModule(); |
724 | } |
725 | } else { |
726 | $this->running[$class] = new NullModule(); |
727 | } |
728 | } |
729 | |
730 | return $this->running[$class]; |
731 | } |
732 | |
733 | /** |
734 | * Initialize all modules for a request. |
735 | * |
736 | * @param RequestAbstract $request Request |
737 | * @param string $ctlName Controller name (null = current app) |
738 | * |
739 | * @return void |
740 | * |
741 | * @since 1.0.0 |
742 | */ |
743 | public function initRequestModules(RequestAbstract $request, string $ctlName = null) : void |
744 | { |
745 | $toInit = $this->getRoutedModules($request); |
746 | foreach ($toInit as $module) { |
747 | $this->initModuleController($module, $ctlName); |
748 | } |
749 | } |
750 | |
751 | /** |
752 | * Get modules that run on this page. |
753 | * |
754 | * @param RequestAbstract $request Request |
755 | * |
756 | * @return string[] |
757 | * |
758 | * @since 1.0.0 |
759 | */ |
760 | public function getRoutedModules(RequestAbstract $request) : array |
761 | { |
762 | $files = $this->getUriLoad($request); |
763 | $modules = []; |
764 | |
765 | if (isset($files['4'])) { |
766 | foreach ($files['4'] as $module) { |
767 | $modules[] = $module['module_load_file']; |
768 | } |
769 | } |
770 | |
771 | return $modules; |
772 | } |
773 | } |