1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | namespace Suricate; |
||||
6 | |||||
7 | use InvalidArgumentException; |
||||
8 | use Suricate\Cache\Apc as CacheApc; |
||||
9 | use Suricate\Cache\File as CacheFile; |
||||
10 | use Suricate\Cache\Memcache as CacheMemcache; |
||||
11 | use Suricate\Cache\Memcached as CacheMemcached; |
||||
12 | use Suricate\Cache\Redis as CacheRedis; |
||||
13 | use Suricate\Event\EventDispatcher; |
||||
14 | use Suricate\Migrations\MigrationService; |
||||
15 | use Suricate\Session\Native as SessionNative; |
||||
16 | |||||
17 | /** |
||||
18 | * Suricate - Another micro PHP framework |
||||
19 | * |
||||
20 | * @author Mathieu LESNIAK <[email protected]> |
||||
21 | * @copyright 2013-2024 Mathieu LESNIAK |
||||
22 | * @version 0.6.0 |
||||
23 | * @package Suricate |
||||
24 | * |
||||
25 | * @method static \Suricate\App App($newInstance = false) Get instance of App service |
||||
26 | * @method static \Suricate\Cache Cache($newInstance = false) Get instance of Cache service |
||||
27 | * @method static \Suricate\CacheMemcache CacheMemcache($newInstance = false) Get instance of CacheMemcache service |
||||
28 | * @method static \Suricate\CacheMemcached CacheMemcached($newInstance = false) Get instance of CacheMemcached service |
||||
29 | * @method static \Suricate\CacheRedis CacheRedis($newInstance = false) Get instance of CacheRedis service |
||||
30 | * @method static \Suricate\CacheApc CacheApc($newInstance = false) Get instance of CacheApc service |
||||
31 | * @method static \Suricate\CacheFile CacheFile($newInstance = false) Get instance of CacheFile service |
||||
32 | * @method static \Suricate\Curl Curl($newInstance = false) Get instance of Curl service |
||||
33 | * @method static \Suricate\Database Database($newInstance = false) Get instance of Database service |
||||
34 | * @method static \Suricate\Error Error($newInstance = false) Get instance of Error service |
||||
35 | * @method static \Suricate\Event\EventDispatcher EventDispatcher($newInstance = false) Get instance of EventDispatcher service |
||||
36 | * @method static \Suricate\I18n I18n($newInstance = false) Get instance of I18n service |
||||
37 | * @method static \Suricate\Logger Logger($newInstance = false) Get instance of Logger service |
||||
38 | * @method static \Suricate\Request Request($newInstance = false) Get instance of Request service |
||||
39 | * @method static \Suricate\Request Response($newInstance = false) Get instance of Request/Response service |
||||
40 | * @method static \Suricate\Router Router($newInstance = false) Get instance of Router service |
||||
41 | * @method static \Suricate\Session Session($newInstance = false) Get instance of Session service |
||||
42 | * @method static \Suricate\SessionNative SessionNative($newInstance = false) Get instance of Session service |
||||
43 | * @method static \Suricate\SessionCookie SessionCookie($newInstance = false) Get instance of Session service |
||||
44 | * @method static \Suricate\SessionMemcache SessionMemcache($newInstance = false) Get instance of Session service |
||||
45 | * @method static \Suricate\Migrations\MigrationService Migration($newInstance = false) Get instance of Migration service |
||||
46 | */ |
||||
47 | |||||
48 | class Suricate |
||||
49 | { |
||||
50 | const VERSION = '0.6.0'; |
||||
51 | |||||
52 | const CONF_DIR = '/conf/'; |
||||
53 | |||||
54 | private $config = []; |
||||
55 | private $configFile = []; |
||||
56 | |||||
57 | private $useAutoloader = false; |
||||
58 | |||||
59 | private static $servicesContainer; |
||||
60 | private static $servicesRepository; |
||||
61 | |||||
62 | private $servicesList = [ |
||||
63 | 'App' => App::class, |
||||
64 | 'Cache' => Cache::class, |
||||
65 | 'CacheMemcache' => CacheMemcache::class, |
||||
66 | 'CacheMemcached' => CacheMemcached::class, |
||||
67 | 'CacheRedis' => CacheRedis::class, |
||||
68 | 'CacheApc' => CacheApc::class, |
||||
69 | 'CacheFile' => CacheFile::class, |
||||
70 | 'Curl' => Curl::class, |
||||
71 | 'Database' => Database::class, |
||||
72 | 'Error' => Error::class, |
||||
73 | 'EventDispatcher' => EventDispatcher::class, |
||||
74 | 'I18n' => I18n::class, |
||||
75 | 'Logger' => Logger::class, |
||||
76 | 'Request' => Request::class, |
||||
77 | 'Response' => Request::class, |
||||
78 | 'Router' => Router::class, |
||||
79 | 'Session' => Session::class, |
||||
80 | 'SessionNative' => SessionNative::class, |
||||
81 | 'Migration' => MigrationService::class, |
||||
82 | 8 | ]; |
|||
83 | |||||
84 | 8 | /** |
|||
85 | 6 | * Suricate contructor |
|||
86 | * |
||||
87 | * @param array $paths Application paths |
||||
88 | * @param string|array|null $configFile path of configuration file(s) |
||||
89 | 8 | * |
|||
90 | * @SuppressWarnings(PHPMD.StaticAccess) |
||||
91 | 8 | */ |
|||
92 | 8 | public function __construct($paths = [], $configFile = null) |
|||
93 | { |
||||
94 | 8 | if ($configFile !== null) { |
|||
95 | $this->setConfigFile($configFile); |
||||
96 | } |
||||
97 | |||||
98 | // Load helpers |
||||
99 | require_once __DIR__ . DIRECTORY_SEPARATOR . 'Helper.php'; |
||||
100 | |||||
101 | 8 | $this->loadConfig(); |
|||
102 | 8 | $this->setAppPaths($paths); |
|||
103 | 8 | ||||
104 | if ($this->useAutoloader) { |
||||
105 | 8 | // Configure autoloader |
|||
106 | require_once __DIR__ . DIRECTORY_SEPARATOR . 'AutoLoader.php'; |
||||
107 | 8 | AutoLoader::register(); |
|||
108 | 8 | } |
|||
109 | |||||
110 | // Define error handler |
||||
111 | set_exception_handler([Error::class, 'handleException']); |
||||
112 | set_error_handler([Error::class, 'handleError']); |
||||
113 | register_shutdown_function([Error::class, 'handleShutdownError']); |
||||
114 | |||||
115 | 1 | self::$servicesRepository = new Container(); |
|||
116 | |||||
117 | 1 | $this->initServices(); |
|||
118 | } |
||||
119 | |||||
120 | 8 | /** |
|||
121 | * Get app configuration |
||||
122 | 8 | * |
|||
123 | * @return array |
||||
124 | */ |
||||
125 | public function getConfig(): array |
||||
126 | 8 | { |
|||
127 | return $this->config; |
||||
128 | } |
||||
129 | |||||
130 | private function setAppPaths($paths = []) |
||||
131 | { |
||||
132 | 8 | foreach ($paths as $key => $value) { |
|||
133 | $this->config['App']['path.' . $key] = realpath($value); |
||||
134 | 8 | } |
|||
135 | |||||
136 | 8 | return $this; |
|||
137 | 8 | } |
|||
138 | /** |
||||
139 | * Initialize Framework services |
||||
140 | * @return void |
||||
141 | */ |
||||
142 | private function initServices() |
||||
143 | 8 | { |
|||
144 | self::$servicesRepository->setWarehouse($this->servicesList); |
||||
145 | 8 | ||||
146 | 8 | self::$servicesRepository['Request']->parse(); |
|||
147 | 8 | if (isset($this->config['App']['locale'])) { |
|||
148 | 8 | $this->config['I18n'] = [ |
|||
149 | 'locale' => $this->config['App']['locale'] |
||||
150 | ]; |
||||
151 | } |
||||
152 | |||||
153 | // Define constants |
||||
154 | 8 | if (isset($this->config['Constants'])) { |
|||
155 | foreach ( |
||||
156 | $this->config['Constants'] |
||||
157 | as $constantName => $constantValue |
||||
158 | 8 | ) { |
|||
159 | $constantName = strtoupper($constantName); |
||||
160 | 1 | define($constantName, $constantValue); |
|||
161 | } |
||||
162 | } |
||||
163 | 1 | ||||
164 | 1 | // first sync, && init, dependency to Suricate::request |
|||
165 | self::$servicesContainer = clone self::$servicesRepository; |
||||
166 | |||||
167 | foreach (array_keys($this->servicesList) as $serviceName) { |
||||
168 | if (isset($this->config[$serviceName])) { |
||||
169 | 8 | self::$servicesRepository[$serviceName]->configure( |
|||
170 | 8 | $this->config[$serviceName] |
|||
171 | ); |
||||
172 | 1 | ||||
173 | /** |
||||
174 | 1 | TODO : remove sync in service creation |
|||
175 | */ |
||||
176 | self::$servicesContainer = clone self::$servicesRepository; |
||||
177 | 6 | } |
|||
178 | } |
||||
179 | 6 | ||||
180 | 6 | // final sync, repository is complete |
|||
181 | 6 | self::$servicesContainer = clone self::$servicesRepository; |
|||
182 | } |
||||
183 | |||||
184 | public static function hasService(string $serviceName): bool |
||||
185 | 6 | { |
|||
186 | return isset(self::$servicesContainer[$serviceName]); |
||||
187 | } |
||||
188 | |||||
189 | public static function listServices(): array |
||||
190 | { |
||||
191 | 8 | return self::$servicesContainer->getKeys(); |
|||
192 | } |
||||
193 | 8 | ||||
194 | 8 | private function setConfigFile($configFile) |
|||
195 | 6 | { |
|||
196 | 6 | foreach ((array) $configFile as $file) { |
|||
197 | 6 | if (is_file($file)) { |
|||
198 | 6 | $this->configFile[] = $file; |
|||
199 | 6 | } |
|||
200 | } |
||||
201 | |||||
202 | return $this; |
||||
203 | } |
||||
204 | 6 | ||||
205 | 5 | private function parseYamlConfig($filename) { |
|||
206 | 5 | return yaml_parse_file($filename, 0, $ndocs, |
|||
207 | [ |
||||
208 | '!include' => function($value, $tag, $flags) use($filename) |
||||
0 ignored issues
–
show
The parameter
$tag is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||
209 | { |
||||
210 | $directory = dirname($filename); |
||||
211 | return $this->parseYamlConfig("$directory/$value"); |
||||
212 | } |
||||
213 | ]); |
||||
214 | } |
||||
215 | /** |
||||
216 | * Load framework configuration from ini file |
||||
217 | 8 | * @return void |
|||
218 | 8 | */ |
|||
219 | private function loadConfig() |
||||
220 | { |
||||
221 | $userConfig = []; |
||||
222 | if (count($this->configFile)) { |
||||
223 | $userConfig = []; |
||||
224 | foreach ($this->configFile as $configFile) { |
||||
225 | 8 | if (stripos($configFile, 'yml') !== false) { |
|||
226 | $userConfig = array_merge_recursive($userConfig, $this->parseYamlConfig($configFile)); |
||||
227 | } else { |
||||
228 | $userConfig = array_merge_recursive( |
||||
229 | 8 | $userConfig, |
|||
230 | (array) parse_ini_file($configFile, true, INI_SCANNER_TYPED) |
||||
231 | 8 | ); |
|||
232 | 8 | } |
|||
233 | } |
||||
234 | 8 | ||||
235 | // Advanced ini parsing, split key with '.' into subarrays |
||||
236 | 8 | foreach ($userConfig as $section => $configData) { |
|||
237 | 8 | foreach ($configData as $name => $value) { |
|||
238 | 8 | if (stripos($name, '.') !== false) { |
|||
239 | 8 | $subkeys = explode('.', $name); |
|||
240 | unset($userConfig[$section][$name]); |
||||
241 | 8 | $str = |
|||
242 | "['" . implode("']['", $subkeys) . "'] = \$value;"; |
||||
243 | eval("\$userConfig[\$section]" . $str); |
||||
0 ignored issues
–
show
|
|||||
244 | } |
||||
245 | } |
||||
246 | } |
||||
247 | } |
||||
248 | |||||
249 | foreach ($this->getDefaultConfig() as $context => $directives) { |
||||
250 | if (isset($userConfig[$context])) { |
||||
251 | $this->config[$context] = array_merge( |
||||
252 | $directives, |
||||
253 | $userConfig[$context] |
||||
254 | ); |
||||
255 | unset($userConfig[$context]); |
||||
256 | } else { |
||||
257 | $this->config[$context] = $directives; |
||||
258 | } |
||||
259 | } |
||||
260 | |||||
261 | $this->config = array_merge($this->config, $userConfig); |
||||
262 | $this->configureAppMode(); |
||||
263 | } |
||||
264 | |||||
265 | private function configureAppMode() |
||||
266 | { |
||||
267 | $errorReporting = true; |
||||
268 | $errorDumpContext = true; |
||||
269 | 8 | $logLevel = Logger::LOGLEVEL_WARN; |
|||
270 | $logFile = 'php://stdout'; |
||||
271 | |||||
272 | 8 | if (isset($this->config['App']['mode'])) { |
|||
273 | switch ($this->config['App']['mode']) { |
||||
274 | case App::DEVELOPMENT_MODE: |
||||
275 | 8 | $errorReporting = true; |
|||
276 | $errorDumpContext = true; |
||||
277 | $logLevel = Logger::LOGLEVEL_INFO; |
||||
278 | 8 | $logFile = 'php://stdout'; |
|||
279 | break; |
||||
280 | case App::DEBUG_MODE: |
||||
281 | $errorReporting = true; |
||||
282 | 8 | $errorDumpContext = true; |
|||
283 | 8 | $logLevel = Logger::LOGLEVEL_DEBUG; |
|||
284 | 8 | $logFile = 'php://stdout'; |
|||
285 | 8 | break; |
|||
286 | 8 | case App::PRELIVE_MODE: |
|||
287 | $errorReporting = true; |
||||
288 | $errorDumpContext = false; |
||||
289 | $logLevel = Logger::LOGLEVEL_WARN; |
||||
290 | $logFile = 'php://stderr'; |
||||
291 | 8 | break; |
|||
292 | case App::PRODUCTION_MODE: |
||||
293 | $errorReporting = false; |
||||
294 | 8 | $errorDumpContext = false; |
|||
295 | $logLevel = Logger::LOGLEVEL_WARN; |
||||
296 | $logFile = 'php://stderr'; |
||||
297 | break; |
||||
298 | } |
||||
299 | } |
||||
300 | if (isset($this->config['Logger']['level'])) { |
||||
301 | $logLevel = $this->config['Logger']['level']; |
||||
302 | } |
||||
303 | if (isset($this->config['Logger']['logfile'])) { |
||||
304 | $logFile = $this->config['Logger']['logfile']; |
||||
305 | } |
||||
306 | if (isset($this->config['Error']['report'])) { |
||||
307 | 19 | $errorReporting = $this->config['Error']['report']; |
|||
308 | } |
||||
309 | 19 | if (isset($this->config['Error']['dumpContext'])) { |
|||
310 | 1 | $errorDumpContext = $this->config['Error']['dumpContext']; |
|||
311 | } |
||||
312 | |||||
313 | 19 | $this->config['Logger']['level'] = $logLevel; |
|||
314 | $this->config['Logger']['logfile'] = $logFile; |
||||
315 | $this->config['Error']['report'] = $errorReporting; |
||||
316 | $this->config['Error']['dumpContext'] = $errorDumpContext; |
||||
317 | } |
||||
318 | /** |
||||
319 | * Default setup template |
||||
320 | * @return array setup |
||||
321 | */ |
||||
322 | private function getDefaultConfig() |
||||
323 | { |
||||
324 | return [ |
||||
325 | 'Router' => [], |
||||
326 | 'Logger' => [ |
||||
327 | 'enabled' => true |
||||
328 | ], |
||||
329 | 'App' => ['base_uri' => '/'] |
||||
330 | ]; |
||||
331 | } |
||||
332 | |||||
333 | public function run() |
||||
334 | { |
||||
335 | self::$servicesContainer['Router']->doRouting(); |
||||
336 | } |
||||
337 | |||||
338 | public static function __callStatic($name, $arguments) |
||||
339 | { |
||||
340 | if (isset($arguments[0]) && $arguments[0] === true) { |
||||
341 | return clone self::$servicesRepository[$name]; |
||||
342 | } |
||||
343 | |||||
344 | return self::$servicesContainer[$name]; |
||||
345 | } |
||||
346 | |||||
347 | public function registerService(string $serviceName, string $serviceClass): self |
||||
348 | { |
||||
349 | if (isset(self::$servicesContainer[$serviceName])) { |
||||
350 | throw new InvalidArgumentException('Service ' . $serviceName . ' already registered'); |
||||
351 | } |
||||
352 | |||||
353 | self::$servicesContainer->addToWarehouse($serviceName, $serviceClass); |
||||
354 | self::$servicesRepository->addToWarehouse($serviceName, $serviceClass); |
||||
355 | if (isset($this->config[$serviceName])) { |
||||
356 | self::$servicesContainer[$serviceName]->configure( |
||||
357 | $this->config[$serviceName] |
||||
358 | ); |
||||
359 | } |
||||
360 | return $this; |
||||
361 | } |
||||
362 | } |
||||
363 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.