Как я могу уловить ВСЕ ошибки в PHP?

Я нашел много попыток для реализации всех ошибок обработки ошибок, и я решил, что могу написать вики-стиль, чтобы, надеюсь, предоставить полное решение, которое я придумал.

Возникает вопрос:

"Как можно поймать, обработать или перехватить ВСЕ типы ошибок в PHP?"

Теперь - это может считаться "переписывать" некоторыми, но я не знаю, что предлагалось комплексное решение.

Ответ 1

Существует ряд уровней ошибок PHP, некоторые из которых требуют установки отдельных обработчиков ошибок, и для того, чтобы поймать каждую ошибку, которую может вызвать PHP, вы должны написать что-то, что охватывает все эти типы ошибок, запуск, "время выполнения" и исключения.

Мое решение поймать каждую (насколько я могу судить) ошибку, которая спускается по трубе:

  • Пара глобальных переменных
  • Метод инициализации
  • 4 'не-OOP' методы обработки ошибок
  • Класс ErrorHandler с методом "AppendError", в котором можно внести изменения в то, как точно выводятся ошибки или нет (в этом случае ошибки просто выгружаются на экран в некотором минимальном HTML из этого метода)

...

// Moved this line to the bottom of the 'file' for usability - 
// I keep each of the above mentioned 'pieces' in separate files.
//$ErrorHandler = new ErrorHandler();

$ErrorCallback = "HandleRuntimeError";
$ExceptionCallback = "HandleException";
$FatalCallback = "HandleFatalError";

$EnableReporting = true;
$ErrorLevel = E_ALL;

function InitializeErrors()
{
    if($GLOBALS["EnableReporting"])
    {
        error_reporting($GLOBALS["ErrorLevel"]);

        if( isset($GLOBALS["ErrorCallback"]) && strlen($GLOBALS["ErrorCallback"]) > 0 )
        {
            set_error_handler($GLOBALS["ErrorCallback"]);

            // Prevent the PHP engine from displaying runtime errors on its own
            ini_set('display_errors',false);
        }
        else
            ini_set('display_errors',true);

        if( isset($GLOBALS["FatalCallback"]) && strlen($GLOBALS["FatalCallback"]) > 0 )
        {
            register_shutdown_function($GLOBALS["FatalCallback"]);

            // Prevent the PHP engine from displaying fatal errors on its own
            ini_set('display_startup_errors',false);
        }
        else
            ini_set('display_startup_errors',true);

        if( isset($GLOBALS['ExceptionCallback']) && strlen($GLOBALS['ExceptionCallback']) > 0 )
            set_exception_handler($GLOBALS["ExceptionCallback"]);
    }
    else
    {
        ini_set('display_errors',0);
        ini_set('display_startup_errors',0);
        error_reporting(0);
    }
}

function HandleRuntimeError($ErrorLevel,$ErrorMessage,$ErrorFile=null,$ErrorLine=null,$ErrorContext=null)
{
    if( isset($GLOBALS['ErrorHandler']))
    {
        //  Pass errors up to the global ErrorHandler to be later inserted into
        // final output at the appropriate time.
        $GLOBALS['ErrorHandler']->AppendError($ErrorLevel,"Runtime Error: " . $ErrorMessage,$ErrorFile,$ErrorLine,$ErrorContext);

        return true;
    }
    else
    {
        PrintError($ErrorLevel,$ErrorMessage,$ErrorFile,$ErrorLine,$ErrorContext);
        return true;
    }
}

function HandleException($Exception)
{
    if( isset($GLOBALS['ErrorCallback']))
    {
        // Parse and pass exceptions up to the standard error callback.
        $GLOBALS['ErrorCallback']($Exception->getCode(), "Exception: " . $Exception->getMessage(), $Exception->getFile(), $Exception->getLine(), $Exception->getTrace());

        return true;
    }
    else
    {       
        PrintError($Exception->getCode(), "Exception: " . $Exception->getMessage(), $Exception->getFile(), $Exception->getLine(), $Exception->getTrace());
        return true;
    }
}

function HandleFatalError()
{
    $Error = error_get_last();

    // Unset Error Type and Message implies a proper shutdown.
    if( !isset($Error['type']) && !isset($Error['message']))
        exit();
    else if( isset($GLOBALS['ErrorCallback']))
    {
        // Pass fatal errors up to the standard error callback.
        $GLOBALS["ErrorCallback"]($Error['type'], "Fatal Error: " . $Error['message'],$Error['file'],$Error['line']);

        return null;
    }
    else
    {
        PrintError($Error['type'], "Fatal Error: " . $Error['message'],$Error['file'],$Error['line']);
        return null;
    }
}

// In the event that our 'ErrorHandler' class is in fact the generator of the error,
// we need a plain-Jane method that will still deliver the message.
function PrintError($ErrorLevel,$ErrorMessage,$ErrorFile=null,$ErrorLine=null,$ErrorContext=null)
{
    if( class_exists("ErrorHandler"))
        $ErrorTypeString = ErrorHandler::ErrorTypeString($ErrorLevel);
    else
        $ErrorTypeString = "$ErrorLevel";

    if( isset($ErrorContext) && !is_array($ErrorContext) && strlen($ErrorContext) > 0 )
        $ErrorContext = str_replace("#", "<br/>\r\n#", $ErrorContext);

    $ReturnValue = "";
    $ReturnValue .= "<div class=\"$ErrorTypeString\" style=\"margin: 10px;\">\r\n";

    $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Error Level:</span> <span class=\"ErrorValue\">$ErrorTypeString</span></p>\r\n";

    if( isset($ErrorFile) && strlen($ErrorFile) > 0 )
        $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">File:</span> <span class=\"ErrorValue\">'$ErrorFile'</span></p>\r\n";

    if( isset($ErrorLine) && strlen($ErrorLine) > 0 )
        $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Line:</span> <span class=\"ErrorValue\">$ErrorLine</span></p>\r\n";

    if( isset($ErrorContext) && is_array($ErrorContext))
        $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Context:</span><span class=\"ErrorValue\">" . var_export($ErrorContext,true) . "</span></p>\r\n";
    else if( isset($ErrorContext) && strlen($ErrorContext) > 0 )
        $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Context:</span><span class=\"ErrorValue\">$ErrorContext</span></p>\r\n";

    $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Message:</span> <span class=\"ErrorValue\">" . str_replace("\r\n","<br/>\r\n",$ErrorMessage) . "</span></p>\r\n";

    $ReturnValue .= "</div>\r\n";

    echo($ReturnValue);
}

class ErrorHandler
{   
    public function AppendError($ErrorLevel,$ErrorMessage,$ErrorFile=null,$ErrorLine=null,$ErrorContext=null)
    {
        // Perhaps evaluate the error level and respond accordingly
        //
        // In the event that this actually gets used, something that might 
        // determine if you're in a production environment or not, or that 
        // determines if you're an admin or not - or something - could belong here.
        // Redirects or response messages accordingly.
        $ErrorTypeString = ErrorHandler::ErrorTypeString($ErrorLevel);

        if( isset($ErrorContext) && !is_array($ErrorContext) && strlen($ErrorContext) > 0 )
            $ErrorContext = str_replace("#", "<br/>\r\n#", $ErrorContext);

        $ReturnValue = "";
        $ReturnValue .= "<div class=\"$ErrorTypeString\" style=\"margin: 10px;\">\r\n";

        $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Error Level:</span> <span class=\"ErrorValue\">$ErrorTypeString</span></p>\r\n";

        if( isset($ErrorFile) && strlen($ErrorFile) > 0 )
            $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">File:</span> <span class=\"ErrorValue\">'$ErrorFile'</span></p>\r\n";

        if( isset($ErrorLine) && strlen($ErrorLine) > 0 )
            $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Line:</span> <span class=\"ErrorValue\">$ErrorLine</span></p>\r\n";

        if( isset($ErrorContext) && is_array($ErrorContext))
            $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Context:</span><span class=\"ErrorValue\">" . var_export($ErrorContext,true) . "</span></p>\r\n";
        else if( isset($ErrorContext) && strlen($ErrorContext) > 0 )
            $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Context:</span><span class=\"ErrorValue\">$ErrorContext</span></p>\r\n";

        $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Message:</span> <span class=\"ErrorValue\">" . str_replace("\r\n","<br/>\r\n",$ErrorMessage) . "</span></p>\r\n";

        $ReturnValue .= "</div>\r\n";

        echo($ReturnValue);
    }

    public static function ErrorTypeString($ErrorType)
    {
        $ReturnValue = "";

        switch( $ErrorType )
        {
            default:
                $ReturnValue = "E_UNSPECIFIED_ERROR"; 
                break;
            case E_ERROR: // 1 //
                $ReturnValue = 'E_ERROR'; 
                break;
            case E_WARNING: // 2 //
                $ReturnValue = 'E_WARNING'; 
                break;
            case E_PARSE: // 4 //
                $ReturnValue = 'E_PARSE'; 
                break;
            case E_NOTICE: // 8 //
                $ReturnValue = 'E_NOTICE'; 
                break;
            case E_CORE_ERROR: // 16 //
                $ReturnValue = 'E_CORE_ERROR'; 
                break;
            case E_CORE_WARNING: // 32 //
                $ReturnValue = 'E_CORE_WARNING'; 
                break;
            case E_COMPILE_ERROR: // 64 //
                $ReturnValue = 'E_COMPILE_ERROR'; 
                break;
            case E_CORE_WARNING: // 128 //
                $ReturnValue = 'E_COMPILE_WARNING'; 
                break;
            case E_USER_ERROR: // 256 //
                $ReturnValue = 'E_USER_ERROR'; 
                break;
            case E_USER_WARNING: // 512 //
                $ReturnValue = 'E_USER_WARNING'; 
                break;
            case E_USER_NOTICE: // 1024 //
                $ReturnValue = 'E_USER_NOTICE'; 
                break;
            case E_STRICT: // 2048 //
                $ReturnValue = 'E_STRICT';
                break;
            case E_RECOVERABLE_ERROR: // 4096 //
                $ReturnValue = 'E_RECOVERABLE_ERROR';
                break;
            case E_DEPRECATED: // 8192 //
                $ReturnValue = 'E_DEPRECATED'; 
                break;
            case E_USER_DEPRECATED: // 16384 //
                $ReturnValue = 'E_USER_DEPRECATED'; 
                break;
        }

        return $ReturnValue;
    }
}

$ErrorHandler = new ErrorHandler();

Теперь - код в сторону...

Чтобы реализовать это, это так же просто, как включение этого файла, и выполнение метода InitializeErrors. Помимо этого, это зависит от вас, что вы хотите делать с ошибками; это просто оболочка для каждой ошибки, которую может генерировать PHP, - и для внесения изменений в то, как обрабатывается любая заданная ошибка, в основном так же просто, как оценить ее в методе "AppendError" и соответственно реагировать.

- Я должен отметить - я применил возвращаемые значения по причинам, которые я не могу объяснить, и я должен пересмотреть свою собственную работу на этом фронте, но я не уверен, что это имеет какое-то отношение к результату.

Ответ 2

Существует три типа обработчиков ошибок:

  • set_exception_handler, чтобы поймать любые иначе неперехваченные исключения.

  • set_error_handler, чтобы поймать "стандартные" ошибки PHP. Мне нравится сначала проверять его на error_reporting, чтобы увидеть, если это ошибка, которую нужно обработать или проигнорировать (я вообще игнорирую уведомления - вероятно, плохо, но это мой выбор) и бросает ErrorException, позволяя обработчику исключений Вывод.

  • register_shutdown_function в сочетании с error_get_last. Проверьте значение ['type'], чтобы увидеть, есть ли оно E_ERROR, E_PARSE или в основном любые фатальные типы ошибок, которые вы хотите поймать. Обычно они обходят set_error_handler, поэтому их можно поймать и захватить. Опять же, я просто бросаю ErrorException, так что все ошибки в конечном итоге обрабатываются одним и тем же обработчиком исключений.

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