Решил сделать свою MVC, но без использования Composer Namespace. Решил пойти по пути Zend'a и сделать неймспейсы в названиях классов. Проблема началась тогда, когда я захотел реализовать расширения для MVC, чтобы было легко добавлять "плагины" без влезания в основной код.
Позаимствовав extendClass у движка XenForo, я получил класс для расширений, но вот проблема в том, что при попытке сделать class_exists, я получаю ошибку, что нет такого класса, получается, spl_autoload_register не видит этот класс при попытке сделать class_alias с виртуальным классом.
Ошибка:
Could not find class MyAddon_Application_Controller_Pub_Index when attempting to extend Application_Controller_Pub_Index
Проблема еще заключается в том, что в Extension.php я делаю:
Application::autoload($outputClass);
dump(Application::getAutoloader()->getLoadedClasses());
if (!class_exists($outputClass))
{
throw new \Exception("Could not find class $outputClass when attempting to extend $class");
}
Вывод дампа:
^ array:2 [▼
0 => "Application_Mvc_Extension"
1 => "Application_Controller_Pub_AbstractController"
]
По какой-то причине тут нет "MyAddon_Application_Controller_Pub_Index".
Очень надеюсь, что есть те, кто помогут. Потому что для меня это выглядит как какая-то магия
Структура проекта:
Код autoloader'a:
<?php
class Application_Autoload
{
protected $loadedClasses;
protected static $_instance;
public function __construct()
{
spl_autoload_register([$this, 'autoloader']);
}
public static final function getInstance(): Application_Autoload
{
if (!self::$_instance)
{
self::$_instance = new self();
}
return self::$_instance;
}
public function getLoadedClasses()
{
return $this->loadedClasses;
}
/**
* @throws Exception
*/
public function autoloader($class, $type = null): bool
{
$this->loadedClasses[] = $class;
if (!$class)
{
return false;
}
if (class_exists($class, false) || interface_exists($class, false))
{
return true;
}
if (substr($class, 0, 5) == 'XFCP_')
{
throw new \Exception('Cannot load class using XFCP. Load the class using the correct loader first.');
}
$filename = $this->autoloaderClassToFile($class, $type);
if (!$filename)
{
return false;
}
if (file_exists($filename))
{
include($filename);
return (class_exists($class, false) || interface_exists($class, false));
}
return false;
}
public function autoloaderClassToFile($class, $type)
{
if (preg_match('#[^a-zA-Z0-9_\\\\]#', $class))
{
return false;
}
$srcPath = $type ? '/src/addons/' : '/src/';
return str_replace(['_', '\\'], '/', Application::getRootDirectory()) . $srcPath . str_replace(['_', '\\'], '/', $class) . '.php';
}
}
Код Application.php(точка входа приложения, вызывается start() из index.php):
// ...
public static function getExtensions(): array
{
return [
'Application_Controller_Pub_Index' => [
'MyAddon_Application_Controller_Pub_Index'
]
];
}
/**
* @throws Exception
*/
public static function start($rootDirectory): void
{
set_error_handler(['Application', 'errorHandler']);
register_shutdown_function(['Application', 'fatalHandler']);
set_exception_handler(['Application', 'exceptionHandler']);
ini_set('display_errors', 0);
ini_set('error_reporting', E_ALL);
self::$time = time();
self::$rootDirectory = $rootDirectory;
self::$autoloader = new Application_Autoload();
foreach (Application::getExtensions() as $baseClass => $fakeClass)
{
self::extendClass($baseClass, $fakeClass);
}
self::$router = new Application_Mvc_Router();
}
/**
* @throws Exception
*/
public static function extendClass($extendClass, $fakeClass = null)
{
$a = new Application_Mvc_Extension(self::getExtensions());
return $a->extendClass($extendClass, $fakeClass);
}
public static function autoload($class)
{
return Application_Autoload::getInstance()->autoloader($class);
}
// ...
Код Extension.php:
<?php
class Application_Mvc_Extension
{
protected $classExtensions = [];
public function __construct(array $classExtensions = [])
{
$this->classExtensions = $classExtensions;
}
/**
* @throws Exception
*/
public function extendClass($class, $type = '', $fakeBase = false)
{
if (!$class)
{
return false;
}
$extensions = !empty($this->classExtensions[$class]) ? $this->classExtensions[$class] : [];
if (!$extensions)
{
return $class;
}
$outputClass = $class;
Application::autoload($outputClass);
foreach ($extensions as $baseClass => $extendClass)
{
if (preg_match('/[;,$\/#"\'\.()]/', $extendClass))
{
continue;
}
$proxyClass = 'XFCP_' . $extendClass;
class_alias($outputClass, $proxyClass);
$outputClass = $extendClass;
Application::autoload($outputClass);
if (!class_exists($outputClass))
{
throw new \Exception("Could not find class $outputClass when attempting to extend $class");
}
}
$this->classExtensions[$class] = $outputClass;
Application::autoload($outputClass);
return $outputClass;
}
}
src/addons/MyAddon/Application/Controller/Pub/Index.php:
<?php
class MyAddon_Application_Controller_Pub_Index extends XFCP_Application_Controller_Pub_Index
{
public function __construct()
{
echo 'constructor';
}
public function actionIndex($params)
{
echo 'extend class';
}
}
if (false)
{
class MyAddon_Application_Controller_Pub_Index extends Application_Controller_Pub_Index
{
}
}