Создание обработчика ошибок для фреймворка
Продолжаем разработку нашего фреймворка. Перед тем как перейти к написанию класса маршрутизатора, давайте создадим вспомогательный класс для обработки ошибок.
В процессе разработки любого веб-приложения на PHP важным аспектом является правильная обработка ошибок. В этом посте мы создадим класс `ErrorHandler`, который будет отвечать за управление ошибками в нашем фреймворке. Этот класс будет обеспечивать логирование ошибок, отображение сообщений об ошибках и поддержку работы с исключениями.
Зачем нужен класс обработчик ошибок?
Этот класс будет необходим для ловли исключений, которые возникают в процессе работы приложения, и для реагирования на них. Он поможет нам улавливать ошибки, которые могут возникнуть при написании кода, и корректно на них реагировать.
Я рекомендую вам ознакомиться с этой статьей на Хабре, которая, хотя и старая, до сих пор актуальна. Ссылку я оставлю в коде.
Создание класса ErrorHandler
В ядре фреймворка создаем класс ErrorHandler. Мы немного расширим его функционал. Например, 404 ошибка в предыдущей версии фреймворка имела свой отдельный шаблон. Теперь мы реализуем стандартный шаблон для страницы 404, который будет выводить сообщение «Страница не найдена» и генерировать код 404.
namespace blog;
class ErrorHandler {
public function __construct()
{
if (DEBUG) {
error_reporting(-1);
} else {
error_reporting(0);
}
set_exception_handler([$this, 'exceptionHandler']);
set_error_handler([$this, 'errorHandler']);
ob_start();
register_shutdown_function([$this, 'fatalErrorHandler']);
}
public function errorHandler($errno, $errstr, $errfile, $errline)
{
$this->logError($errstr, $errfile, $errline);
$this->displayError($errno, $errstr, $errfile, $errline);
}
public function fatalErrorHandler()
{
$error = error_get_last();
if (!empty($error) && $error['type'] & (E_ERROR | E_PARSE | E_COMPILE_ERROR | E_CORE_ERROR)) {
$this->logError($error['message'], $error['file'], $error['line']);
ob_end_clean();
$this->displayError($error['type'], $error['message'], $error['file'], $error['line']);
} else {
ob_end_flush();
}
}
public function exceptionHandler(\Throwable $e)
{
$this->logError($e->getMessage(), $e->getFile(), $e->getLine());
$this->displayError('Исключение', $e->getMessage(), $e->getFile(), $e->getLine(), $e->getCode());
}
protected function logError($message = '', $file = '', $line = '')
{
file_put_contents(
LOGS . '/errors.log',
"[" . date('Y-m-d H:i:s') . "] Текст ошибки: {$message} | Файл: {$file} | Строка: {$line}\n=================\n",
FILE_APPEND);
}
protected function displayError($errno, $errstr, $errfile, $errline, $responce = 500)
{
if ($responce == 0) {
$responce = 404;
}
http_response_code($responce);
if ($responce == 404 && !DEBUG) {
require WWW . '/errors/404.php';
die;
}
if (DEBUG) {
require WWW . '/errors/development.php';
} else {
require WWW . '/errors/production.php';
}
die;
}
}
Настройка обработчиков ошибок
В конструкторе мы используем стандартные функции PHP: set_exception_handler и set_error_handler. Также учтем константу DEBUG, которая имеет значение 1 (включен режим отладки) или 0 (режим продакшена).
if (defined('DEBUG') && DEBUG) {
error_reporting(E_ALL);
} else {
error_reporting(0);
}
Обработка исключений
Теперь создадим метод exceptionHandler, который будет принимать объект исключения и обрабатывать его.
public function exceptionHandler($exception) {
$this->logError($exception->getMessage(), $exception->getFile(), $exception->getLine());
$this->displayError($exception);
}
Логирование и отображение ошибок
У нас будет два метода: logError и displayError.
private function logError($message, $file, $line) {
$logFile = __DIR__ . '/logs/errors.log';
$logContent = "[" . date("Y-m-d H:i:s") . "] Error: $message in $file on line $line\n";
file_put_contents($logFile, $logContent, FILE_APPEND);
}
private function displayError($exception) {
http_response_code(500);
require 'public/errors/production.php'; // Показываем страничку с ошибкой
}
Реализация страницы 404
Создаем страничку 404 в папке public/errors/404.php. В ней будет простое сообщение, информирующее пользователя о том, что страница не найдена.
404 Not Found"; echo "Страница не найдена.";
Тестирование обработчика ошибок
Теперь давайте протестируем созданный класс. В конструкторе класса App создадим его экземпляр:
class App {
public function __construct() {
new ErrorHandler();
}
}
Давайте подробно разберем код!
Основные технологии
- PHP: серверный язык программирования.
- Namespaces: для организации кода и предотвращения конфликтов имен.
- Обработка ошибок и исключений: механизмы для управления ошибками и их логирования.
Структура проекта
/vendor └── /blog ├── ErrorHandler.php /config └── params.php /public ├── index.php └── /errors├── /images └── 404.png├── development.php ├── production.php └── 404.php /tmp └── /logs
- vendor/blog/: Папка, содержащая классы для нашего приложения.
- public/errors/: Папка для хранения шаблонов ошибок.
- tmp/logs/: Папка для хранения логов ошибок.
Разбор кода
1. Класс ErrorHandler
namespace blog;
class ErrorHandler
{
public function __construct()
{
if (DEBUG) {
error_reporting(-1);
} else {
error_reporting(0);
}
set_exception_handler([$this, 'exceptionHandler']);
set_error_handler([$this, 'errorHandler']);
ob_start();
register_shutdown_function([$this, 'fatalErrorHandler']);
}
// ...
}
Объяснение:
- В конструкторе класса
ErrorHandlerмы настраиваем обработку ошибок. Если включен режим отладки (DEBUG), мы выводим все ошибки, иначе — ничего не выводим. - Устанавливаем обработчики для исключений и ошибок, а также начинаем буферизацию вывода с помощью
ob_start(). Это позволяет управлять выводом ошибок.
2. Обработка ошибок
public function errorHandler($errno, $errstr, $errfile, $errline)
{
$this->logError($errstr, $errfile, $errline);
$this->displayError($errno, $errstr, $errfile, $errline);
}
Объяснение:
- Метод
errorHandlerпринимает параметры ошибки и вызывает методы для логирования и отображения ошибки. Это позволяет сохранить информацию об ошибке в логах и показать пользователю понятное сообщение.
3. Логирование ошибок
protected function logError($message = '', $file = '', $line = '')
{
file_put_contents(LOGS .'/errors.log', "[". date('Y-m-d H:i:s') . "] Текст ошибки: {$message} | Файл: {$file} | Строка: {$line}\n=================\n", FILE_APPEND);
}
Объяснение:
- Метод
logErrorзаписывает информацию об ошибке в файлerrors.log. Это полезно для последующего анализа и устранения проблем.
4. Обработка фатальных ошибок
public function fatalErrorHandler()
{
$error = error_get_last();
if (!empty($error)) {
$this->logError($error['message'], $error['file'], $error['line']);
ob_end_clean();
$this->displayError($error['type'], $error['message'], $error['file'], $error['line']);
} else {
ob_end_flush();
}
}
Объяснение:
- Метод
fatalErrorHandlerобрабатывает фатальные ошибки, которые могут произойти в приложении. Он логирует ошибку и отображает сообщение, если необходимо.
5. Обработка исключений
public function exceptionHandler(\Throwable $e)
{
$this->logError($e->getMessage(), $e->getFile(), $e->getLine());
$this->displayError('Исключение', $e->getMessage(), $e->getFile(), $e->getLine(), $e->getCode());
}
Объяснение:
- Метод
exceptionHandlerпринимает объект исключения и логирует его сообщение, файл и строку, где произошло исключение. После этого он вызываетdisplayError, чтобы показать пользователю понятное сообщение об ошибке.
6. Отображение ошибок
public function displayError($errno, $errstr, $errfile, $errline, $response = 500)
{
if ($response == 0) {
$response = 404;
}
http_response_code($response);
if ($response == 404 && !DEBUG) {
require WWW .'/errors/404.php';
die();
}
if (DEBUG) {
require WWW .'/errors/development.php';
} else {
require WWW .'/errors/production.php';
}
die();
}
Объяснение:
- Метод
displayErrorотвечает за показ сообщения об ошибке пользователю, отображая детальную информацию в режиме отладки или очищая информацию для конечных пользователей.
Шаблон 404.php
Важно отметить, что в наше блоге nikolayogreba.ru проекте мы в основном используете шаблон по следующему адресу: https://nikolayogreba.ru/404. Однако иногда может отображаться шаблон по умолчанию, поэтому важно иметь возможность кастомизировать страницу ошибки 404.
Создание файла development.php
Теперь давайте создадим файл development.php в каталоге errors/ с содержимым:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Ошибка</title>
</head>
<body>
<h1>Произошла ошибка</h1>
<p><b>Код ошибки:</b> <?=$errno;?></p>
<p><b>Текст ошибки:</b> <?=$errstr;?></p>
<p><b>Файл, в котором произошла ошибка:</b> <?=$errfile;?></p>
<p><b>Строка, в которой произошла ошибка:</b> <?=$errline;?></p>
</body>
</html>
Объяснение:
- Этот шаблон предназначен для отображения подробной информации об ошибках в режиме разработки. Он показывает код ошибки, текст ошибки, файл и строку, где произошла ошибка, что позволяет разработчикам быстро диагностировать и исправлять проблемы.
Создание файла production.php
Также создадим файл production.php в том же каталоге с содержимым:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Ошибка</title>
</head>
<body>
<h1>Произошла ошибка</h1>
<p><a href="<?= PATH; ?>">Вернуться на главную</a></p>
</body>
</html>
Объяснение:
- Этот шаблон предназначен для конечных пользователей и показывает общее сообщение об ошибке с ссылкой для возврата на главную страницу. Он не раскрывает детали ошибки, что важно для безопасности.
Использование шаблона 404
Если происходит ошибка 404, и режим отладки отключен, загружается шаблон 404. В таких случаях вы можете использовать свой шаблон по умолчанию, а также создать файл 404.php для случаев, когда он не может быть загружен. Вот пример содержимого файла 404.php:
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Ошибка - 404</title>
<style type="text/css">
body {
font-family: Arial, Helvetica, sans-serif;
}
.wrap {
width: 1000px;
margin: 0 auto;
}
.logo {
width: 430px;
position: absolute;
top: 25%;
left: 35%;
}
p a {
color: #eee;
font-size: 13px;
margin-left: 30px;
padding: 5px;
background: #FF3366;
text-decoration: none;
border-radius: .3em;
}
p a:hover {
color: #fff;
}
.footer {
position: absolute;
bottom: 10px;
right: 10px;
font-size: 12px;
color: #aaa;
}
.footer a {
color: #666;
text-decoration: none;
}
</style>
</head>
<body>
<div class="wrap">
<div class="logo">
<img src="/errors/images/404.png" width="500" alt="Ошибка 404" />
<p><a href="<?= PATH; ?>">Вернуться на главную</a></p>
</div>
</div>
<div class="footer">
Дизайн с помощью - <a href="http://w3layouts.com">W3layouts</a>
</div>
</body>
</html>
Объяснение:
- Этот шаблон отображает страницу ошибки 404 с изображением и ссылкой для возврата на главную страницу. Он использует стили для улучшения внешнего вида и удобства пользователя.
Объяснение:
- Этот шаблон отображает страницу ошибки 404 с изображением и ссылкой для возврата на главную страницу. Он использует стили для улучшения внешнего вида и удобства пользователя.
Заключение
Создание класса ErrorHandler для обработки ошибок в вашем фреймворке — это важный шаг к улучшению надежности и пользовательского опыта. Правильная обработка ошибок не только помогает разработчикам находить и устранять проблемы, но и дарит пользователям уверенность в том, что их взаимодействие с вашим приложением будет защищено и контролируемо.
Выбросим исключение для проверки: \public в index.php
throw new Exception("Тестовое исключение");
Таким образом, наш класс обработчика ошибок готов. Теперь, если в коде возникнут проблемы, мы сразу увидим информацию об ошибках в режиме разработки. В следующем уроке мы продолжим работать с функционалом нашего фреймворка.
Если у вас есть вопросы или вы хотите узнать больше о других аспектах разработки фреймворка на PHP, не стесняйтесь задавать их в комментариях!
Добавить комментарий или задать вопрос ツ
Комментариев нет