Выбрасывание исключений в автозагрузчике SPL?

Есть ли способ выбросить исключения из автозагрузчика SPL в PHP в случае его отказа? Он не работает в PHP 5.2.11.

class SPLAutoLoader{ public static function autoloadDomain($className) { if(file_exists('test/'.$className.'.class.php')){ require_once('test/'.$className.'.class.php'); return true; } throw new Exception('File not found'); } } //end class //start spl_autoload_register( array('SPLAutoLoader', 'autoloadDomain') ); try{ $domain = new foobarDomain(); }catch(Exception $c){ echo 'File not found'; } 

Когда вызывается вышеупомянутый код, нет никаких признаков исключения, вместо этого я получаю стандарт «Неустранимая ошибка: класс» foobarDomain «не найден в bla». И выполнение скрипта завершается.

Это не ошибка, это дизайнерское решение :

Примечание . Исключения, сброшенные в функцию __autoload не могут быть захвачены в блоке catch и приводят к фатальной ошибке.

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

Если вы хотите проверить, можете ли вы создать экземпляр класса, используйте class_exists и передайте true в качестве второго аргумента (или оставьте его, значение по умолчанию – по умолчанию):

 if (class_exists('foobarDomain', $autoload = true)) { $domain = new foobarDomain(); } else { echo 'Class not found'; } 

Согласно комментариям в документации для spl_autoload_register , можно вызвать другую функцию из автозагрузчика, которая, в свою очередь, будет генерировать исключение.

 class SPLAutoLoader{ public static function autoloadDomain($className) { if(file_exists('test/'.$className.'.class.php')){ require_once('test/'.$className.'.class.php'); return true; } self::throwFileNotFoundException(); } public static function throwFileNotFoundException() { throw new Exception('File not found'); } } //end class //start spl_autoload_register( array('SPLAutoLoader', 'autoloadDomain') ); try{ $domain = new foobarDomain(); }catch(Exception $c){ echo 'File not found'; } 

Вот полноценный заводский объект, который демонстрирует автоматическую загрузку, поддержку пространств имен, вызывающие вызовы из нестатических экземпляров (с переменными путями), обработку ошибок загрузки и пользовательских исключений.

 abstract class AbstractFactory implements \ArrayAccess { protected $manifest; function __construct($manifest) { $this->manifest = $manifest; } abstract function produce($name); public function offsetExists($offset) { return isset($this->manifest[$offset]); } public function offsetGet($offset) { return $this->produce($offset); } //implement stubs for other ArrayAccess funcs } abstract class SimpleFactory extends AbstractFactory { protected $description; protected $path; protected $namespace; function __construct($manifest, $path, $namespace = "jj\\") { parent::__construct($manifest); $this->path = $path; $this->namespace = $namespace; if (! spl_autoload_register(array($this, 'autoload'), false)) //throws exceptions on its own, but we want a custom one throw new \RuntimeException(get_class($this)." failed to register autoload."); } function __destruct() { spl_autoload_unregister(array($this, 'autoload')); } public function autoload($class_name) { $file = str_replace($this->namespace, '', $class_name); $filename = $this->path.$file.'.php'; if (file_exists($filename)) try { require $filename; //TODO add global set_error_handler and try clause to catch parse errors } catch (Exception $e) {} //autoload exceptions are not passed by design, nothing to do } function produce($name) { if (isset($this->manifest[$name])) { $class = $this->namespace.$this->manifest[$name]; if (class_exists($class, $autoload = true)) { return new $class(); } else throw new \jj\SystemConfigurationException('Factory '.get_class($this)." was unable to produce a new class {$class}", 'SYSTEM_ERROR', $this); //an example of a custom exception with a string code and data container } else throw new LogicException("Unknown {$this->description} {$name}."); } function __toString() //description function if custom exception class wants a string explanation for its container { return $this->description." factory ".get_class($this)."(path={$this->path}, namespace={$this->namespace}, map: ".json_encode($this->manifest).")"; } } . abstract class AbstractFactory implements \ArrayAccess { protected $manifest; function __construct($manifest) { $this->manifest = $manifest; } abstract function produce($name); public function offsetExists($offset) { return isset($this->manifest[$offset]); } public function offsetGet($offset) { return $this->produce($offset); } //implement stubs for other ArrayAccess funcs } abstract class SimpleFactory extends AbstractFactory { protected $description; protected $path; protected $namespace; function __construct($manifest, $path, $namespace = "jj\\") { parent::__construct($manifest); $this->path = $path; $this->namespace = $namespace; if (! spl_autoload_register(array($this, 'autoload'), false)) //throws exceptions on its own, but we want a custom one throw new \RuntimeException(get_class($this)." failed to register autoload."); } function __destruct() { spl_autoload_unregister(array($this, 'autoload')); } public function autoload($class_name) { $file = str_replace($this->namespace, '', $class_name); $filename = $this->path.$file.'.php'; if (file_exists($filename)) try { require $filename; //TODO add global set_error_handler and try clause to catch parse errors } catch (Exception $e) {} //autoload exceptions are not passed by design, nothing to do } function produce($name) { if (isset($this->manifest[$name])) { $class = $this->namespace.$this->manifest[$name]; if (class_exists($class, $autoload = true)) { return new $class(); } else throw new \jj\SystemConfigurationException('Factory '.get_class($this)." was unable to produce a new class {$class}", 'SYSTEM_ERROR', $this); //an example of a custom exception with a string code and data container } else throw new LogicException("Unknown {$this->description} {$name}."); } function __toString() //description function if custom exception class wants a string explanation for its container { return $this->description." factory ".get_class($this)."(path={$this->path}, namespace={$this->namespace}, map: ".json_encode($this->manifest).")"; } } 

и, наконец, пример:

 namespace jj; require_once('lib/AbstractFactory.php'); require_once('lib/CurrenciesProvider.php'); //base abstract class for all banking objects that are created class CurrencyProviders extends SimpleFactory { function __construct() { $manifest = array( 'Germany' => 'GermanBankCurrencies', 'Switzerland' => 'SwissBankCurrencies' ); parent::__construct($manifest, __DIR__.'/CurrencyProviders/', //you have total control over relative or absolute paths here 'banks\'); $this->description = 'currency provider country name'; } } 

теперь делай

 $currencies_cache = (new \jj\CurrencyProviders())['Germany']; 

или

 $currencies_cache = (new \jj\CurrencyProviders())['Ukraine']; 

LogicException («Неизвестное название страны-поставщика валюты Украины»)

Если в файле / CurrencyProviders нет файла SwissCurrencies.php /,

\ jj \ SystemConfigurationException ('Factory jj \ CurrencyProviders не удалось создать новый класс банков \ SwissCurrencies. Данные отладки: название страны поставщика валюты jj \ CurrencyProviders (путь = / var / www / hosting / site /…/ CurrencyProviders / , namespace = banks \, map: {"Германия": "GermanBankCurrencies", "Швейцария": "SwissBankCurrencies"} ')

С достаточным усилием эта фабрика может быть расширена для обнаружения ошибок разбора ( как уловить ошибку require () или include () в PHP? ) И передать аргументы конструкторам.