PHP では debug_print_backtrace() でバックトレースを表示できるので、以下の例のようにプログラムがどういう順番で実行されたかをデバッグすることができる。
|
1 2 3 4 5 |
#番号 関数名(引数のオブジェクト) called at [ファイルのフルパス:行数] ... 例) #0 Dog->run(2) called at [/Applications/MAMP/htdocs/xxx/main.php:28] |
例えばこんな感じの3つのファイルがあった場合
|
1 2 3 4 5 6 7 8 9 10 |
<?php class Dog { static function bark() { debug_print_backtrace(); echo "bowwow\n"; } } ?> |
|
1 2 3 4 5 6 7 8 9 10 |
<?php class Cat { static function bark() { debug_print_backtrace(); echo "meowmeow\n"; } } ?> |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?php require_once __DIR__.'/dog.php'; require_once __DIR__.'/cat.php'; class Main { static function bark($animals) { foreach ($animals as $aClass) { $aClass::bark(); } } function dogBark() { Dog::bark(); } } echo "start.\n"; Main::bark(['Dog', 'Cat']); (new Main())->dogBark(); ?> |
出力はこうなる。
|
1 2 3 4 5 6 7 8 9 10 11 |
$ php xxx/main.php start. #0 Dog::bark() called at [/Applications/MAMP/htdocs/xxx/main.php:28] #1 Main::bark(Array ([0] => Dog,[1] => Cat)) called at [/Applications/MAMP/htdocs/xxx/main.php:38] bowwow #0 Cat::bark() called at [/Applications/MAMP/htdocs/xxx/main.php:28] #1 Main::bark(Array ([0] => Dog,[1] => Cat)) called at [/Applications/MAMP/htdocs/xxx/main.php:38] meowmeow #0 Dog::bark() called at [/Applications/MAMP/htdocs/xxx/main.php:33] #1 Main->dogBark() called at [/Applications/MAMP/htdocs/xxx/main.php:39] bowwow |
オレオレ backtrace
便利なことにはかわりないのだけど、僕にはこれが(引数でいくらか制御可能とは言え)少し冗長で見づらいので欲しい情報だけを出力するオレオレ backtrace をすることにした。情報は debug_trace() で入手できるのでこれをゴニョゴニョする。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
$is_debug = true; function print_backtrace($limit = 2) { global $is_debug; if (!$is_debug) { return; } $break = php_sapi_name() == 'cli' ? "\n" : "<br>"; $info = debug_backtrace(); echo $break; $output = array(); foreach ($info as $i => $trace) { // if ($i == 0) { continue; } 模倣を目指すなら入れる if ($i > $limit) { break; } $file = preg_replace('/(^.*)(\\/.*?\\/.*?\.php)/', '$2', $trace["file"]); $output[$i] = <<<EOD $file ({$trace["line"]}) {$trace["class"]}{$trace["type"]}{$trace["function"]} $break EOD; } foreach (array_reverse($output) as $value) { echo $value; } } |
この関数の要点は以下の通り。
- グローバルに定義した $is_debug でデバッグの時以外は何もしない
- コマンドラインとブラウザ両方で使うことを想定して改行方法を変更している(debug_print_backtrace() だとブラウザで改行されなくて見辛かった)
- $limit でどの階層まで出力するか選べる(debug_print_backtrace() と同じ仕様)
- 出力を 【1つ上のディレクトリ/ファイル名 (行数) クラス名::関数名 改行】 とした
- 呼び出し順を逆から辿るようにした
出力結果はこうなる。ファイル名がフルパスだと見辛かったからそれを変えたくて正規表現で1つ上のディレクトリより前は削除したおかげで結構見やすくなったと思う。参考: 【PHP】正規表現を用いて文字列を操作する
(print_backtrace() の呼び出し元情報が不要なら foreach の先頭で if ($i == 0) { continue; } する。)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$ php xxx/main.php start. /xxx/main.php (38) Main::bark /xxx/main.php (28) Dog::bark /xxx/dog.php (6) print_backtrace bowwow /xxx/main.php (38) Main::bark /xxx/main.php (28) Cat::bark /xxx/cat.php (6) print_backtrace meowmeow /xxx/main.php (39) Main->dogBark /xxx/main.php (33) Dog::bark /xxx/dog.php (6) print_backtrace bowwow |
ちなみに
スタックフレームではなく現在の情報(ファイル名や行数)だけが欲しい場合には __FILE__ , __LINE__ などが用意されている。
| 名前 | 説明 |
|---|---|
| __FUNCTION__ | 現在の関数名 |
| __LINE__ | 現在の行番号 |
| __FILE__ | 現在のファイル名 |
| __CLASS__ | 現在のクラス名 |