First Commit
This commit is contained in:
Executable
+201
@@ -0,0 +1,201 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user