Использование расширяющего класса MySQLi в обработчике ошибок с автозагрузчиком - PullRequest
4 голосов
/ 08 ноября 2011

Я столкнулся с проблемой при использовании следующих комбинаций в 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;
    }
}

Ответы [ 2 ]

1 голос
/ 13 ноября 2011

Вы не можете войти с вашим обработчиком ошибок в базу данных, когда она недоступна.

Это тот случай, если обработчик ошибок вызывается до инициализации базы данных.

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

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

0 голосов
/ 13 декабря 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...