大家好。我正在为应用程序开发编写我的库。现在有一个关于如何处理错误和异常的严肃问题。决定使用记录错误、致命错误和异常的方法。我注意到一个奇怪的地方,set_error_handler
即使它已经发送了标头,register_shutdown_function
它也可以工作两次。现在的问题是我无法重定向到错误页面:. 该错误是专门在要检查的视图中引发的,即在渲染页面之后。Cannot modify header information - headers already sent by
通常,任务是显示包含详细信息的错误页面或不显示详细信息的错误页面。
如何解决重复错误的问题并在必要时重定向?
<?php
namespace Core;
use Helpers\Config;
use Helpers\File;
use Helpers\Url;
/**
* Class ErrorHandler
* Обработчик ошибок и исключений
* @package Сore
*/
class ErrorHandler
{
/**
* @var array Константа ассоциаций кода ошибок к тексту
*/
const ERRORS = [
E_ERROR => 'ERROR',
E_WARNING => 'WARNING',
E_PARSE => 'PARSE',
E_NOTICE => 'NOTICE',
E_CORE_ERROR => 'CORE_ERROR',
E_CORE_WARNING => 'CORE_WARNING',
E_COMPILE_ERROR => 'COMPILE_ERROR',
E_COMPILE_WARNING => 'COMPILE_WARNING',
E_USER_ERROR => 'USER_ERROR',
E_USER_WARNING => 'USER_WARNING',
E_USER_NOTICE => 'USER_NOTICE',
E_STRICT => 'STRICT',
E_RECOVERABLE_ERROR => 'RECOVERABLE_ERROR',
E_DEPRECATED => 'DEPRECATED',
E_USER_DEPRECATED => 'USER_DEPRECATED',
];
public static $status;
/**
* Регистрация методов управления ошибками и исключениями.
* Включение отображения ошибок
*/
public static function register()
{
ini_set('display_errors', 1);
error_reporting(E_ALL);
# Управление ошибками
set_error_handler([__CLASS__, 'errorHandler']);
# Управление фатальными ошибками
register_shutdown_function([__CLASS__, 'fatalErrorHandler']);
# Управление исключениями
set_exception_handler([__CLASS__, 'exceptionHandler']);
}
/**
* Управление ошибками
* @param $errno Код (номер) ошибки
* @param $errstr Сообщение ошибки
* @param $errfile Файл ошибки
* @param $errline Строка в файле ошикбки
* @return bool Предотвращение дальнейшего выполнения
*/
public static function errorHandler($errno, $errstr, $errfile, $errline)
{
self::show($errno, $errstr, $errfile, $errline);
# Не передается ошибка далее на обработку
return true;
}
/**
* Отображение ошибок на экран
* @param $errno Код (номер) ошибки
* @param $errstr Сообщение ошибки
* @param $errfile Файл ошибки
* @param $errline Строка в файле ошикбки
*/
private static function show($errno, $errstr, $errfile, $errline)
{
http_response_code(self::$status);
echo "[" . date('Y-m-d H:i:s') . "] <b>" . self::getErrorName($errno) .
"</b> в файле {$errfile} на строке <b>{$errline}</b>:<br>{$errstr}<br>";
$message = "[" . date('Y-m-d H:i:s') . "]" . self::getErrorName($errno) .
" в файле {$errfile} на строке {$errline}:" . PHP_EOL . $errstr . PHP_EOL . PHP_EOL;
self::writeLog($message);
}
/**
* Получение наименования класса ошибки по ее коду
* @param $error integer Номер ошибки
* @return string Наименование класса ошибки
*/
private static function getErrorName($error)
{
$error_name = $error;
if (array_key_exists($error, self::ERRORS)) {
$error_name = self::ERRORS[$error];
}
return $error_name;
}
private static function writeLog($message)
{
$errors_config_section = Config::getSettings('logs', 'errors');
$log_file = File::createFile($errors_config_section['filename'], $errors_config_section['extension'], LOG);
File::write($log_file, $message);
header("Location: " . Url::to($errors_config_section['controller'], self::$status));
}
/**
* Управление фатальными ошибками
* @return bool Предотвращение дальнейшего выполнения
*/
public static function fatalErrorHandler(int $status = 500)
{
self::$status = $status;
if (!empty($error = error_get_last()) && $error['type'] && (E_ERROR | E_PARSE | E_COMPILE_ERROR | E_CORE_ERROR)) {
ob_get_clean();
self::show($error['type'], $error['message'], $error['file'], $error['line']);
}
return true;
}
/**
* Управление исключениями
* @param \Exception|\Error $ex Исключение
* @return bool Предотвращение дальнейшего выполнения
*/
public static function exceptionHandler($ex, int $status = 500)
{
self::$status = $status;
self::show(get_class($ex), $ex->getMessage(), $ex->getFile(), $ex->getLine());
return true;
}
}
目前尚不清楚重定向与它有什么关系。
重定向是发生错误时您可以做的最糟糕的事情。
有诸如 HTTP 返回码之类的东西。这东西非常有用和方便。
它告诉客户他的请求是如何结束的。
因此,不应欺骗客户。并且您应该准确给出与页面状态相对应的返回码。
如果在处理请求后,应该请求另一个 URL,那么我们发送 3xx 代码和 Location: 标头。但是如果请求失败,那么唯一正确的响应代码是 500,没有任何重定向。
显示错误页面的代码的不同之处仅在于检查负责站点模式的变量 - 战斗或开发中。取决于此,是否显示相似性。返回码保持不变 - 500,没有重定向。
捕获渲染错误是少数合理的缓冲情况之一。但是不要被缓冲冲昏了头脑——就像任何错误消息一样,不能修改标题信息有助于程序员。并且不应该不必要地堵塞这个错误。也就是说,缓冲应该只在视图中的输出直接开始之前打开。
您需要使用 ob_start() 缓冲视图的渲染,并且只有在一切正常时才将其输出到流中。在这种情况下,不会发生“已经发送的标头”。