Как и какими средствами находить ошибки в PHP коде?

Алексей Шиманский

При разработке, порой, код не работает так, как задумано или вообще не работает. Сижу, гадаю: что и где не так?

Час посмотрев на код - иду на проф. ресурсы, например Stack Overflow и публикую вопрос "Где здесь ошибка?" или "Почему не работает?"

В итоге часто проблема мелкая, дурацкая опечатка, ошибка в синтаксисе и прочее. Профессионалом так не станешь, если по каждой ерунде бегать по ресурсам. А я хочу им быть.

Вопрос: какие есть способы, чтобы найти ошибки в PHP коде? Какие инструменты, методы, плагины, пути и пр.?

3 ответа

Алексей Шиманский

Вчера всё работало, а сегодня не работает / Код не работает как задумано

или

Debugging (Отладка)

В чем заключается процесс отладки? Что это такое?

Процесс отладки состоит в том, что мы останавливаем выполнения скрипта в любом месте, смотрим, что находится в переменных, в функциях, анализируем и переходим в другие места; ищем те места, где поведение отклоняется от правильного.

Будет рассмотрен пример с PHPStorm, но отладить код можно и в любой другой IDE.

Подготовка

Для начала необходимо, чтобы в PHP имелась библиотека для отладки под названием xdebug. Если её еще нет, то надо скачать на xdebug.org.

Обычно все библиотеки лежат в папке ext внутри папки PHP. Туда и надо поместить dll.

Далее в php.ini прописываем настройки:

[Xdebug]
zend_extension="C:/server/php/ext/php_xdebug.dll" // <!-- тут свой путь до dll!!!
xdebug.default_enable = 1
xdebug.remote_enable = 1
xdebug.remote_handler = "dbgp"
xdebug.remote_host = "localhost"
xdebug.remote_port = 9000
xdebug.auto_trace = 0
-->

Перезагружаем сервер, на всякий случай.

Теперь если в файле .php написать phpinfo(); то можно будет увидеть в самом низу такую картину:

Открываем PHPStorm

  • нажимаем create project from existing files
  • выбираем Web server is installed locally, source files are located under its document root
  • выбираем папку с файлами, и нажав вверху кнопку "Project Root" помечаем папку как корень проекта
  • нажимаем "Next"
  • нажимаем Add new local server

  • вводим имя сервера любое и Web Server root URL. В рассматриваемом примере это http://localhost/testy2
  • нажимаем "Next" и затем "Finish"

Запуск

Для начала в левой части панели с кодом на любой строке можно кликнуть ЛКМ, тем самым поставив точку останова (breakpoint - брейкпойнт). Это то место, где отладчик автоматически остановит выполнение PHP, как только до него дойдёт. Количество breakpoint'ов не ограничено. Можно ставить везде и много.

Если кликнуть ПКМ и во всплывающем меню выбрать Debug (или в верхнем меню - RunDebug), то при первом запуске PHPStorm попросит настроить интерпретатор. Т.е. надо выбрать версию PHP из папки, где он лежит, чтобы шторм знал, какую версию он будет отлаживать.

Теперь можно нажать Debug!!!

В данном случае, т.к. функция вызывается сразу на той же странице, то при нажатии кнопки Debug — отладчик моментально вызовет функцию, выполнение "заморозится" на первом же брейкпойнте. В ином случае, для активации требуется исполнить действие, при котором произойдет исполнение нужного участка кода (клик на кнопку, передача POST-запроса с формы с данными и другие действия).

Цифрами обозначены:

  1. Стэк вызовов, все вложенные вызовы, которые привели к текущему месту кода.
  2. Переменные. На текущий момент строки ниже номера 3 ещё не выполнились, поэтому определена лишь $data
  3. Показывает текущие значения любых переменных и выражений. В любой момент здесь можно нажать на +, вписать имя любой переменной и посмотреть её значение в реальном времени. Например: $data или $nums[0], а можно и $nums[i] и item['test']['data'][$name[5]][$info[$key[1]]] и т.д. На текущий момент строки ниже номера 3 ещё не выполнились, поэтому $sum и $output обозначены красным цветом с надписью "cannot evaluate expression".

Процесс

Для самого процесса используются элементы управления (см. изображение выше, выделено зеленым прямоугольником) и немного из дополнительно (см. изображение выше, выделено оранжевым прямоугольником).

Show Execution Point (Alt+F10) — переносит в файл и текущую линию отлаживаемого скрипта. Например, если файлов много, решили посмотреть что в других вкладках, а потом забыли где у вас отладка :)

Step Over (F8) — делает один шаг, не заходя внутрь функции. Т.е. если на текущей линии есть какая-то функция, а не просто переменная со значением, то при клике данной кнопки, отладчик не будет заходить внутрь неё.

Step Into (F7) — делает шаг. Но в отличие от предыдущей, если есть вложенный вызов (например функция), то заходит внутрь неё.

Step Out (Shift+F8) — выполняет команды до завершения текущей функции. Удобно, если случайно вошли во вложенный вызов и нужно быстро из него выйти, не завершая при этом отладку.

Rerun (Ctrl+F5) — перезапускает отладку.

Resume Program(F9) — продолжает выполнение скрипта с текущего момента. Если больше нет других точек останова, то отладка заканчивается и скрипт продолжает работу. В ином случае работа прерывается на следующей точке останова.

Stop (Ctrl+F2) — завершает отладку.

View Breakpoints (Ctrl+Shift+F8) — просмотр всех установленных брейкпойнтов.

Mute Breakpoints — отключает брейкпойнты.

...

Итак, в текущем коде видно значение входного параметра:

  • $data = "23 24 11 18" — строка с данными через пробел
  • $nums = (4) ["23", "24", "11", "18"] — массив, который получился из входной переменной.

Если нажмем F8 2 раза, то окажемся на строке 7; во вкладках Watches и Variables и в самой странице с кодом увидим, что переменная $sum была инициализирована и её значение равно 0.

Если теперь нажмем F8, то попадем внутрь цикла foreach и, нажимая теперь F8, пока не окончится цикл, можно будет наблюдать на каждой итерации, как значения $num и $sum постоянно изменяются. Тем самым мы можем проследить шаг за шагом весь процесс изменения любых переменных и значений на любом этапе, который интересует.

Дальнейшие нажатия F8 переместят линию кода на строки 11, 12 и, наконец, 15.

Дополнительно

Если нажать на View Breakpoints в левой панели, то можно не только посмотреть все брейкпойнты, но в появившемся окне можно еще более тонко настроить условие, при котором на данной отметке надо остановиться. В функции выше, например, нужно остановиться только когда $sum превысит значение 20.

Это удобно, если останов нужен только при определённом значении, а не всегда (особенно в случае с циклами).


Алексей Шиманский

Сообщения об ошибках PHP

Как на локальном, так и на боевом сервере необходимо читать и обрабатывать все ошибки. Отличие в том, что на локальном сервере нужно настроить вывод ошибок на экран. На боевом — ошибки НЕ выводить на экран, НО нужно писать в лог, где можно их будет прочитать и проанализировать.

Чтобы вывести все ошибки на экран — надо в самом начале скрипта написать:

ini_set('display_errors',1);
error_reporting(E_ALL ^E_NOTICE);

В данном случае эти строки будут сообщать обо всех критических ошибках на экран. Если никаких ошибок не выведется, надо написать:

error_reporting(E_ALL);

Для вывода ошибок синтаксиса нужно исправить в php.ini (или в .htaccess добавить строчку php_flag display_errors 1).

Итог:

Можно сразу видеть:

  • уровень (предупреждение, уведомление, ошибка)
  • полный текст ошибки
  • имя скрипта с ошибкой
  • номер строки в том самом скрипте

Можно перейти в скрипт на указанную строку и проанализировать.

Не знаете английский?

Откройте любой онлайн переводчик и скопируйте туда текст ошибки, заменив заглавные буквы на строчные:

fatal error: uncaught error: call to undefined function getSum() in W:\test\index.php on line 6

Фатальная ошибка: неперехваченная ошибка: вызов <span>неопределенной функции getSum()</span> 
в W:\test\index.php  в строке 6

Прям русским языком говорит: неопределенная функция getSum. Значит вызов есть, а объявления нет и искать надо в указанном направлении.


Алексей Шиманский

Дедовский способ

Не смотря на описанный ниже способ, нужно сразу отметить, что есть замечательные инструменты, которые быстрее помогут обнаружить и исправить ошибки. Одним из них является Интегрированная среда разработки (IDE). Более подробно о ней можно прочитать в вопросе:

Какие есть способы предупреждения ошибок, их нахождения и устранения?

О способе.

Этот способ использовался в стародавние времена, когда как раз писали код, по-сути, в блокнотах. Сейчас он тоже работает, хотя при наличии умных сред разработок и отладчиков — это не самый быстрый и эффективный способ.

Используется банально echo/print_r/var_dump. Иногда с добавлением die(), чтобы код не шёл дальше.

Алгоритм действий:

  1. Пишем echo(ИМЯ_ПЕРЕМЕННОЙ) или print_r(ИМЯ_ПЕРЕМЕННОЙ) в одну из точек скрипта. Смотрим, чему равны значения.
  2. Если значения те, какие и ожидаются - ошибка ниже. Удаляем вывод переменных и пишем его ниже.
  3. Если значения не те, какие ожидались, значит проблема выше. Удаляем вывод переменных и пишем его выше.
  4. Повторяем шаги 2 и(или) 3 пока не дойдем до строк, где вывод переменных на строке выше — дает правильный результат, а ниже — нет.

Пример:

$test1 = 3;
$test2 == 2;
$result = $test1 + $test2;
echo $result;

Ожидаем увидеть 5, но видим 3. Напишем

echo '$test1: '.$test1.', $test2: '.$test2.', $result: '.$result;

и видим

$test1: 3, $test2: , $result: 3

$test1 — правильное значение

$test2 и $result — неверные значения. Особенно у $test2

Значит, минимум, проблема в $test2. Если присмотреться, видно, что поставили случайно вместо знака присваивания — знак равно.

$test2 == 2;
        ^------- лишний знак

Исправляем.

(Заметка) Если код не исполняется в принципе, ничего не выводит, например, то достаточно писать echo 'тестовая_фраза'; в коде и перемещать её выше и выше пока она не появится. Как только надпись появилась, значит понятно, что дело было в строке ниже.

Итог: всё, что нужно — смотреть на необходимую переменную на каждом шаге алгоритма и понять, на каком моменте возникает ошибка.

licensed under cc by-sa 3.0 with attribution.