Я столкнулся с проблемой при использовании следующих комбинаций в PHP:
- Пользовательский загрузчик классов "ClassLoader", реализованный как одиночный и зарегистрированный в spl_autoload_register, который выполняет функцию require_once для включенияклассы.Ничего особенного, только несколько путей организации классов на диске.
- Класс базы данных "DB", расширяющий класс mysqli.Он создан на фабрике, которая в настоящее время знает только один экземпляр.Он делает чуть больше, чем загружает правильную конфигурацию и предлагает несколько быстрых методов.
- Пользовательский обработчик ошибок "ErrorHandler", реализованный как одноэлементный, который использует класс DB для регистрации уведомлений и предупреждений в базе данных.
Все это прекрасно работает;загрузчик классов загружает классы, класс DB выполняет запросы правильно, а обработчик ошибок регистрирует ошибки в базе данных.
... За исключением случаев, когда возникает ошибка, прежде чем какие-либо операции с базой данных были вызваны.В этом случае PHP падает и сильно падает;никаких сообщений об ошибках или чего-либо еще, даже никаких эхо или var_dump, даже кодов 4xx или 5xx.Браузер просто сообщает, что ничего не получено.
Я обнаружил ряд "исправлений":
- Не расширяйте класс mysqli в моем классе "DB";это делает его неработоспособным, но, похоже, указывает на то, что расширение mysqli вызывает проблему.
- Автозагрузка моего собственного класса "DB" либо глобально, либо в начале моего файла класса ErrorHandler.php.
- Явно загрузить класс «DB» с помощью require_once непосредственно перед получением экземпляра внутри класса ErrorHandler.
Я все еще могу автоматически загружать другие классы внутри ErrorHandler, но в тот момент, когда я создаю экземпляр «DB», PHP кажетсяк аварийному завершению.
Из того, что я вижу, единственное разумное объяснение, по-видимому, состоит в том, что что-то в mysqli имеет проблемы с возможностью первого создания экземпляра внутри класса ErrorHandler, поскольку все исправления, которые работают, похоже, имеют общий аспект, но это, кажется, не имеет никакого смысла вообще.
Кто-нибудь знает, что здесь происходит?
ClassLoader ...
class ClassLoader {
private $_paths = array();
private function __construct() {
// ... bunch of $this->append() calls with all paths and 3rd party libs
}
private static $_instance = null;
public static function get() {
if (self::$_instance === null) {
self::$_instance = new self();
}
return self::$_instance;
}
public function append($path, $format = '$.class.php') {
if (!array_key_exists($path, $this->_paths)) {
$this->_paths[$path] = $format;
return true;
}
return false;
}
public function autoload($class_name) {
foreach ($this->_paths as $path => $format) {
$file = $path.'/'.str_replace('$', $class_name, $format);
if (file_exists($file)) {
require_once($file);
return true;
}
}
return false;
}
}
$autoloader = ClassLoader::get();
$autoloader->append(dirname(__FILE__).'/classes');
spl_autoload_register(array($autoloader, 'autoload'));
DB ...
class DB extends mysqli {
private static $_instances = array();
public static function get(Config $config) {
$hash = md5(serialize($config));
if (!array_key_exists($hash, self::$_instances)) {
self::$_instances[$hash] = new self($config);
}
return self::$_instances[$hash];
}
private $_prefix = '';
private $_die = false;
public function dieOnError($die) { $this->_die = $die; }
private function __construct(Config $config) {
parent::__construct(
$config->host
, $config->username
, $config->password
, $config->database
);
if ($this->connect_error) {
_report_error($this->connect_errno, $this->connect_error);
}
$this->_prefix = $config->prefix;
}
}
Config - это синглтон с некоторыми общедоступными свойствами.
ErrorHandler
class ErrorHandler extends Object {
/*
* Strip recursion problems in the backtrace
*/
private static function _filter_backtrace($array, $depth = 0) {
$result = array();
foreach ($array as $name => $value) {
switch (gettype($value)) {
case 'object':
case 'unknown type':
case 'resource':
break;
case 'array':
//$result[$name] = self::_filter_backtrace($value);
break;
default:
//$result[$name] = $value;
}
}
return $result;
}
private function _handle_db($errno, $errstr, $errfile, $errline, $backtrace) {
$db = DB::get(Config::get());
$db->dieOnError(true); // prevents infinite loops in error handler
// DB query here
$db->dieOnError(false); // for non-dying.
}
private function __construct() {
}
private static $_instance = null;
public static function get() {
if (self::$_instance === null) {
self::$_instance = new self();
}
return self::$_instance;
}
public function handle($errno, $errstr, $errfile, $errline) {
// No error? Return without reporting
if (!($errno & error_reporting())) {
return;
}
// Type of error
switch ($errno) {
case E_NOTICE:
case E_USER_NOTICE:
$errors = "Notice";
break;
case E_WARNING:
case E_USER_WARNING:
$errors = "Warning";
break;
case E_ERROR:
case E_USER_ERROR:
$errors = "Fatal Error";
break;
default:
$errors = "Unknown";
break;
}
//$backtrace = self::_filter_backtrace(array_shift(debug_backtrace()));
$backtrace = array();
switch (Config::get()->error_log) {
case 'db':
ErrorHandler::_handle_db($errno, $errstr, $errfile, $errline, $backtrace);
break;
default:
// Dump
if (ini_get("display_errors")) {
printf("<br />\n<b>%s</b>: %s in <b>%s</b> on line <b>%d</b><br /><br />\n", $errors, $errstr, $errfile, $errline);
}
// Log
if (ini_get('log_errors')) {
error_log(sprintf("PHP %s: %s in %s on line %d", $errors, $errstr, $errfile, $errline));
}
break;
}
// Exit/return strategy
switch ($errno) {
case E_ERROR:
case E_USER_ERROR:
die();
break;
}
return TRUE;
}
}