Идея написать этот топик возникла, когда я на храбре задал 2 вопроса:
- Вопрос о перехвате предупреждений и вывод ошибок в указанное место шаблона
- Вопрос о перехвате критических ошибок, заставляющие выполнение скрипт только остановиться
По моей карме и добавление в избранное я понял, что они оказались интересные для PHP хабрасообщества. По этой причине я решил оформить решения этих вопросов в виде статьи, да бы людям и поисковикам было проще и комплексно находить нужную информацию.
Если заинтересовались, то подробности под катом…
Причины использования
Пользователю/поисковику, требуется внятно ответить, что на сервере проблемы. Без использования определенного фен-шуя, этого добиться достаточно трудно, а порой невозможно. Тут я проливаю свет на все это, ну и себе заметочку оставляю, так как еще неделю назад я не знал за что взяться, и, наверное, многие новички тоже будут обескуражены.
Описания функций
Данный функционал доступен в PHP для того, что бы обрабатывать ошибки и контролировать вывод. Вот описание их вкусностей и недостатков. Документацию я приводить не буду, я только сошлюсь её страницы и опишу свое мнение. Все что будет приведено это только малая доля, ссылки на соответствующие разделы документации я приведу в конце статьи. Итак встречаем:
— Контроль некритических ошибок: замечания, предупреждения, пользовательские ошибки. В общем все то, что не завершает интерпретацию аварийно.
set_error_handler — Задает определенный пользователем обработчик ошибок.
Нужен для того, что бы писать в лог все такие ошибки. Если её не задать, то в лог это не пишется, а мне вот всегда хочется узнать при каких боевых ситуациях могут вызываться замечания и предупреждения. То есть позволяет автоматически тестировать продукт пользователем и он даже не будет замечать этого.
Если функция не задана, то PHP лишь пытается вывести данные на экран, а если ему и это не дают, то вообще никаких признаков жизни от этих типов ошибок не возникает.
— Контроль, исключений: является ошибкой типа E_ERROR.
set_exception_handler — Задает пользовательский обработчик исключений
Ну не знаю, зачем это вообще было придумано, когда есть то, что описано ниже и просто обработка ошибки типа Exception. Так что сообщаю что оно просто существует. Она перехватывает критическую ошибку «исключение» и позволяет что-то с ней делать. В любом случае скрипт завершается. Её работы по умолчанию лично для меня достаточно (пишет в логи, пытается вывести на экран). Я бы её вообще не переопределял, а то придется в логи о случившимся исключении самому писать.
— Функции контроля вывода: Тут я опишу 3 функции, которые следует знать по разным причинам. Например, для проблем производительности или для проблем вывода заголовков. В нашем случае требуется выводить заголовки ошибок.
ob_start — Включение буферизации вывода
ob_flush — Сброс (отправка) буфера вывода
ob_end_clean — Очищает (стирает) буфер вывода и отключает буферизацию вывода
Если вкратце, то данные функции позволяют записывать все данные выводимые через echo в некий буфер. Это функционал позволяют отправлять заголовки в любой части кода, а так же многие другие вещи, темы которых не относится к данной статье. А если вдруг случилась беда. То можно сбрасывать весь буфер и весь стек (об этом не в этой статье), писать заголовок ошибки и выводить уведомление пользователю.
— Получение последней произошедшей ошибки: её надо использовать вместе с другими перехватывающими функциями, которые тут описаны.
error_get_last — Получение информации о последней произошедшей ошибке
С помощью нее, возможно вернуть последнюю ошибку. Ей очень удобно ловить критические ошибки и код становится оптимальным (появилась с 5.2.х если что).
— Функция которая запускается после того как все отработало: Барабанная дробь.
register_shutdown_function — Регистрирует функцию, которая выполнится по завершении работы скрипта. Завершение может быть штатным или аварийным, без разницы.
Много плюсов и никаких минусов:
- Данная функция не переопределяется, а определяется дополнительно, и определить вы её можете много раз
- Так как она не переопределяется, то все ошибки уже пишутся в лог так или иначе, их не требуется переопределять
- Данная функция, не еще отправляет контент, о чем написано в документации, и соотвественно можно пользоваться функциями буферизации
Многие Объектно Ориентированные люди обрадуются
Что все вышеизложенные функции можно зарегистрировать даже на методы классов, а также, наверняка, на статические методы классов по той же схеме. Правда способ очень не очевиден для глаза обычного не PHP программиста.
Параметр handler требуется задать через массив, с элементами «название класса|объекта», «метод объекта». Устанавливаемый метод обязательно должен быть public. Пример для функции set_error_handler:
<?php class BaseErrorCatcher { public function __construct() { set_error_handler(array($this, 'ErrorCatcher')); echo "друг, я создался\n"; $errorVarArray['real index'] = true; echo $errorVarArray['error index']; } public function ErrorCatcher($errno, $errstr) { echo "друг, да ты вероянто напортачил где-то в этом: $errstr\n"; } } error_reporting(E_ALL | E_STRICT); // включаем замечания у кого они выключены ini_set('display_errors','On'); // ну уж что бы точно вы увидели результат new BaseErrorCatcher(); // начало теста ?>
Результат:
друг, я создался друг, да ты вероянто напортачил где-то в этом: Undefined index: error index
Рабочий пример
Пример работы, архитектура класса для универсального и полного контроля ошибок. Я исследовал этот вопрос очень досконально и составил гибридный метод, из ответов на заданные мною вопросы.
Условия
Есть файл с кодом, который запускается первым или перед кодом в котоом может появиться ошибка и этот файл и все файлы до него 100% отлаженные с невозможностью появления ошибки. Вот такое вот условие, что бы было проще — без ошибок до того пока не пройдут все регистрации вышеизложенных функций. В данном файле описаны данные методики контроля ошибок в комплексе. Контролируется буфер, если ошибка, то сбросить буфер и вывести ошибку.
Код с комментариями
От себя добавлю, что код не тестировал, так как это упрощенная схема того, что у меня в коде, замечания принимаются
<?php class ErrorSupervisor { public function __construct() { // регистрация ошибок set_error_handler(array($this, 'OtherErrorCatcher')); // перехват критических ошибок register_shutdown_function(array($this, 'FatalErrorCatcher')); // создание буфера вывода ob_start(); } public function OtherErrorCatcher($errno, $errstr) { // контроль ошибок: // - записать в лог return false; } public function FatalErrorCatcher() { $error = error_get_last(); if (isset($error)) if($error['type'] == E_ERROR || $error['type'] == E_PARSE || $error['type'] == E_COMPILE_ERROR || $error['type'] == E_CORE_ERROR) { ob_end_clean(); // сбросить буфер, завершить работу буфера // контроль критических ошибок: // - записать в лог // - вернуть заголовок 500 // - вернуть после заголовка данные для пользователя } else { ob_end_flush(); // вывод буфера, завершить работу буфера } else { ob_end_flush(); // вывод буфера, завершить работу буфера } } } // запуск контроллера $errorController = new ErrorSupervisor(); // генерируем контент // изменяем заголовки, в обещм делаем много чего echo "генерация простейшего контента"; // тестируем систему (не тестировал, но у меня в более сложном варианте работает) include 'null'; // запускается OtherErrorCatcher // require 'null'; // запустится FatalErrorCatcher // require 'foobar.php'; // а внутри этого файла вообще ошибка компиляции ?>
Ссылки
Разделы документации
- Функции обработки ошибок
- Функции контроля вывода
- Управление функциями — невзрачно оформлено правда? Без помощи Aco я бы еще очень долго читал документацию и решал задачу.
Другая полезная информация
- Функция echo в PHP может выполняться более 1 секунды
- Предопределенные константы — они же типы ошибок
Спасибо за внимание.
UPD: По советам из комментариев, дополнил класс ErrorSupervisor новой функциональностью, исправил пару заблуждений, добавил дополнительную интересную информацию по теме, немного отладил код
UPD2 Внимание: Товарищ по PHP-разуму написал хорошую статью про битовые операции в PHP как раз к теме данной статьи, советую почитать. Эти знания позволяют более элегантно писать код. Менять текст данной статьи уже не стал для того, что бы сохранился смысл.