Практическое получение стека вызовов при отладке PHP

Раздел: Разработка на PHP -> Отладка

Стек вызовов (stack trace) показывает последовательность функций, приведшую к текущему месту исполнения. Это незаменимый инструмент при отладке сложных ошибок.

Основные методы получения стека вызовов

Как получить массив с полной информацией о стеке вызовов?

Основной способ - функция debug_backtrace. Она возвращает массив, каждый элемент которого содержит имя файла, строку, класс, функцию, аргументы и т.д. Для получения объектов (если нужно) используется флаг DEBUG_BACKTRACE_PROVIDE_OBJECT, а для игнорирования аргументов - DEBUG_BACKTRACE_IGNORE_ARGS.

function example() {
    return debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS);
}
function foo() { return example(); }
foo();

проверка php (проверка установки php)

Результат (сокращённо):

array(2) {
  [0]=> array(5) { ... }
  [1]=> array(5) { ... }
}

Index php log (логирование в php)

Проблемы: debug_backtrace может потреблять много памяти при глубоком стеке. Аргументы больших объектов могут замедлить выполнение. Рекурсивные вызовы приводят к бесконечному циклу, если стек не ограничен.

Решение: использовать флаг DEBUG_BACKTRACE_IGNORE_ARGS для уменьшения объема данных; при рекурсии задавать глубину через ini_set('xdebug.max_nesting_level', ...).

Как вывести стек непосредственно в поток вывода?

Функция debug_print_backtrace выводит стек в стандартный вывод (или возвращает в виде строки, если установлен параметр return).

function inner() { debug_print_backtrace(); }
function outer() { inner(); }
outer();

Php warning session start (предупреждение сессии)

#0  inner() called at [/path/to/file.php:10]
#1  outer() called at [/path/to/file.php:12]

Php view source (просмотр исходного кода php)

Функция не возвращает массив, только выводит или возвращает строку. Нельзя легко обработать данные программно.

Как получить стек из блока catch исключения?

У объектов Exception и Error есть метод getTrace(), который возвращает массив, аналогичный debug_backtrace. Также getTraceAsString() возвращает строку.

try {
    throw new \Exception("Test");
} catch (\Exception $e) {
    $trace = $e->getTrace();
    echo $e->getTraceAsString();
}

Php stack (стек вызовов в php)

#0 {main} called at [...:5]

Php trace (трассировка в php)

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

Как получить стек при обработке ошибки?

Установить собственный обработчик через set_error_handler и внутри вызвать debug_backtrace.

set_error_handler(function($severity, $message, $file, $line) {
    $backtrace = debug_backtrace();
    // логирование
    return true;
});

Php get null (ошибка получения get параметров)

Фатальные ошибки (E_ERROR, E_PARSE) не обрабатываются этим хендлером, для них требуется register_shutdown_function или Xdebug.

Как получить более детальный стек с помощью Xdebug?

Xdebug предоставляет функцию xdebug_get_function_stack(), которая возвращает массив со значительно большей информацией, включая переменные на каждом уровне. Также Xdebug автоматически выводит стек при ошибках.

$stack = xdebug_get_function_stack();
print_r($stack);

Fail get contents php (ошибка при использовании file_get_contents)

Xdebug должен быть установлен и активирован. В боевой среде его обычно включают только для отладки.

Как получить последнюю ошибку с контекстом стека?

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

register_shutdown_function(function() {
    $error = error_get_last();
    if ($error !== null) {
        debug_print_backtrace();
    }
});

error_get_last() не даёт информацию о стеке, только финальное сообщение.

- Error php stack trace (стек вызовов ошибки php)
- Php stack trace (получение стека вызовов в php)
- Php log (логирование в php)

Расширенные примеры получения стека вызовов

Пример 1. Рекурсия и глубокий стек

Рекурсивная функция, вызывающая себя 5 раз. Для предотвращения переполнения используется лимит глубины.

Пример
function factorial($n) {
    if ($n <= 1) {
        var_dump(debug_backtrace());
        return 1;
    }
    return $n * factorial($n - 1);
}
factorial(3);
array(3) {
  [0]=> array(5) {
    ["file"]=> "test.php"
    ["line"]=> 5
    ["function"]=> "factorial"
    ["args"]=> array(1) { [0]=> int(1) }
  }
  [1]=> array(5) { ... }
  [2]=> array(5) { ... }
}

Проблема: при большой глубине вывод может быть огромным. Рекомендуется использовать debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS).

Пример 2. Трассировка в трейте

Метод трейта может вызываться из разных классов. Стек покажет вызывающий класс.

Пример
trait TraceTrait {
    public function showTrace() {
        print_r(debug_backtrace());
    }
}
class A { use TraceTrait; }
class B { use TraceTrait; }
(new A)->showTrace();
(new B)->showTrace();
// Для A: первый фрейм содержит 'class' => 'A'
// Для B: 'class' => 'B'

Пример 3. Анонимные функции и замыкания

Стек от анонимной функции может не содержать имени класса. Полезно для отладки обратных вызовов.

Пример
$lambda = function() {
    debug_print_backtrace();
};
function caller($fn) { $fn(); }
caller($lambda);
#0  {closure}() called at [...]
#1  caller() called at [...]

Пример 4. Фильтрация фреймов по имени функции

Извлечение только тех фреймов, где функция начинается с 'process'.

Пример
function getProcessFrames() {
    $trace = debug_backtrace();
    return array_filter($trace, function($frame) {
        return isset($frame['function']) && strpos($frame['function'], 'process') === 0;
    });
}
class Handler {
    public function processData() {
        return getProcessFrames();
    }
}
$h = new Handler();
print_r($h->processData());
Array (
    [0] => Array ( 'function' => 'processData', ... )
)

Пример 5. Форматирование стека в HTML для веб-приложения

Преобразование массива в читаемый HTML с подсветкой.

Пример
function formatBacktraceHtml(array $trace) {
    $html = '
    '; foreach ($trace as $i => $frame) { $file = $frame['file'] ?? 'n/a'; $line = $frame['line'] ?? '?'; $func = $frame['function'] ?? 'main'; $class = $frame['class'] ?? ''; $html .= "
  • #$i {$class}::{$func} at {$file}:{$line}
  • "; } return $html . '
'; } // Применение в обработчике ошибок echo formatBacktraceHtml(debug_backtrace());

Пример 6. Получение стека в конструкторе класса

Позволяет узнать, кто создал экземпляр класса.

Пример
class Logger {
    public function __construct() {
        $trace = debug_backtrace();
        $caller = $trace[1] ?? null;
        echo 'Created by: ' . ($caller['class'] ?? 'unknown') . '::' . ($caller['function'] ?? '');
    }
}
class Service {
    public function init() {
        new Logger();
    }
}
(new Service())->init();
Created by: Service::init

Пример 7. Интеграция с Monolog и добавление контекста стека

В обработчике исключений добавляется стек в виде строки.

Пример
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

$log = new Logger('app');
$log->pushHandler(new StreamHandler('php://stdout'));

try {
    throw new \RuntimeException('Ошибка');
} catch (\Throwable $e) {
    $context = [
        'trace' => $e->getTraceAsString()
    ];
    $log->error($e->getMessage(), $context);
}
[2025-04-03T12:00:00+00:00] app.ERROR: Ошибка {"trace":"#0 ..."}

Пример 8. Обработка фатальных ошибок с помощью register_shutdown_function

Захват последней фатальной ошибки и вывод полного стека (через Xdebug).

Пример
register_shutdown_function(function() {
    $error = error_get_last();
    if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
        echo "
Фатальная ошибка: {$error['message']}
"; // Если Xdebug активен, выведет стек автоматически // Дополнительно можно вызвать debug_print_backtrace() debug_print_backtrace(); } }); // Вызов несуществующей функции nonexistent_func();
Фатальная ошибка: Call to undefined function nonexistent_func()
#0 {main} called at ...

Пример 9. Использование xdebug_get_function_stack с фильтрацией

Показывает переменные в каждом фрейме, но может быть большим.

Пример
function demo() {
    $x = 42;
    xdebug_print_function_stack('trace');
}
demo();
-> demo() called at ...
  variables: $x = 42

Пример 10. Трассировка динамических вызовов через call_user_func

Стек показывает реальную вызываемую функцию.

Пример
function target($a) {
    debug_print_backtrace();
}
call_user_func('target', 'arg');
#0  target() called at ...
#1  call_user_func() called at ...

Получение стека вызовов в PHP - comments

En
Php stack trace (php)