Роздрукувати стек виклику PHP


257

Я шукаю спосіб надрукувати стек викликів у PHP.

Бонусні бали, якщо функція промиває буфер вводу-виводу.



12
... але ці відповіді кращі.
Бен

Відповіді:


123

Якщо ви хочете створити зворотний трек, ви шукаєте debug_backtraceта / або debug_print_backtrace.


Перший, наприклад, отримає такий масив, як цей (цитуючи посібник) :

array(2) {
[0]=>
array(4) {
    ["file"] => string(10) "/tmp/a.php"
    ["line"] => int(10)
    ["function"] => string(6) "a_test"
    ["args"]=>
    array(1) {
      [0] => &string(6) "friend"
    }
}
[1]=>
array(4) {
    ["file"] => string(10) "/tmp/b.php"
    ["line"] => int(2)
    ["args"] =>
    array(1) {
      [0] => string(10) "/tmp/a.php"
    }
    ["function"] => string(12) "include_once"
  }
}


Вони, мабуть, не змивають буфер вводу / виводу, але ви можете зробити це самостійно, за допомогою flushта / або ob_flush.

(див. сторінку першої інструкції, щоб дізнатися, чому "та / або" ;-))


7
це регулярно змушує мою php втратити пам'ять. Я рекомендую рішення Tobiasz.
peedee

Якщо вам важко читати / розуміти, я також рекомендую рішення
Тобіаша

1
@peedee потрібно лише надати один із необов'язкових DEBUG_BACKTRACE_IGNORE_ARGSпараметрів; це робить їх функціонально еквівалентними(new \Exception())->getTraceAsString()

565

Читальніше, ніж debug_backtrace():

$e = new \Exception;
var_dump($e->getTraceAsString());

#2 /usr/share/php/PHPUnit/Framework/TestCase.php(626): SeriesHelperTest->setUp()
#3 /usr/share/php/PHPUnit/Framework/TestResult.php(666): PHPUnit_Framework_TestCase->runBare()
#4 /usr/share/php/PHPUnit/Framework/TestCase.php(576): PHPUnit_Framework_TestResult->run(Object(SeriesHelperTest))
#5 /usr/share/php/PHPUnit/Framework/TestSuite.php(757): PHPUnit_Framework_TestCase->run(Object(PHPUnit_Framework_TestResult))
#6 /usr/share/php/PHPUnit/Framework/TestSuite.php(733): PHPUnit_Framework_TestSuite->runTest(Object(SeriesHelperTest), Object(PHPUnit_Framework_TestResult))
#7 /usr/share/php/PHPUnit/TextUI/TestRunner.php(305): PHPUnit_Framework_TestSuite->run(Object(PHPUnit_Framework_TestResult), false, Array, Array, false)
#8 /usr/share/php/PHPUnit/TextUI/Command.php(188): PHPUnit_TextUI_TestRunner->doRun(Object(PHPUnit_Framework_TestSuite), Array)
#9 /usr/share/php/PHPUnit/TextUI/Command.php(129): PHPUnit_TextUI_Command->run(Array, true)
#10 /usr/bin/phpunit(53): PHPUnit_TextUI_Command::main()
#11 {main}"

50
Блін, це так набагато краще, чому вони не могли зробити це результатом за замовчуванням для debug_print_backtrace ()? Можна було б додати логічний параметр "returnTrace" для тих, хто хоче його у змінній, не відлунне, і це було б ідеально!
jurchiks

1
Я не знаю, скільки місяців я намагався зрозуміти, як це зробити, ніколи не думав, що це спрацює
WojonsTech

Це рішення також займає менше пам'яті, ніж захоплення виводу debug_backtrace () як масив, а потім друк його за допомогою print_r (), що я робив, поки не бачив цього!
Пітер

5
Я шукав спосіб обмежити debug_backtraceлише повернення першого рівня в стек-трасі - це рішення робить для мене роботу. Дякую!
ankr

3
@Andrew print_rзбереже всі повідомлення.
mopo922

41

Щоб записати слід

$e = new Exception;
error_log(var_export($e->getTraceAsString(), true));

Дякую @Tobiasz


35

Назад викидає багато сміття, яке вам не потрібно. Це займає дуже довго, важко читати. Все, що ви завжди хочете - це "що називати, що звідки?" Ось просте рішення з статичною функцією. Зазвичай я кладу його у клас під назвою "налагодження", який містить усі мої функції налагодження.

class debugUtils {
    public static function callStack($stacktrace) {
        print str_repeat("=", 50) ."\n";
        $i = 1;
        foreach($stacktrace as $node) {
            print "$i. ".basename($node['file']) .":" .$node['function'] ."(" .$node['line'].")\n";
            $i++;
        }
    } 
}

Ви називаєте це так:

debugUtils::callStack(debug_backtrace());

І він отримує вихід таким чином:

==================================================
 1. DatabaseDriver.php::getSequenceTable(169)
 2. ClassMetadataFactory.php::loadMetadataForClass(284)
 3. ClassMetadataFactory.php::loadMetadata(177)
 4. ClassMetadataFactory.php::getMetadataFor(124)
 5. Import.php::getAllMetadata(188)
 6. Command.php::execute(187)
 7. Application.php::run(194)
 8. Application.php::doRun(118)
 9. doctrine.php::run(99)
 10. doctrine::include(4)
==================================================


33

Дивно, що ніхто не публікував так:

debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);

Це насправді друкує заднім ходом без сміття - лише який метод називався і де.


2
Дійсно, дійсно рівнозначне головному голосувальному рішенню і коротше. Спасибі
брунеттон

9

Якщо ви хочете слід стека, який дуже схожий на те, як php форматує трасування стека винятків, ніж використовувати цю функцію, я написав:

function debug_backtrace_string() {
    $stack = '';
    $i = 1;
    $trace = debug_backtrace();
    unset($trace[0]); //Remove call to this function from stack trace
    foreach($trace as $node) {
        $stack .= "#$i ".$node['file'] ."(" .$node['line']."): "; 
        if(isset($node['class'])) {
            $stack .= $node['class'] . "->"; 
        }
        $stack .= $node['function'] . "()" . PHP_EOL;
        $i++;
    }
    return $stack;
} 

Це поверне трасування стека у форматі так:

#1 C:\Inetpub\sitename.com\modules\sponsors\class.php(306): filePathCombine()
#2 C:\Inetpub\sitename.com\modules\sponsors\class.php(294): Process->_deleteImageFile()
#3 C:\Inetpub\sitename.com\VPanel\modules\sponsors\class.php(70): Process->_deleteImage()
#4 C:\Inetpub\sitename.com\modules\sponsors\process.php(24): Process->_delete() 

2
або просто$e = new Exception; echo $e->getTraceAsString();
Бред Кент

Бред, це рішення не видаляє останній елемент із сліду стека, щоб ви не показували елемент сліду, викликаний новим винятком
TroySteven



4

phptrace - це чудовий інструмент для друку стека PHP у будь-який час, коли вам потрібно, не встановлюючи жодних розширень.

Є дві основні функції phptrace: по-перше, стек виклику друку PHP, який не потребує нічого встановлювати, по-друге, простежує виконання потоків php, для чого потрібно встановити розширення, яке воно постачає.

наступним чином:

$ ./phptrace -p 3130 -s             # phptrace -p <PID> -s
phptrace 0.2.0 release candidate, published by infra webcore team
process id = 3130
script_filename = /home/xxx/opt/nginx/webapp/block.php
[0x7f27b9a99dc8]  sleep /home/xxx/opt/nginx/webapp/block.php:6
[0x7f27b9a99d08]  say /home/xxx/opt/nginx/webapp/block.php:3
[0x7f27b9a99c50]  run /home/xxx/opt/nginx/webapp/block.php:10 

Чи є версія Windows?
Джоні

Мені подобається, що тут відображається адреса пам'яті. Це може бути корисно
Тайлер Майлз

3

Використовуйте debug_backtraceдля отримання зворотнього зв'язку з тим, які функції та методи були викликані та які файли були включені, що призвело до того, куди debug_backtraceбуло викликано.





1

Рішення Walltearer є чудовим, особливо якщо воно вкладено в тег "до":

<pre>
<?php debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); ?>
</pre>

- який визначає дзвінки на окремих лініях, чітко пронумерованих


0

Відповідь дона Бріггса я адаптував вище, щоб використовувати внутрішній журнал помилок замість публічного друку, що може викликати велике занепокоєння під час роботи на живому сервері. Крім того, додано ще декілька модифікацій, наприклад, можливість включити повний шлях до файлу замість основного імені (тому що можуть бути файли з однаковим іменем у різних шляхах), а також (для тих, хто цього вимагає) повний вихід стека вузла:

class debugUtils {
    public static function callStack($stacktrace) {
        error_log(str_repeat("=", 100));
        $i = 1;
        foreach($stacktrace as $node) {
            // uncomment next line to debug entire node stack
            // error_log(print_r($node, true));
            error_log( $i . '.' . ' file: ' .$node['file'] . ' | ' . 'function: ' . $node['function'] . '(' . ' line: ' . $node['line'] . ')' );
            $i++;
        }
        error_log(str_repeat("=", 100));
    } 
}

// call debug stack
debugUtils::callStack(debug_backtrace());
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.