1: <?php
2: /*
3: * SimpleID
4: *
5: * Copyright (C) Kelvin Mo 2014-2025
6: *
7: * This program is free software; you can redistribute it and/or
8: * modify it under the terms of the GNU General Public
9: * License as published by the Free Software Foundation; either
10: * version 2 of the License, or (at your option) any later version.
11: *
12: * This program is distributed in the hope that it will be useful,
13: * but WITHOUT ANY WARRANTY; without even the implied warranty of
14: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15: * General Public License for more details.
16: *
17: * You should have received a copy of the GNU General Public
18: * License along with this program; if not, write to the Free
19: * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20: *
21: */
22:
23: namespace SimpleID;
24:
25: use \Prefab;
26: use \Base;
27: use Psr\Log\LogLevel;
28:
29: /**
30: * Manages SimpleID modules.
31: *
32: * This is a singleton class which is used to load modules specified in
33: * the configuration file. This class also provides functionality for
34: * calling hooks.
35: */
36: class ModuleManager extends Prefab {
37: /** @var array<string, Module> array of all loaded modules */
38: private $modules = [];
39:
40: /** @var Base */
41: private $f3;
42:
43: /** @var \Psr\Log\LoggerInterface */
44: private $logger;
45:
46: function __construct() {
47: $this->f3 = Base::instance();
48: $this->logger = $this->f3->get('logger');
49: }
50:
51: /**
52: * Loads a module.
53: *
54: * @param string $name the fully qualified class name of the module to load
55: * @return void
56: */
57: public function loadModule($name) {
58: $this->logger->log(LogLevel::INFO, 'SimpleID\ModuleManager->loadModule: ' . $name);
59:
60: if (isset($this->modules[$name])) return;
61:
62: $info = $this->getModuleInfo($name);
63: $module = new $name();
64: if ($module instanceof Module) $this->modules[$name] = $module;
65:
66: if (isset($info['asset_domain'])) {
67: $this->f3->set('UI', $this->f3->get('UI') . ';' . $info['asset_dir']);
68: $this->f3->set('LOCALES', $this->f3->get('LOCALES') . ';' . $info['asset_dir']);
69: }
70: }
71:
72: /**
73: * Returns whether a specified module is loaded.
74: *
75: * @param string $name the fully qualified class name of the module
76: * @return bool true if the module is loaded
77: */
78: public function isModuleLoaded($name) {
79: return isset($this->modules[$name]);
80: }
81:
82: /**
83: * Returns a specified loaded module.
84: *
85: * @param string $name the fully qualified class name of the module to return
86: * @return Module the module
87: */
88: public function getModule($name) {
89: return $this->modules[$name];
90: }
91:
92: /**
93: * Returns a list of loaded modules
94: *
95: * @return array<string> an array of fully qualified class names of the loaded
96: * modules
97: */
98: public function getModules() {
99: return array_keys($this->modules);
100: }
101:
102: /**
103: * Initialises the loaded modules.
104: *
105: * @return void
106: */
107: public function initModules() {
108: $listeners = \Listeners::instance();
109:
110: foreach ($this->modules as $name => $module) {
111: if (method_exists($name, 'init')) {
112: $this->logger->log(LogLevel::DEBUG, 'SimpleID\ModuleManager->initModules: ' . $name);
113: call_user_func([ $name, 'init' ], $this->f3);
114: }
115: $listeners->map($module);
116: }
117: }
118:
119: /**
120: * Retrieves information on a SimpleID module
121: *
122: * @param string $class the fully qualified class name of the module
123: * @return array<string, string>
124: */
125: public function getModuleInfo($class) {
126: $loader = $this->f3->get('class_loader');
127: $root_dir = strtr(dirname(__DIR__), '\\', '/'); // Cross-platform way of getting a parent directory
128:
129: $results = [];
130:
131: /** @var non-empty-string $class_file */
132: $class_file = realpath($loader->findFile($class));
133: $class_dir = strtr(dirname($class_file), '\\', '/');
134:
135: if (strncmp($root_dir, $class_dir, strlen($root_dir)) === 0) {
136: $relative_dir = substr($class_dir, strlen($root_dir) + 1);
137: $segments = explode('/', $relative_dir, 3);
138:
139: switch ($segments[0]) {
140: case 'core':
141: break;
142: case 'upgrade':
143: $results['asset_dir'] = $segments[0] . '/';
144: $results['asset_domain'] = $segments[0];
145: break;
146: case 'site':
147: $results['asset_dir'] = $segments[0] . '/' . $segments[1] . '/';
148: $results['asset_domain'] = $segments[1];
149: break;
150: }
151: }
152:
153: $results['file'] = $class_file;
154: $results['dir'] = $class_dir;
155:
156: return $results;
157: }
158: }
159:
160: ?>