Как дебажить PHP
Содержание
Отладка ошибки 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() - выводит структуру переменной в удобочитаемом виде
var_dump() - также выводит тип данных для всех переменных
var_export() - выводит структуру переменно в формате пригодным для выполнения в PHP
По умолчанию выводы этих функций используют '\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);