
От автора: я опять сегодня не выспался! Вчера отлавливал ошибки в написанном скрипте, а сегодня комар жужжал под ухом всю ночь. И кажется, что это насекомое сродни тому багу, который я прошлой ночью еле нашел. В общем, сегодня (раз уж мне не спится) узнаем о кодах ошибок в PHP.
Если их не видно
Причин, почему ошибки не выводятся на экране, может быть несколько:
Их вывод отключен хостером специально – таким образом он заботится о повышении безопасности своего серверного пространства. В том числе и вашего ресурса. И все потому, что в описаниях некоторых ошибок отладчик выводит конфиденциальную информацию. Умелый хакер сможет легко использовать ее для взлома вашего сайта. Например, при указании неправильного пароля при подключении к базе, расположенной на сервере MySQL. В результате на экран выводится описание ошибки, содержащее логин активного пользователя СУБД.
В настройках ядра языка.
Бесплатный курс по PHP программированию
Освойте курс и узнайте, как создать динамичный сайт на PHP и MySQL с полного нуля, используя модель MVC
В курсе 39 уроков | 15 часов видео | исходники для каждого урока
Все эти «частные» случаи мы рассматривали в одном из наших предыдущих материалов. Но так как нами используется Денвер, то я кратко опишу решение данной проблемы в нем.
Перед тем, как найти ошибку коде PHP, сначала нужно ее вывести на экран. Если это не происходит, тогда прямая дорога в php. ini. Здесь нужно активировать значения нескольких параметров:
Данные настройки расположены в разделе «Error handling and logging».

Логов нет!
Как отмечалось ранее, в большинстве случает на хостингах вывод программных багов на веб-страницах отключен. Но это не значит, что они недоступны для отслеживания и отладки владельцам ресурсов. Все данные об ошибках записываются в файлы логов.
В Денвере данная функция (по умолчанию) отключена. Это можно легко исправить, прописав нужный код в конфигурационном файле (в том же разделе).
Снова откройте php. ini и внесите в него выделенные ниже строки. Точнее, одна из них уже должна быть, но лучше проверить. С помощью этих директив (log_errors и error_log) разрешается запись сообщений обо всех ошибках. А также задается путь к файлу логов, с помощью которого вы сможете осуществить PHP проверку кода на ошибки.
Бесплатный курс по PHP программированию
Освойте курс и узнайте, как создать динамичный сайт на PHP и MySQL с полного нуля, используя модель MVC
В курсе 39 уроков | 15 часов видео | исходники для каждого урока

После редактирования конфигурационного файла перезапустите локальный сервер, чтобы все изменения вступили в силу.
Проверяем
Теперь проверим, что у нас вышло. Для этого запустим умышленную синтаксическую неточность в коде: неправильно объявим переменную PHP.
PHP для начинающих. Обработка ошибок // PHP
О да, в этой статье я поведу свой рассказа об ошибках в PHP, и том как их обуздать.
Ошибки
Разновидности в семействе ошибок
Перед тем как приручать ошибки, я бы рекомендовал изучить каждый вид и отдельно обратить внимание на самых ярких представителей.
Чтобы ни одна ошибка не ушла незамеченной потребуется включить отслеживание всех ошибок с помощью функции error_reporting(), а с помощью директивы display_errors включить их отображение:
Фатальные ошибки
Самый грозный вид ошибок – фатальные, они могут возникнуть как при компиляции, так и при работе парсера или PHP-скрипта, выполнение скрипта при этом прерывается.
E_PARSE
Это ошибка появляется, когда вы допускаете грубую ошибку синтаксиса и интерпретатор PHP не понимает, что вы от него хотите, например если не закрыли фигурную или круглую скобочку:
Или написали на непонятном языке:
Лишние скобочки тоже встречаются, и не важно круглые либо фигурные:
Отмечу один важный момент – код файла, в котором вы допустили parse error не будет выполнен, следовательно, если вы попытаетесь включить отображение ошибок в том же файле, где возникла ошибка парсера то это не сработает:
E_ERROR
Это ошибка появляется, когда PHP понял что вы хотите, но сделать сие не получилось ввиду ряда причин, так же прерывает выполнение скрипта, при этом код до появления ошибки сработает:
Не был найден подключаемый файл:
Было брошено исключение (что это за зверь, расскажу немного погодя), но не было обработано:
При попытке вызвать несуществующий метод класса:
Отсутствия свободной памяти (больше, чем прописано в директиве memory_limit) или ещё чего-нить подобного:
Очень часто происходит при чтении либо загрузки больших файлов, так что будьте внимательны с вопросом потребляемой памяти
Рекурсивный вызов функции. В данном примере он закончился на 256-ой итерации, ибо так прописано в настройках xdebug:
Не фатальные
Данный вид не прерывает выполнение скрипта, но именно их обычно находит тестировщик, и именно они доставляют больше всего хлопот у начинающих разработчиков.
Бывает, если используешь неправильный тип аргументов при вызове функций:
Их очень много, и перечислять все не имеет смысла…
E_NOTICE
Это самые распространенные ошибки, мало того, есть любители отключать вывод ошибок и клепают их целыми днями. Возникают при целом ряде тривиальных ошибок.
Когда обращаются к неопределенной переменной:
Когда обращаются к несуществующему элементу массива:
Когда обращаются к несуществующей константе:
Когда не конвертируют типы данных:
Для избежания подобных ошибок – будьте внимательней, и если вам IDE подсказывает о чём-то – не игнорируйте её:
E_DEPRECATED
Так PHP будет ругаться, если вы используете устаревшие функции (т. е. те, что помечены как deprecated, и в следующем мажорном релизе их не будет):
В моём редакторе подобные функции будут зачёркнуты:
Обрабатываемые
Этот вид, которые разводит сам разработчик кода, я их уже давно не встречал, не рекомендую их вам заводить:
Отдельно стоит отметить E_USER_DEPRECATED – этот вид всё ещё используется очень часто для того, чтобы напомнить программисту, что метод или функция устарели и пора переписать код без использования оной. Для создания этой и подобных ошибок используется функция trigger_error():
Приручение
Для работы с ошибками в PHP существует 3 функции:
-
— устанавливает обработчик для ошибок, которые не обрывают работу скрипта (т. е. для не фатальных ошибок) — получает информацию о последней ошибке — регистрирует обработчик который будет запущен при завершении работы скрипта. Данная функция не относится непосредственно к обработчикам ошибок, но зачастую используется именно для этого
У вас не получится назначить более одной функции для обработки ошибок, хотя очень бы хотелось регистрировать для каждого типа ошибок свой обработчик, но нет – пишите один обработчик, и всю логику отображения для каждого типа описывайте уже непосредственно в нём
С обработчиком, который написан выше есть одна существенная проблема – он не ловит фатальные ошибки, и вместо сайта пользователи увидят лишь пустую страницу, либо, что ещё хуже, сообщение об ошибке. Дабы не допустить подобного сценария следует воспользоваться функцией register_shutdown_function() и с её помощью зарегистрировать функцию, которая всегда будет выполняться по окончанию работы скрипта:
Данная функция будет срабатывать всегда!
Но вернёмся к ошибкам, для отслеживания появления в коде ошибки воспользуемся функцией error_get_last(), с её помощью можно получить информацию о последней выявленной ошибке, а поскольку фатальные ошибки прерывают выполнение кода, то они всегда будут выполнять роль “последних”:
Задание
Дополнить обработчик фатальных ошибок выводом исходного кода файла где была допущена ошибка, а так же добавьте подсветку синтаксиса выводимого кода.
О прожорливости
Проведём простой тест, и выясним – сколько драгоценных ресурсов кушает самая тривиальная ошибка:
В результате запуска данного скрипта у меня получился вот такой результат:
Теперь добавим ошибку в цикле:
Результат ожидаемо хуже, и на порядок (даже на два порядка!):
Вывод однозначен – ошибки в коде приводят к лишней прожорливости скриптов – так что во время разработки и тестирования приложения включайте отображение всех ошибок!
Тестирование проводил на PHP версии 5.6, в седьмой версии результат лучше – 0.0004 секунды против 0.0050 – разница только на один порядок, но в любом случае результат стоит прикладываемых усилий по исправлению ошибок
Где собака зарыта
В PHP есть спец символ «@» – оператор подавления ошибок, его используют дабы не писать обработку ошибок, а положится на корректное поведение PHP в случае чего:
Если вы в такой способ подавляете ошибки, то это уменьшает нагрузку на процессор в сравнении с тем, если вы их просто скрываете (см. сравнительный тест выше), но в любом случае, подавление ошибок это зло
Исключения
В эру PHP4 не было исключений (exceptions), всё было намного сложнее, и разработчики боролись с ошибками как могли, это было сражение не на жизнь, а на смерть… Окунуться в эту увлекательную историю противостояния можете в статье Исключительный код. Часть 1. Стоит ли её читать сейчас? Думаю да, ведь это поможет вам понять эволюцию языка, и раскроет всю прелесть исключений
Исключения — исключительные событие в PHP, в отличии от ошибок не просто констатируют наличие проблемы, а требуют от программиста дополнительных действий по обработке каждого конкретного случая.
К примеру, скрипт должен сохранить какие-то данные в кеш файл, если что-то пошло не так (нет доступа на запись, нет места на диске), генерируется исключение соответствующего типа, а в обработчике исключений принимается решение – сохранить в другое место или сообщить пользователю о проблеме.
В каких случаях стоит применять исключения:
Для иллюстрации первого сценария возьмём уже озвученный пример функции для записи данных в файл – помешать нам может очень много факторов, а для того, чтобы сообщить выше стоящему коду в чем именно была проблема необходимо создать и выбросить исключение:
Соответственно ловить данные исключения будем примерно так:
В данном примере приведен очень простой сценарий обработки исключений, когда у нас любая исключительная ситуация обрабатывается на один манер. Но зачастую – различные исключения требуют различного подхода к обработке, и тогда следует использовать коды исключений и задать иерархию исключений в приложении:
Теперь, если использовать эти исключения то можно получить следующий код:
Важно помнить, что Exception — это прежде всего исключительное событие, иными словами исключение из правил. Не нужно использовать их для обработки очевидных ошибок, к примеру, для валидации введённых пользователем данных (хотя тут не всё так однозначно). При этом обработчик исключений должен быть написан в том месте, где он будет способен его обработать. К примеру, обработчик для исключений вызванных недоступностью файла для записи должен быть в методе, который отвечает за выбор файла или методе его вызывающем, для того что бы он имел возможность выбрать другой файл или другую директорию.
Так, а что будет если не поймать исключение? Вы получите “Fatal Error: Uncaught exception …”. Неприятно.
Чтобы избежать подобной ситуации следует использовать функцию set_exception_handler() и установить обработчик для исключений, которые брошены вне блока try-catch и не были обработаны. После вызова такого обработчика выполнение скрипта будет остановлено:
Ещё расскажу про конструкцию с использованием блока finally – этот блок будет выполнен вне зависимости от того, было выброшено исключение или нет:
Для понимания того, что это нам даёт приведу следующий пример использования блока finally :
Т. е. запомните – блок finally будет выполнен даже в том случае, если вы в блоке catch пробрасываете исключение выше (собственно именно так он и задумывался).
Для вводной статьи информации в самый раз, кто жаждет ещё подробностей, то вы их найдёте в статье Исключительный код 
Задание
Написать свой обработчик исключений, с выводом текста файла где произошла ошибка, и всё это с подсветкой синтаксиса, так же не забудьте вывести trace в читаемом виде. Для ориентира – посмотрите как это круто выглядит у whoops.
PHP7 – всё не так, как было раньше
Так, вот вы сейчас всю информацию выше усвоили и теперь я буду грузить вас нововведениями в PHP7, т. е. я буду рассказывать о том, с чем вы столкнётесь через год работы PHP разработчиком. Ранее я вам рассказывал и показывал на примерах какой костыль нужно соорудить, чтобы отлавливать критические ошибки, так вот – в PHP7 это решили исправить, но как обычно завязались на обратную совместимость кода, и получили хоть и универсальное решение, но оно далеко от идеала. А теперь по пунктам об изменениях:
Интерфейс Throwable практически полностью повторяет нам Exception :
Сложно? Теперь на примерах, возьмём те, что были выше и слегка модернизируем:
В результате ошибку поймаем и выведем:
TypeError – для ошибок, когда тип аргументов функции не совпадает с передаваемым типом:
ArithmeticError – могут возникнуть при математических операциях, к примеру когда результат вычисления превышает лимит выделенный для целого числа:
AssertionError – редкий зверь, появляется когда условие заданное в assert() не выполняется:
При настройках production-серверов, директивы zend. assertions и assert. exception отключают, и это правильно
Задание
Написать универсальный обработчик ошибок для PHP7, который будет отлавливать все возможные исключения.
При написании данного раздела были использованы материалы из статьи Throwable Exceptions and Errors in PHP 7
Отладка
Иногда для отладки кода нужно отследить что происходило с переменной или объектом на определённом этапе, для этих целей есть функция debug_backtrace() и debug_print_backtrace() которые вернут историю вызовов функций/методов в обратном порядке:
В результате выполнения функции debug_print_backtrace() будет выведен список вызовов приведших нас к данной точке:
Assert
Отдельно хочу рассказать о таком экзотическом звере как assert() в PHP, собственно это кусочек контрактной методологии программирования, и дальше я расскажу вам как я никогда его не использовал 
Первый случай – это когда вам надо написать TODO прямо в коде, да так, чтобы точно не забыть реализовать заданный функционал:
В результате выполнения данного кода получим E_WARNING :
PHP7 можно переключить в режим exception, и вместо ошибки будет всегда появляться исключение AssertionError :
Второй вариант использования – это создание некоего подобия TDD, но помните – это лишь подобие. Хотя, если сильно постараться, то можно получить забавный результат, который поможет в тестировании вашего кода:
Третий теоретический вариант – это непосредственно контрактное программирование – когда вы описали правила использования своей библиотеки, но хотите точно убедится, что вас поняли правильно, и в случае чего сразу указать разработчику на ошибку (я вот даже не уверен, что правильно его понимаю, но пример кода вполне рабочий):
Никогда не используйте assert() для проверки входных параметров, ведь фактически assert() интерпретирует строковую переменную (ведёт себя как eval() ), а это чревато PHP-инъекцией. И да, это правильное поведение, т. к. просто отключив assert’ы всё что передаётся внутрь будет проигнорировано, а если делать как в примере выше, то код будет выполняться, а внутрь отключенного assert’a будет передан булевый результат выполнения
Если у вас есть живой опыт использования assert() – поделитесь со мной, буду благодарен. И да, вот вам ещё занимательно чтива по этой теме – PHP Assertions, с таким же вопросом в конце 
Ошибки в PHP

Всем известная истина звучит, что не ошибается только тот, кто ничего не делает. В этой статье мы рассмотрим, какие ошибки бывают, а также кратко рассмотрим функцию error reporting, используемую для контроля отображения errors. Но для начала (in first) изучим каждый вид дефектов отдельно, обратив внимание на наиболее распространенные.
Error в PHP: разновидности
Ошибки в PHP бывают:
Фатальные
Fatal error в PHP — одна из наиболее серьезных проблем. Такие дефекты появляются и при компиляции, и во время работы парсера либо PHP-скрипта. Основной нюанс заключается в том, что происходит прерывание исполнения скрипта.
Ниже рассмотрим основные разновидности фатальных ошибок:
— не удалось обнаружить подключаемый файл PHP;
— было выброшено, но не было обработано исключение;
— разработчик пытался вызвать метод класса, причем данный метод не существует;
— отсутствует свободная память (превышен лимит директивы memory_limit).
Нередко проблема возникает во время чтения либо загрузки больших файлов, поэтому надо быть особенно внимательным в вопросах потребляемой памяти.
Не фатальные
В данном случае выполнение скрипта не прерывается, однако именно эти дефекты часто находят тестировщики программного обеспечения. И именно эти недоработки доставляют наибольшие хлопоты начинающим программистам на PHP.
— обращение к неопределенной переменной;
— обращение к элементу массива, когда элемент не существует;
— обращение к несуществующей константе;
— проблема, возникающая, если не конвертируются типы данных и т. п.
Чтобы избежать таких недоработок, надо быть внимательным, особенно к тому, что подсказывает IDE — игнорировать подсказки точно не стоит;
public function test() <
echo «It’s test for me. It is not fatal error»;
Но вообще тип E_STRICT больше актуален для PHP 5.6, поэтому он практически выпилен из 7-й версии языка.
Пользовательские
Этот «балаган» разводится самим разработчиком. Злоупотреблять такими errors не рекомендуется:
Отдельно надо сказать про E_USER_DEPRECATED — напоминает о том, что метод либо функция устарели, то есть пришло время переписать код. Чтобы создать эту и подобные ошибки, применяется функция trigger_error:

Раз основные разновидности проблем уже были рассмотрены, пришло время дать пояснение относительно работы директивы display_errors:
Error reporting
Функция error reporting является встроенной. Она позволяет контролировать, какие именно errors станут отображаться и сообщаться (reported) разработчику. Не стоит забывать и о том, что в PHP ini существует директива error_reporting, причем во время выполнения функция error_reporting() задает значение этой директивы.
https://webformyself. com/kody-oshibok-v-php-ix-vyvod-i-lovlya-na-zhivca-s-pomoshhyu-nedosypaniya/
https://anton. shevchuk. name/php/php-for-beginners-error-handling/
https://otus. ru/journal/oshibki-v-php/