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

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

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

Функция debug_backtrace()

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

debug_backtrace() возвращает массив ассоциативных массивов, описывающих стек вызовов. Каждый элемент содержит ключи 'file', 'line', 'function', 'class', 'object', 'type', 'args' и другие.

Базовый пример:


function first() {
    second();
}
function second() {
    third();
}
function third() {
    $trace = debug_backtrace();
    print_r($trace);
}
first();
  

Line num php (использование __line__ в php)

Array
(
    [0] => Array
        (
            [file] => example.php
            [line] => 10
            [function] => third
            [args] => Array ()
        )
    [1] => Array
        (
            [file] => example.php
            [line] => 7
            [function] => second
            [args] => Array ()
        )
    [2] => Array
        (
            [file] => example.php
            [line] => 4
            [function] => first
            [args] => Array ()
        )
)
  

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

Управление выводом: debug_backtrace() принимает битовую маску флагов (DEBUG_BACKTRACE_PROVIDE_OBJECT, DEBUG_BACKTRACE_IGNORE_ARGS). По умолчанию объекты не включаются, аргументы включаются. Флаг DEBUG_BACKTRACE_PROVIDE_OBJECT добавляет ключ 'object'. Флаг DEBUG_BACKTRACE_IGNORE_ARGS пропускает аргументы для уменьшения объема данных.


$trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS);
  

Php get trace (получение стека вызовов в php)

Проблема: Вызов debug_backtrace() внутри замыкания может не включать контекст родительской функции. Решение: Использовать DEBUG_BACKTRACE_PROVIDE_OBJECT для получения объекта замыкания, но это не гарантирует полный стек.

Типичная ошибка: Попытка использовать debug_backtrace() в production без фильтрации. Это может привести к утечке путей файлов и имен функций. Решение: Применять только в dev-режиме или удалять чувствительные данные.

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

Функция debug_print_backtrace()

Как вывести стек вызовов в виде текста напрямую?

debug_print_backtrace() выводит стек вызовов в стандартный вывод (или в строку при передаче true). Полезна для быстрого дампа.


function foo() { debug_print_backtrace(); }
foo();
  

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

#0  foo() called at [/path/to/file.php:5]
  

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

Проблема: Вывод происходит сразу, невозможно изменить формат. Решение: Использовать debug_print_backtrace(true) для получения строки и дальнейшей обработки.

Цель: Быстрая отладка в процессе разработки, когда нужно увидеть вызовы без написания дополнительного кода.

Метод Exception::getTrace() и getTraceAsString()

Каким образом получить стек вызовов из исключения?

При перехвате исключения можно вызвать getTrace() (возвращает массив, аналогичный debug_backtrace()) или getTraceAsString() (текстовое представление).


try {
    throw new \Exception('test');
} catch (\Exception $e) {
    $traceArray = $e->getTrace();
    $traceString = $e->getTraceAsString();
    echo $traceString;
}
  

Source php page (исходный код php страницы)

#0 /path/to/file.php(5): {main}
  

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

Проблема: getTrace() не включает контекст, предшествующий выбросу исключения, если исключение создано в другом месте. Решение: Создавать исключение непосредственно в месте вызова или передавать предыдущее исключение.

Цель: Получение стека вызовов для логирования ошибок, отправки в систему мониторинга.

Получение стека в обработчиках ошибок и исключений

Как получить стек при возникновении фатальной ошибки или неперехваченного исключения?

Установка пользовательского обработчика через set_error_handler() или set_exception_handler(). Внутри обработчика можно вызвать debug_backtrace() или получить объект исключения.


set_exception_handler(function (\Throwable $e) {
    $trace = $e->getTrace();
    file_put_contents('error.log', $e->getTraceAsString());
});
  

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

// (запись в файл error.log)
  

Error php stack trace (стек вызовов ошибки php)

Проблема: Обработчик ошибок может сам вызвать ошибку (например, при записи в лог). Решение: Использовать буферизацию и проверять состояние.

Цель: Централизованный сбор информации об ошибках в приложении.

Расширенная трассировка с Xdebug

Как получить более подробный стек вызовов с аргументами, памятью и временем?

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


// Требуется xdebug
$stack = xdebug_get_function_stack();
var_dump($stack);
  

Php stack trace (получение стека вызовов в php)

array(
  0 => array(
    'function' => 'foo',
    'file' => '/path/to/file.php',
    'line' => 10,
    'params' => array('arg1' => 'value1'),
  ),
  ...
)
  

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

Проблема: Xdebug не всегда доступен на production серверах. Решение: Использовать условное включение или альтернативы.

Цель: Глубокий анализ производительности и состояния переменных в стеке.

ReflectionFunction для анализа стека

Можно ли получить стек вызовов через рефлексию?

ReflectionFunction не предоставляет прямой информации о стеке, но можно комбинировать с debug_backtrace() для получения имени текущей функции и последующего анализа её отражения.


function currentFunctionInfo() {
    $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
    $caller = $trace[0];
    $ref = new ReflectionFunction($caller['function']);
    echo $ref->getFileName();
}
  

Проблема: Для методов класса требуется ReflectionMethod. Решение: Проверять наличие ключа 'class'.

Цель: Получение метаданных функции, вызвавшей текущий код (например, докблоков).

- проверка php (проверка установки php)
- ошибки php (обработка ошибок в php)
- Php error (обработка ошибок php)

Расширенные примеры работы со стеком вызовов

Пример 1: Форматирование стека для лога с удалением чувствительных данных

Пример

function cleanTrace() {
    $trace = debug_backtrace();
    foreach ($trace as & $frame) {
        unset($frame['args'], $frame['object']);
    }
    return json_encode($trace, JSON_PRETTY_PRINT);
}

function risky() {
    echo cleanTrace();
}
risky();
[
    {
        "file": "/var/www/example.php",
        "line": 14,
        "function": "risky",
        "class": "",
        "type": "",
        "args": []
    },
    {
        "file": "/var/www/example.php",
        "line": 16,
        "function": "{main}",
        "args": []
    }
]

Пояснение: Удаление аргументов и объектов предотвращает утечку данных. Используется в production логировании.

Пример 2: Стек вызовов через исключение с сохранением предыдущего контекста

Пример

function inner(\Throwable $previous = null) {
    throw new \Exception('Inner error', 0, $previous);
}
function outer() {
    try {
        throw new \Exception('Outer error');
    } catch (\Exception $e) {
        inner($e);
    }
}
try {
    outer();
} catch (\Exception $e) {
    echo $e->getTraceAsString();
    echo "\nPrevious:\n";
    echo $e->getPrevious()->getTraceAsString();
}
#0 /path/to/file.php(5): inner()
#1 /path/to/file.php(10): outer()
#2 /path/to/file.php(16): {main}
Previous:
#0 /path/to/file.php(9): outer()
#1 /path/to/file.php(16): {main}

Пояснение: Цепочка исключений позволяет получить стек как текущей точки, так и исходной ошибки.

Пример 3: Рекурсивный обход и фильтрация стека по файлам

Пример

function filterTraceByFile($pattern) {
    $trace = debug_backtrace();
    $filtered = [];
    foreach ($trace as $frame) {
        if (isset($frame['file']) && preg_match($pattern, $frame['file'])) {
            $filtered[] = $frame['file'] . ':' . $frame['line'] . ' - ' . $frame['function'];
        }
    }
    return implode("\n", $filtered);
}

class A {
    public function run() { B::test(); }
}
class B {
    public static function test() { echo filterTraceByFile('/vendor/'); }
}
(new A)->run();
// (пусто, так как все файлы не из vendor)

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

Пример 4: Получение стека внутри замыкания со всеми аргументами

Пример

$closure = function () {
    $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT);
    foreach ($trace as $frame) {
        echo 'Function: ' . $frame['function'] . PHP_EOL;
        if (isset($frame['object'])) {
            echo 'Object class: ' . get_class($frame['object']) . PHP_EOL;
        }
    }
};
class Foo {
    public function bar() {
        $closure = function () {
            $trace = debug_backtrace();
            echo 'Closure trace count: ' . count($trace) . PHP_EOL;
        };
        $closure();
    }
}
$foo = new Foo();
$foo->bar();
Function: {closure}
Object class: Foo
Closure trace count: 2

Пояснение: В замыкании debug_backtrace() может не показывать полный контекст, если объект не передан. Использование флага PROVIDE_OBJECT добавляет объект замыкания.

Пример 5: Использование xdebug_get_function_stack() для детальной информации

Пример

// Требуется установленный Xdebug
function detailed() {
    $stack = xdebug_get_function_stack();
    foreach ($stack as $frame) {
        echo $frame['function'] . ' in ' . $frame['file'] . ':' . $frame['line'] . PHP_EOL;
        if (isset($frame['params'])) {
            print_r($frame['params']);
        }
    }
}
function test($a) { detailed(); }
test('hello');
detailed in /path/to/file.php:10
test in /path/to/file.php:14
Array
(
    [a] => 'hello'
)

Пояснение: Xdebug предоставляет аргументы в виде ассоциативного массива с именами параметров, что удобнее чем debug_backtrace().

Пример 6: Стек вызовов в трейте

Пример

trait Traceable {
    public function showTrace() {
        $trace = debug_backtrace();
        echo 'Called from: ' . $trace[0]['file'] . ':' . $trace[0]['line'] . PHP_EOL;
    }
}
class MyClass {
    use Traceable;
    public function doSomething() {
        $this->showTrace();
    }
}
$obj = new MyClass();
$obj->doSomething();
Called from: /path/to/file.php:10

Пояснение: Трейты могут использовать debug_backtrace() для определения файла и строки, откуда вызван метод трейта (полезно для логирования).

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

En
Php get trace (php)