Не получается загрузить класс через spl_autoload_register, как исправить?

Ссылка скопирована
PHP
1 ответ

Решил сделать свою 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"); 			}

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" ]

^ array:2 [▼ 0 => "Application_Mvc_Extension" 1 => "Application_Controller_Pub_AbstractController" ]

По какой-то причине тут нет "MyAddon_Application_Controller_Pub_Index".
Очень надеюсь, что есть те, кто помогут. Потому что для меня это выглядит как какая-то магия

Структура проекта:

Не получается загрузить класс через spl_autoload_register, как исправить?

Код 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'; 	} }

<?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); 	}  // ...

// ... 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; 	} }

<?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 	{ 	} }

<?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 { } }

Дополнительно:

Вы походу в первый 3енд смотрите. Смотрите в третий, не изобретайте велосипед, ставьте psr4

  • Дмитрий, я бы поставил, да переделывать всё под namespace муторно и хотелось бы сделать своё(изобрести велосипед)
  • Aqulus, думаете ещё не изобрели?)
    Лучше переписать сейчас, чем потом просто с нуля пилить все, попробуйте нэймспейсы, 5.3 умер
  • Дмитрий, переделал под неймспейсы, увы, не помогло.
    Кажись, композер не отрабатывает загрузку классов из вне папки Application, потому что плагины находятся на уровень выше.

    корень/src/addons/
    корень/src/Application/

    корень/src/composer.json:
    {
    "autoload": {
    "psr-4": {
    "Application\": "Application"
    }
    }
    }

    Не знаете, как сделать, чтобы загрузка шла из addons и из Application? Не хотелось бы переносить плагины в Application

  • Aqulus, можно же добавлять папки
  • Дмитрий, Честно говоря, не особо понимаю как. Просто какая-то проблема с загрузчиком или с чем-то другим. Неймспейсы прописаны, но при этом файл аддона не работает.

    1. /src/Application/Extension.php
    2. /src/addons/Application/Controller/Pub/Index.php

    В первом я пытался вызвать класс под номером 2, но он его не видит.
    Всё прописано:
    namespace MyAddonApplicationControllerPub;
    class Index extends XFCP_Index

    Может я что-то не так делаю? Эта проблема уже замучала, 3 дня колупаю, в чём загвоздка и почему он не видит класс. Что с неймспейсами, что с ручной регистрацией через spl_autoload_register - одна проблема

  • Решил проблему тем, что перешёл на неймспейсы и в своём extension.php отдельно(независимо от composer autoloader'a) подключал файлы плагинов.

    spl_autoload_register(function ($class) 				{ 					$path = str_replace('\', '/', Application::getRootDirectory() . '/src/addons/' . $class . '.php');  					if (!file_exists($path)) 					{ 						throw new Exception("Файл расширения {$class} по пути {$path} не найден!"); 					} 					else 					{ 						require $path; 					} 				});

    spl_autoload_register(function ($class) { $path = str_replace('\', '/', Application::getRootDirectory() . '/src/addons/' . $class . '.php'); if (!file_exists($path)) { throw new Exception("Файл расширения {$class} по пути {$path} не найден!"); } else { require $path; } });

    Спасибо Дмитрий !

    • Хм. А прописать единственную строку в composer.json не проще было?
      Ваша функция, конечно, тоже не особо сложная, но одну строку прописать в секции автозагрузки все равно проще =))
    • Сергей delphinpro, не подскажете как? Я пытался добавить так:

      {
      "autoload": {
      "psr-4": {
      "Application\": "Application",
      "addons\": "addons"
      }
      },
      "require-dev": {
      "symfony/var-dumper": "^5.4"
      },
      "require": {
      "ext-pdo": "*"
      }
      }

      Но не сработало. Если вы знаете, то подскажите, пожалуйста

    • Aqulus, Подскажу
      Для начала вот
      PSR-0 или PSR-4, и как правильно построить структуру проекта?
      PHP: автозагрузка классов. Объясните на пальцах на простом примере?

      Допустим имеется вот такая структура

      src   App     Controllers       AnyController.php   Addons     FirstAddon       SomeClass.php

      src App Controllers AnyController.php Addons FirstAddon SomeClass.php

      App – это основное приложение. Все классы в этой папке будут начинаться с неймспейса App. Таким образом в файле AnyController.php будет класс class AppControllersAnyController {}

      Addons - дополнения. Все классы в этой папке будут начинаться с неймспейса Addons. Таким образом в файле SomeClass.php будет класс class AddonsFirstAddonSomeClass {}

      В composer.json пишется следующее

      {   "autoload": {     "psr-4": {       "App\": "src/App",       "Addons\": "src/Addons"     }   }, }

      { "autoload": { "psr-4": { "App\": "src/App", "Addons\": "src/Addons" } }, }

      Выполняется команда composer du, подключается автозагрузчик из папки vendor include 'vendor/autoload.php'; и всё работает

    • Корневые папки пространства имен, те что описываются в composer.json могут называться как угодно.
      src   dpr     core       Controllers         AnyController.php

      src dpr core Controllers AnyController.php

      {   "autoload": {     "psr-4": {       "Delphinpro\App\": "src/dpr/core"     }   }, }

      { "autoload": { "psr-4": { "Delphinpro\App\": "src/dpr/core" } }, }

      главное, что указан корневой неймспейс, и именно он будет использоваться

      файл src/dpr/core/Controllers/AnyController.php содержит класс

      class DelphinproAppControllersAnyController { }

      class DelphinproAppControllersAnyController { }

      src/dpr/core/Controllers/AnyController.php
      class DelphinproAppControllersAnyController

    Нужно решить такую задачу?

    Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.

    Заказать помощь
    Лучший ответ
    1
    Павел Админов Ответ

    Проблема с загрузкой класса через spl_autoload_register может возникать по нескольким причинам. Вот несколько шагов, которые могут помочь исправить эту проблему:

    1. Проверьте путь к файлу класса: Убедитесь, что путь к файлу класса указан правильно в функции spl_autoload_register. Если путь указан неверно, PHP не сможет найти файл и загрузить класс.

    2. Проверьте правильность именования файлов: Убедитесь, что имя файла класса совпадает с именем класса и что расширение файла соответствует используемому языку программирования (например, .php для PHP).

    3. Проверьте пространство имен и автозагрузку: Если вы используете пространства имен в своем коде, убедитесь, что они правильно настроены и соответствуют структуре каталогов. Также проверьте, что автозагрузчик корректно обрабатывает имена классов с учетом пространства имен.

    4. Проверьте наличие файла с классом: Убедитесь, что файл с классом существует в указанном пути и что он доступен для чтения. Если файла нет или у вас нет прав на его чтение, PHP не сможет загрузить класс.

    5. Проверьте ошибки и логи: Проверьте журнал ошибок PHP на наличие сообщений об ошибках при попытке загрузить класс. Это может помочь вам определить причину проблемы и исправить ее.

    Если после выполнения этих шагов проблема с загрузкой класса через spl_autoload_register все еще не решена, попробуйте использовать другие методы автозагрузки классов, такие как Composer или простое подключение файлов с классами вручную.

    Другие ответы (0)

    Пока нет других ответов. Будьте первым, кто поможет автору.

    Ответить на вопрос

    комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *

    Вам также может быть интересно