我遇到了在
PHP中使用以下组合的问题:
>自定义类加载器“ClassLoader”,实现为单例并在spl_autoload_register中注册,它执行require_once以包含类.没什么特别的,只是在磁盘上组织类的一些路径.
>扩展mysqli类的数据库类“DB”.它是从一个目前只知道一个实例的工厂创建的.它只是加载正确的配置并提供一些快捷方法.
>自定义错误处理程序“ErrorHandler”,实现为单例,使用DB类将通知和警告记录到数据库.
所有这一切都很好;类加载器加载类,DB类正确执行查询,错误处理程序记录数据库中的错误.
…除非在调用任何数据库操作之前发生错误.
在这种情况下,PHP崩溃并崩溃;没有错误消息或任何东西,甚至没有任何echo或var_dump,甚至没有4xx或5xx代码.
浏览器只报告没有收到任何内容.
我发现了一些“修复”:
>不要在我的“DB”类中扩展mysqli类;这使得它不起作用,但似乎表明扩展mysqli会导致问题.
>全局或在我的ErrorHandler.php类文件的顶部自动加载我自己的“DB”类.
>在ErrorHandler类中获取实例之前,使用require_once显式加载“DB”类.
我仍然可以在ErrorHandler中自动加载其他类,但是当我实例化“DB”时,PHP似乎崩溃了.
从我看到的,唯一合理的解释似乎是mysqli中的某些内容与在ErrorHandler类中首次实例化的范围有关,因为所有有效的修复似乎都共享范围方面,但这似乎没有任何意义上的.
有谁知道这里发生了什么?
类加载器…
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'));
D B…
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("
\n%s: %s in %s on line %d
\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;
}
}