Изучаем PHP. Тонкости экстремальной отладки скриптов.
Иногда сайт ведёт себя неадекватно. А разработка сайта всё затягивается. Очень трудно разобраться, в чём проблема, когда вообще выпадает Белый лист, и нигде нет никаких ошибок.
Дело в том, что многие проекты находятся на обычных хостингах, администрация которых действует по правилу «меньше знаешь, лучше спишь». Обычно под выполнение скриптов зарезервированы довольно скудные ресурсы. А вывод ошибок подавляется, или перенаправляется. Как же быть в этой ситуации?
Чем нам может помочь PHP при отладке скриптов?
Но, как показывает практика, для разработчиков администраторы предусматривают переопределение многих параметров.
Примечание автора: здесь будут рассмотрены подходы, которые касаются самого языка программирования PHP. Этот способ является более универсальным, чем рассмотрение методов, связанных с конфигурацией определённого серверного П.О.
Что же делать, когда у Вас вместо сайта возникает белый лист, для начала давайте включим отображение ошибок: добавьте следующие строки в начало скрипта, требующего отладки.
<?php
ini_set
(
"error_reporting"
,E_ALL & ~E_NOTICE);
ini_set
(
"display_errors"
,1);
ini_set
(
"error_log"
,
""
);
function
errPrint(){
print_r(error_get_last());
}
register_shutdown_function(
"errPrint"
);
...
?>
Примечание: подход актуален для полного перехвата даже критических ошибок при отладке в PHP версии, равной или выше 5.2. В ином случае обратите внимание на эту часть статьи.
Разберём приведённый выше PHP код:
Описание функции ini_set:
(string) ini_set ($server_var_name,$server_var_value) – устанавливает значение $server_var_value для переменной среды выполнения PHP $server_var_name, в случае удачного присвоения возвращает значение переменной, иначе возвращает false.
Примечание автора: со всеми переменными окружения, значение которых можно переопределить, Вы можете ознакомиться здесь
Для начала мы включаем отображение ошибок ini_set(“display_errors”,1).
После этого мы устанавливаем значение для вывода ошибок “error_reporting”, E_ALL & ~E_NOTICE, равное отображению всех ошибок, но без подсказок E_NOTICE.
Примечание автора: E_NOTICE – подсказки, как «правильно программировать» на усмотрение PHP интерпретатора. Зачастую, подсказки могут быть очень полезны при отладке сайта в целом, но при отладке в критических ситуациях они, в основном, несут малополезную информацию да ещё и в огромном количестве.
Затем мы переопределяем вывод ошибок в стандартный поток браузера посетителя ini_set(“error_log”,””);.
Примечание автора: вторым параметром здесь можно передать абсолютный путь к файлу, доступному на запись, и весь поток ошибок будет перенаправлен в него.
Всё бы хорошо, но есть определённые критические ошибки, связанные с работой самого интерпретатора, которые могут вызвать прерывание в работе скрипта, и не будут выведены в поток обозревателя посетителя. Вот такой загадочный он наш PHP.
Но, благо, с версии 5.2 (о чудо!), заявленная ещё в php 4, функция – перехватчик register_shutdown_function заработала!.
Описание функции register_shutdown_function:
(void) register_shutdown_function ( callback $function ) – принимает имя функции $function, которая выполняется по завершению исполнения кода, даже во время возникновения критической ошибки с прерыванием работы скрипта.
Примечание автора: в данном случае функция $function должна быть определена (или подключена) до передачи в register_shutdown_function.
Определим функцию errPrint, которая распечатает последнюю ошибку print_r(error_get_last()) после завершения скрипта. И передадим название этой функции обработчику register_shutdown_function:
<?php
register_shutdown_function(
"errPrint"
);
?>
Описание функции error_get_last:
(array) error_get_last ( void ) – возвращает в виде массива информацию о последней случившейся ошибке. Массив содержит ключи “type” – тип ошибки, “message” – подробное сообщение об ошибке, “file” – путь к файлу, в котором произошла ошибка, “line” – номер строки, в которой произошла ошибка и номер символа.
А в нашем случае, последняя «случившаяся» ошибка – и есть та самая, которая приводит к остановке выполнения скрипта.
Благодаря такому подходу можно будет «отловить» буквально любую ошибку и получить детальную информацию о ней.
Не забывайте отключать информацию о выводе ошибок в браузер пользователя!
Но, после отладки php скрипта и устранения критической ошибки, не забывайте отключить полностью информацию о выводе ошибок в браузер пользователя. Связано это как с соображениями безопасности (многие ошибки и предупреждения могут доступной для злоумышленника конфиденциальную информацию, например: об абсолютном пути на сервере к Вашим скриптам, их местоположении и т.п.), а так же могут препятствовать нормальной инициализации сессий и работе функций, передающих заголовки header, так и с эстетической точки зрения не стоит выводить много малопонятной информации в браузер посетителя Вашего сайта. Для этого можно подавить полностью вывод ошибок. Или же переопределить вывод ошибок в файл с помощью функции ini_set(“error_log”,”путь к файлу ошибок, доступному на запись”);. При этом желательно, чтобы этот файл находился вне папок Вашего сайта, или доступ к файлу извне был полностью ограничен.
Примечание автора:
Приведём код полного подавления ошибок:
<?php
ini_set
(
"error_reporting"
,0);
ini_set
(
"display_errors"
,0);
ini_set
(
"error_log"
,
"путь к файлу хранения ошибок"
);
?>
Так же для некоторых функций, которые всё равно генерируют ошибки и предупреждения, можно использовать оператор подавления ошибок. Для этого достаточно перед именем функции указать: @, например:
<?php
@
mkdir
(
"/site/cahce"
,0777);
?>
Хотя, настоятельно рекомендую Вам вместо этого «ленивого» способа с помощью условных операторов обработать все ситуации, которые приводят к появлению данной ошибки. Это добавит стабильности и увеличит «запас прочности» Ваших скриптов во время возникновения внештатных ситуаций, и оставит меньше «почвы для размышления» злоумышленникам.
Я всё равно вижу белую страницу!
Здесь может быть несколько причин:
Данный подход универсален для php 5.2 и выше. Для того, чтобы узнать версию Вашего PHP интерпретатора, добавьте в Ваш скрипт вызов функции phpinfo();
В первых строках будет описана версия PHP.
Если версия php ниже 5.2:
Для более ранних версий php Дмитрием Котеровым было разработано гибкое решение по перехвату критических ошибок, с ним Вы можете ознакомиться здесь
Если у Вас версия PHP выше 5.2, но белая страница всё таки продолжает появляться с настроенным выводом ошибок, значит Ваш скрипт автоматически завершается сервером (Вы превышаете ресурсы процессорного времени, происходит не обрабатываемая критическая ситуация в одной из библиотек PHP и т.п.). В данном случае советую Вам обратиться к хостеру за предоставлением логов ошибок и нагрузки.
Но если Вы не нашли ничего полезного и в логах, предоставленных хостером – самая последняя инстанция: пошаговая отладка с помощью функций принудительной остановки exit или die в «проблемных местах» и распечаткой нужной информации обнаружить «неустойчивый код», и переписать его с учётом меньшей нагрузки.
Приведём пример выполнения пошаговой отладки с простой установкой флагов состояний:
<?php
function
add_user_avatar(
$user_id
,
$avatar_name
,
$watermark
){
...
};
echo
"вызов функции add_user_avatar"
;
add_user_avatar(
$user_id
,
$avatar_name
,
$watermark
);
echo
"окончание работы функции add_user_avatar"
;
die
();
?>
Если же Вы увидели белый лист раньше отображения «окончание работы функции add_user_avatar», значит проблему стоит искать именно в ходе выполнения данной функции add_user_avatar. Если же нет, мы «передвигаем» функцию die или exit ниже, обозначая вызов и завершение далее следующих функций подобным способом.
А в следующих статьях мы рассмотрим с Вами, как написать собственный перехватчик ошибок и расширить ресурсы, выделяемые хостингом для работы наших скриптов.