Трассировка в PHP: пошаговое руководство по отладке
Основные подходы к трассировке PHP
Трассировка (trace) в PHP позволяет отслеживать последовательность вызовов функций, анализировать ошибки и находить узкие места в коде. В зависимости от задачи используются разные методы: от встроенных функций до профессиональных инструментов. Ниже рассмотрены наиболее эффективные и популярные варианты.
Как выполнять глубокую трассировку с помощью Xdebug?
Xdebug - расширение PHP, предоставляющее мощные средства отладки. Оно может генерировать файлы трассировки с подробной информацией о каждом вызове, времени выполнения и использовании памяти. Это наиболее эффективное решение для анализа производительности и ошибок в сложных проектах.
Установка и настройка
Установите Xdebug через менеджер пакетов (PECL, apt, brew) или скомпилируйте. После установки добавьте в php.ini:
zend_extension=xdebug.so
xdebug.mode=debug,trace
xdebug.start_with_request=yes
xdebug.output_dir=/tmp/xdebug
xdebug.trace_output_name=trace.%sпроверка php (проверка установки php)
Теперь каждый запрос будет создавать файл трассировки в указанной папке. Для ручного управления используйте функции:
xdebug_start_trace('/tmp/trace.xt');
// выполняемый код
xdebug_stop_trace();Index php log (логирование в php)
Файл содержит timestamp, уровень вложенности, имя функции, файл и строку, переданные аргументы (ограниченные по умолчанию 4 уровнями).
Типичные проблемы: неправильная конфигурация режима, отсутствие записи в output_dir из-за прав доступа, переполнение диска при большом количестве запросов. Решение - проверять настройки в phpinfo() и очищать папку с трассами.
Как получить стек вызовов при помощи debug_backtrace?
Функция debug_backtrace() возвращает массив с информацией о стеке вызовов на момент её выполнения. Это лёгкий способ трассировки без установки расширений.
function first() { second(); }
function second() { third(); }
function third() {
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
print_r($trace);
}
first();
Php warning session start (предупреждение сессии)
Результат содержит ключи: file, line, function, class, type. С флагом DEBUG_BACKTRACE_PROVIDE_OBJECT также возвращаются объекты. Однако при глубоких стеках (рекурсия) метод может потреблять много памяти.
Ошибки: при использовании без лимита глубины возможен infinite loop при рекурсивных вызовах. В production следует ограничить глубину через настройки xdebug.max_nesting_level или явно прерывать по счётчику.
Как трассировать последнюю ошибку с помощью error_get_last и пользовательского обработчика?
Встроенная функция error_get_last() возвращает информацию о последней произошедшей ошибке. В сочетании с пользовательским обработчиком можно записать полную трассировку.
set_error_handler(function($severity, $message, $file, $line) {
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$log = "Ошибка: $message в $file:$line\n";
$log .= "Стек:\n" . print_r($trace, true);
error_log($log, 3, '/tmp/errors.log');
});
trigger_error('Тестовая ошибка', E_USER_NOTICE);Php view source (просмотр исходного кода php)
Такой подход позволяет записывать трассу в лог без сторонних инструментов. Недостаток - обработчик не перехватывает фатальные ошибки (E_ERROR, E_PARSE), для них нужен register_shutdown_function.
Проблемы: если обработчик сам вызывает ошибку, возникает бесконечная рекурсия. Решение - проверять уровень ошибок и временно отключать обработчик для внутренних операций.
Как использовать библиотеку Whoops для отображения трассировки?
Whoops - это библиотека для отладки, которая красиво отображает ошибки с полным стеком вызовов. Она подходит для разработки и может быть легко интегрирована в любой проект.
require 'vendor/autoload.php';
$whoops = new \Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
$whoops->register();
// далее любой код вызовет красивую страницу с трассой
Whoops поддерживает черный список функций, отображение фреймов, поиск по стеку. Для production обычно используют PlainTextHandler или логирование.
Типичные ошибки: несовместимость с другими обработчиками ошибок, дублирование вывода. Решение - регистрировать Whoops до других обработчиков или использовать prependHandler.
Расширенные примеры с различными сценариями трассировки в PHP.
Пример 1: debug_backtrace в рекурсивной функции
function factorial($n, $depth = 0) {
if ($n <= 1) {
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
echo "Достигнут базовый случай. Глубина стека: $depth\n";
array_shift($trace); // убираем текущий вызов
foreach ($trace as $i => $call) {
echo "#$i {$call['function']}() вызвана в {$call['file']}:{$call['line']}\n";
}
return 1;
}
return $n * factorial($n - 1, $depth + 1);
}
factorial(5);
Результат (сокращённый):
Достигнут базовый случай. Глубина стека: 4
#0 factorial() вызвана в /var/www/test.php:11
#1 factorial() вызвана в /var/www/test.php:11
#2 factorial() вызвана в /var/www/test.php:11
#3 factorial() вызвана в /var/www/test.php:11
#4 {main}() вызвана в /var/www/test.php:14
Пример 2: Xdebug trace с аргументами и временем
xdebug_start_trace('/tmp/trace.html', XDEBUG_TRACE_HTML);
function slowFunction($data) {
usleep(100000); // имитация работы
return substr($data, 0, 10);
}
$result = slowFunction('Длинная строка для теста');
$x = [1,2,3];
array_map(function($v) { return $v*2; }, $x);
xdebug_stop_trace();
Содержимое файла trace.html (открывается в браузере как таблица):
TRACE START [2025-03-15 10:00:00] 0.0010 410248 +strlen → 20 0.0012 410312 +slowFunction( 'Длинная строка для теста' ) /var/www/test.php:6 0.1003 410400 -slowFunction → 'Длинная ст' (0.0991 sec) 0.1004 410480 +array_map( Closure, array(1,2,3) ) /var/www/test.php:9 0.1005 410512 -array_map → array(2,4,6) TRACE END [2025-03-15 10:00:00.1006]
Видно время выполнения каждой функции и размер используемой памяти (в байтах).
Пример 3: Трассировка фатальных ошибок через register_shutdown_function
register_shutdown_function(function() {
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
$trace = "Фатальная ошибка: {$error['message']} в {$error['file']}:{$error['line']}\n";
$trace .= "Стек на момент ошибки:\n";
// debug_backtrace может не работать, используем xdebug_get_function_stack
if (function_exists('xdebug_get_function_stack')) {
$stack = xdebug_get_function_stack();
foreach (array_reverse($stack) as $frame) {
$trace .= "{$frame['function']}() в {$frame['file']}:{$frame['line']}\n";
}
}
file_put_contents('/tmp/fatal_error.log', $trace, FILE_APPEND);
}
});
// вызов несуществующего метода для генерации E_ERROR
$obj = new stdClass;
$obj->undefinedMethod();
Лог-файл получит трассу, показывающую последовательность вызовов до фатальной ошибки (если доступен xdebug_get_function_stack).
Пример 4: Трассировка с помощью phptrace (CLI)
Инструмент phptrace позволяет трассировать работающий PHP-процесс без изменения кода.
# Запускаем PHP скрипт с PID 1234
sudo phptrace -p 1234 -o /tmp/phptrace.log
# Или сразу для выполнения:
phptrace -e 'file_get_contents("/etc/passwd")'
Вывод содержит дату, время, функцию и аргументы (частично). Этот метод полезен для анализа уже работающих процессов.