202 lines
5.6 KiB
PHP
Executable File
202 lines
5.6 KiB
PHP
Executable File
<?php
|
|
|
|
namespace Kernel;
|
|
|
|
use Exception;
|
|
|
|
class ModuleLoader
|
|
{
|
|
protected string $modulesPath;
|
|
protected array $modules = [];
|
|
protected array $loaded = [];
|
|
|
|
public function __construct(string $modulesPath)
|
|
{
|
|
$this->modulesPath = rtrim($modulesPath, '/');
|
|
}
|
|
|
|
/**
|
|
* Registers the autoloader for module classes
|
|
* Pattern: Modules\{Module}\{Class} -> modules/{module}/classes/{Class}.php
|
|
*/
|
|
public function registerAutoload(): void
|
|
{
|
|
spl_autoload_register(function ($class) {
|
|
// Base prefix
|
|
$prefix = 'Modules\\';
|
|
|
|
// Checks if the class uses the prefix
|
|
$len = strlen($prefix);
|
|
if (strncmp($prefix, $class, $len) !== 0) {
|
|
return;
|
|
}
|
|
|
|
// Gets the relative class name without the prefix
|
|
$relativeClass = substr($class, $len);
|
|
|
|
// Separates the module name (first part of the namespace)
|
|
$parts = explode('\\', $relativeClass);
|
|
$moduleName = array_shift($parts);
|
|
|
|
if (empty($moduleName)) {
|
|
return;
|
|
}
|
|
|
|
// Converts the module name to lowercase to match the folder
|
|
$moduleDir = strtolower($moduleName);
|
|
|
|
// Rebuilds the class path
|
|
$classPath = implode('/', $parts);
|
|
|
|
// File path
|
|
$file = $this->modulesPath . '/' . $moduleDir . '/classes/' . $classPath . '.php';
|
|
|
|
// If the file exists, load it
|
|
if (file_exists($file)) {
|
|
require $file;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Loads all modules
|
|
*/
|
|
public function loadAll(): void
|
|
{
|
|
$dirs = glob($this->modulesPath . '/*', GLOB_ONLYDIR);
|
|
|
|
foreach ($dirs as $dir) {
|
|
$moduleName = basename($dir);
|
|
$this->load($moduleName);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads a specific module
|
|
*/
|
|
public function load(string $moduleName): void
|
|
{
|
|
if (isset($this->loaded[$moduleName])) {
|
|
return; // already loaded
|
|
}
|
|
|
|
$modulePath = $this->modulesPath . '/' . $moduleName;
|
|
|
|
// Checks if it exists
|
|
if (!is_dir($modulePath)) {
|
|
throw new Exception("Módulo '{$moduleName}' não encontrado.");
|
|
}
|
|
|
|
// Checks module.json
|
|
$jsonFile = $modulePath . '/module.json';
|
|
|
|
if (!file_exists($jsonFile)) {
|
|
throw new Exception("O módulo '{$moduleName}' não possui um module.json válido.");
|
|
}
|
|
|
|
$config = json_decode(file_get_contents($jsonFile), true);
|
|
|
|
if (!is_array($config)) {
|
|
throw new Exception("module.json inválido no módulo '{$moduleName}'.");
|
|
}
|
|
|
|
$this->modules[$moduleName] = $config;
|
|
|
|
// Load Service Providers
|
|
$this->registerProviders($config);
|
|
|
|
// Load Routes
|
|
$this->registerRoutes($moduleName, $modulePath, $config);
|
|
|
|
// Register Views and Locales
|
|
$this->registerResources($moduleName, $modulePath, $config);
|
|
|
|
$this->loaded[$moduleName] = true;
|
|
}
|
|
|
|
/**
|
|
* Registers Service Providers
|
|
*/
|
|
protected function registerProviders(array $config): void
|
|
{
|
|
if (!isset($config['providers'])) {
|
|
return;
|
|
}
|
|
|
|
foreach ($config['providers'] as $class) {
|
|
if (class_exists($class)) {
|
|
$provider = new $class();
|
|
if (method_exists($provider, 'register')) {
|
|
$provider->register();
|
|
}
|
|
if (method_exists($provider, 'boot')) {
|
|
$provider->boot();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads route files
|
|
*/
|
|
protected function registerRoutes(string $module, string $path, array $config): void
|
|
{
|
|
if (!isset($config['routes'])) {
|
|
return;
|
|
}
|
|
|
|
// Get the application instance to register routes
|
|
$app = \Kernel\Application::getInstance();
|
|
|
|
if (isset($config['routes']['web'])) {
|
|
$file = $path . '/' . $config['routes']['web'];
|
|
if (file_exists($file)) {
|
|
$routeLoader = require $file;
|
|
if (is_callable($routeLoader) && $app) {
|
|
$routeLoader($app);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isset($config['routes']['api'])) {
|
|
$file = $path . '/' . $config['routes']['api'];
|
|
if (file_exists($file)) {
|
|
$routeLoader = require $file;
|
|
if (is_callable($routeLoader) && $app) {
|
|
$routeLoader($app);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registers views and translations
|
|
*/
|
|
protected function registerResources(string $module, string $path, array $config): void
|
|
{
|
|
// Views
|
|
if (isset($config['views'])) {
|
|
$viewsPath = $path . '/' . $config['views'];
|
|
if (is_dir($viewsPath)) {
|
|
View::addPath($module, $viewsPath);
|
|
}
|
|
}
|
|
|
|
// Locales
|
|
if (isset($config['locales'])) {
|
|
$localesPath = $path . '/' . $config['locales'];
|
|
if (is_dir($localesPath)) {
|
|
Translator::addNamespace($module, $localesPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the list of loaded modules
|
|
*/
|
|
public function getModules(): array
|
|
{
|
|
return $this->modules;
|
|
}
|
|
}
|