Как дебажить PHP

Материал из Umicms
Версия от 14:03, 21 января 2020; Vitaliks (обсуждение | вклад) (Добавлен раздел получения информации об используемом классе, методе или функции)
Перейти к:навигация, поиск

Отладка ошибки 500 при помощи register_shutdown_function

Иногда причину 500 ошибки можно определить при помощи вывода последней ошибки, которая произошла перед завершением скрипта. Для ее вывода в начало выполняемого PHP файла необходимо добавить следующий код:

register_shutdown_function(function(){
	if (error_get_last()) {
		var_export(error_get_last());
	}
});

Данный код регистрирует функцию, которая будет выполняться по завершении скрипта. В функции происходит вывод последней ошибки, которая была зафиксирована при выполнении скрипта.

Документация по register_shutdown_function

Вывод данных отладки в браузере

Наиболее простой способ вывести любую переменную это функции print_r(), var_dump() и var_export()

print_r() - выводит структуру переменной в удобочитаемом виде

Документация по print_r

var_dump() - также выводит тип данных для всех переменных

Документация по var_dump

var_export() - выводит структуру переменно в формате пригодным для выполнения в PHP

Документация var_export

По умолчанию выводы этих функций используют '\n' в качестве переносов на новую строку.

Так как в браузере эти символы удаляются, то для того, чтобы результат было удобно читать, можно поместить его в тег <pre>

echo '<pre>';
print_r($var);
echo '</pre>';

или так

echo '<pre>' . print_r($var, true) . '</pre>';

В качестве второго аргумента для функций print_r() и var_export() можно указать true, если необходимо, чтобы функция возвращала результат, а не выводила его.

Код:

echo '<pre>';
print_r($variables['user']);
var_dump($variables['user']);
var_export($variables['user']);
echo '</pre>';

Результат вывода:

Array
(
    [id] => 3
    [type] => guest
)
array(2) {
  ["id"]=>
  int(3)
  ["type"]=>
  string(5) "guest"
}
array (
  'id' => 3,
  'type' => 'guest',
)

Иногда необходимо вывести все доступные в текущей области видимости переменные, в этом случае можно воспользоваться функцией get_defined_vars

Документация по get_defined_vars

Вывод данных отладки в файл

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

Для записи данных отладки в файл нам поможет функция file_put_contents, можно использовать ее со следующими аргументами:

file_put_contents("umitest", print_r([__FILE__.' '.__LINE__, $variable], true)."\n", FILE_APPEND | LOCK_EX);

В результате выполнения данной функции рядом с файлом, из которого было запущено выполнение PHP (обычно это index.php в корне сайта), будет создан файл umitest в который запишется имя файла, строка в нем и содержимое переменной $variable.

__FILE__ - это волшебная константа PHP которая всегда содержит имя текущего файла.

__LINE__ - это волшебная константа PHP которая всегда содержит номер строки в файле в котором она была вызвана.

Эти константы рекомендуется добавлять в вывод по нескольким причинам:

1) Удобство отслеживания из какого файла и строки в нем была сделана запись

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

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

LOCK_EX - блокирует файл для записи, чтобы в него не смогли писать другие скрипты.

Можно вывести в файл все переменные, доступные в текущей области видимости:

file_put_contents("umitest", print_r([__FILE__.' '.__LINE__, get_defined_vars()], true)."\n", FILE_APPEND | LOCK_EX);

или только некоторые из них

file_put_contents("umitest", print_r([__FILE__.' '.__LINE__, $var1, $var2], true)."\n", FILE_APPEND | LOCK_EX);

Документация по file_put_contents

Получить стек вызова функции при помощи debug_backtrace

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

$debug_arr = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)

Также в качестве второго аргумента можно указать глубину стека.

Стек вызова можно вывести как в браузер:

echo '<pre>' . print_r(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), true) . '</pre>';

так и записывать в файл:

file_put_contents("umitest", print_r([__FILE__.' '.__LINE__, debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)], true), FILE_APPEND | LOCK_EX);

Документация по debug_backtrace

Получить расположение используемого класса, метода или функции

Стек вызовов дает информацию откуда была вызвана функция, но иногда необходимо узнать где находится используемый в коде класс, метод или функция. Для решения этой задачи в PHP есть специальные классы ReflectionMethod, методы которого дают возможность получить всю необходимую для этого информацию.

Информация о функции

$reflectionMethod = new ReflectionFunction('function_name');
file_put_contents("umitest", print_r([__FILE__.' '.__LINE__, $reflectionMethod->getFileName(), $reflectionMethod->getStartLine()], true), FILE_APPEND | LOCK_EX);

Этот код запишет в файл umitest путь до файла и номер строки в нем в которой находится функция 'function_name'.

Документация по ReflectionFunction

Информация о классе

$reflectionClass = new ReflectionClass('class_name');
file_put_contents("umicheck", print_r([__FILE__.' '.__LINE__, $reflectionClass->getFileName(), $reflectionClass->getStartLine()], true), FILE_APPEND | LOCK_EX);

Этот код запишет в файл umitest путь до файла и номер строки в нем в которой находится класс 'class_name'.

Документация по ReflectionClass

Информация о методе

$reflectionMethod = new ReflectionMethod('class_name', 'method_name');
file_put_contents("umicheck", print_r([__FILE__.' '.__LINE__, $reflectionMethod->getFileName(), $reflectionMethod->getStartLine()], true), FILE_APPEND | LOCK_EX);

Этот код запишет в файл umitest путь до файла и номер строки в нем в которой находится метод 'method_name' класса 'class_name'.

Первым аргументом при создании ReflectionMethod можно передавать не только имя класса, но и его экземпляр, например:

namespace DebugNamespace;

$className = new class_name();
$reflectionMethod = new \ReflectionMethod($className, 'method_name');
file_put_contents("umicheck", print_r([__FILE__.' '.__LINE__, $reflectionMethod->getFileName(), $reflectionMethod->getStartLine()], true), FILE_APPEND | LOCK_EX);

Обратите внимание, что в данном случае в файле задан namespace 'DebugNamespace', поэтому при получении экземпляра класса \ReflectionMethod необходимо наличие косой черты в начале иначе произойдет ошибка: Ошибка (Error): Class 'DebugNamespace\ReflectionMethod' not found

Документация по ReflectionMethod

Время выполнения скрипта

Иногда необходимо проверить, за сколько времени выполняется тот или иной участок кода. Для этого можно воспользоваться следующим кодом:

$start = microtime(true);
// тут код время выполнения которого необходимо посчитать
$stop = microtime(true) - $start;
file_put_contents("umitest", print_r([__FILE__.' '.__LINE__, $stop], true)."\n", FILE_APPEND | LOCK_EX);

Документация по microtime