Files
rspade_system/app/RSpade/Core/Autoloader.php
root 29c657f7a7 Exclude tests directory from framework publish
Add 100+ automated unit tests from .expect file specifications
Add session system test
Add rsx:constants:regenerate command test
Add rsx:logrotate command test
Add rsx:clean command test
Add rsx:manifest:stats command test
Add model enum system test
Add model mass assignment prevention test
Add rsx:check command test
Add migrate:status command test

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-25 03:59:58 +00:00

265 lines
7.6 KiB
PHP

<?php
/**
* CODING CONVENTION:
* This file follows the coding convention where variable_names and function_names
* use snake_case (underscore_wherever_possible).
*/
namespace App\RSpade\Core;
use RuntimeException;
use App\RSpade\Core\Manifest\Manifest;
class Autoloader
{
// Manifest is now static - no instance needed
/**
* Registered class aliases
*/
protected static $aliases = [];
/**
* Whether the autoloader is registered
*/
protected static $registered = false;
/**
* Whether aliases have been loaded
*/
protected static $aliases_loaded = false;
/**
* Cache of the autoloader class map from manifest
*/
protected static ?array $class_map = null;
/**
* Register the RSX autoloader
*/
public static function register()
{
if (self::$registered) {
return;
}
// Initialize static Manifest and aliases
Manifest::init();
static::__load_aliases();
// Register with SPL after Composer
spl_autoload_register([static::class, 'load'], true, false);
self::$registered = true;
console_debug('AUTOLOADER', 'RSX Autoloader has been registered');
}
/**
* Load a class
*/
public static function load($class)
{
// Remove leading backslash
$requested_class = ltrim($class, '\\');
// Extract simple class name (after last backslash)
$simple_name = substr(strrchr($requested_class, '\\'), 1) ?: $requested_class;
// Try to find the simple class name in the manifest
try {
$metadata = Manifest::php_get_metadata_by_class($simple_name);
// Load the file
$file_path = str_replace('\\', '/', $metadata['file']);
$absolute_path = base_path($file_path);
if (file_exists($absolute_path)) {
require_once $absolute_path;
// If the requested FQCN still doesn't exist but the actual class does,
// create an alias
$actual_fqcn = $metadata['fqcn'];
if (!class_exists($requested_class, false) &&
class_exists($actual_fqcn, false) &&
$requested_class !== $actual_fqcn) {
class_alias($actual_fqcn, $requested_class);
}
return true;
}
} catch (\RuntimeException $e) {
// Class not found in manifest by simple name
}
// Check for special case class patterns that need custom handling
// Check if it's an RSX class by namespace
if (strpos($requested_class, 'Rsx\\') === 0) {
return static::__load_rsx_namespaced_class($requested_class);
}
// Check if it's a non-namespaced RSX class (like Rsx_Controller_Abstract)
if (strpos($requested_class, 'Rsx_') === 0) {
return static::__load_rsx_base_class($requested_class);
}
// Check aliases
if (isset(static::$aliases[$requested_class])) {
return static::__load_aliased_class($requested_class);
}
// Check for non-namespaced classes in the autoloader class map
if (strpos($requested_class, '\\') === false) {
return static::__load_from_class_map($requested_class);
}
// Not our responsibility
return false;
}
/**
* Load an RSX namespaced class
*/
protected static function __load_rsx_namespaced_class($class)
{
// First, try to find the class in the manifest by full class name
$manifest_data = Manifest::get_all();
// Search through all PHP files in the manifest
foreach ($manifest_data as $file_path => $file_info) {
if (!isset($file_info['namespace']) || !isset($file_info['class'])) {
continue;
}
// Check if this is the class we're looking for
$full_class_name = $file_info['namespace'] . '\\' . $file_info['class'];
if ($full_class_name === $class) {
// Found it! Load the file (convert relative to absolute)
// Convert backslashes to forward slashes for path
$file_path = str_replace('\\', '/', $file_path);
$absolute_path = base_path($file_path);
if (file_exists($absolute_path)) {
require_once $absolute_path;
return true;
}
}
}
// Class not found in manifest - return false to let other autoloaders try
return false;
}
/**
* Load RSX base classes
*/
protected static function __load_rsx_base_class($class)
{
// Map of base classes to their locations
$base_classes = [
'Rsx_Controller_Abstract' => 'app/RSpade/Core/Base/Rsx_Controller_Abstract.php',
];
if (isset($base_classes[$class])) {
$file = base_path($base_classes[$class]);
if (file_exists($file)) {
require_once $file;
return true;
}
}
return false;
}
/**
* Load an aliased class
*/
protected static function __load_aliased_class($class)
{
$actual_class = static::$aliases[$class];
// Try to load the actual class
if (class_exists($actual_class, true)) {
class_alias($actual_class, $class);
return true;
}
return false;
}
/**
* Load a class from the autoloader class map
* @param string $class Simple class name without namespace
*/
protected static function __load_from_class_map($class)
{
// Load class map from manifest if not cached
if (static::$class_map === null) {
static::$class_map = Manifest::get_autoloader_class_map();
}
// Check if the class exists in the map
if (!isset(static::$class_map[$class])) {
return false;
}
$fqcns = static::$class_map[$class];
// If multiple FQCNs exist for this simple name, throw an error
if (count($fqcns) > 1) {
$error_msg = "Fatal error: Ambiguous class name '{$class}'. Multiple classes found:\n";
foreach ($fqcns as $fqcn) {
$error_msg .= " - {$fqcn}\n";
}
$error_msg .= 'Please use the fully qualified class name (FQCN) to resolve ambiguity.';
throw new RuntimeException($error_msg);
}
// Single FQCN found, try to load it
$fqcn = $fqcns[0];
// Try to autoload the actual class
if (class_exists($fqcn, true) || interface_exists($fqcn, true) || trait_exists($fqcn, true)) {
// Create an alias for the simple name
class_alias($fqcn, $class);
return true;
}
return false;
}
/**
* Load class aliases from configuration
*/
protected static function __load_aliases()
{
if (static::$aliases_loaded) {
return;
}
// Future: Load from config/rsx.php
static::$aliases = [
// Example: 'UserModel' => 'Rsx\Models\User_Model'
];
static::$aliases_loaded = true;
}
/**
* Refresh the manifest and retry loading
*/
public static function refresh_and_retry($class)
{
// // In development mode, rebuild manifest if class not found
// if (config('app.env') === 'local') {
// Manifest::rebuild();
// return static::load($class);
// }
return false;
}
}