【PHP】Xdebugを使ってコードカバレッジを取る方法



PHPでデバックする時って中々面倒で複雑な分岐等の場合どこを通っているのかがわかりにくい時があります。
なんとXdebugを使えばコードのカバレッジを簡単に取得することが出来ます。

Xampp使っているとXdebugが標準で入っている且つ有効なので設定は特に必要ありません。

使用するのはXdebugのcode_coverage機能です。

サンプル

基本的な使い方。
サンプルはわかりやすいようにswitchにてどこを通っているのか表示するようにしています。

コード

<?php
xdebug_start_code_coverage ();
function switchTest($arg) {
	switch (gettype ( $arg )) {
		case "boolean" :
			echo "boolean";
			break;
		case "integer" :
			echo "integer";
			break;
		case "double" :
			echo "double";
			break;
		case "string" :
			echo "string";
			break;
		case "array" :
			echo "array";
			break;
		case "object" :
			echo "object";
			break;
		case "resource" :
			echo "resource";
			break;
		case "NULL" :
			echo "NULL";
			break;
		default :
			echo "unknown type";
			break;
	}
}

switchTest ( 1 );
var_dump ( xdebug_get_code_coverage () );

結果

サンプルコードの結果になります。
integer

array (size=1)
  'C:\pleiades\xampp\htdocs\xdebug\index.php' => 
    array (size=10)
      3 => int 1
      4 => int 1
      5 => int 1
      8 => int 1
      9 => int 1
      10 => int 1
      32 => int 1
      33 => int 1
      35 => int 1
      36 => int 1
3行目はfunctionなので実行されているのであっていますね。
integerが表示されているので9行目も出ています。
ちゃんと実行された箇所だけ表示されています。

説明

「xdebug_start_code_coverage ();」を呼ぶとxdebugがカバレッジを記録し始めます。
また、「xdebug_code_coverage_started();」を使用するとカバレッジが実行中か返却されます。
trueなら記録中、falseなら記録をしてないことになります。
「xdebug_get_code_coverage ();」で記録したカバレッジを出力されます。
出力形式はarray(ファイル名 => array(行数 => 1))となります。

「xdebug_stop_code_coverage();」を使えばカバレッジ記録を停止させることが出来ますが、記録していたカバレッジも破棄するようなので必要なら取得後に停止させてください。

ただ、このままだとどこを通ったのかわかっても、とてもわかりにくいので応用編に行きます。

応用編

今度はどこが実行されたかわかりやすく表示するように致しました。

コード

<style>
table, th , .row{
	border: 1px #000000 solid;
	border-collapse: collapse;
}
table tr .row {
	text-align:right;
}
td{
	white-space : pre;
	font-size: 12px;
	line-height: 15px;
	padding: 0 0 0 2px;
}
.pass{
	background-color:#FFFF77;
}
</style>
<?php
xdebug_start_code_coverage ();
function switchTest($arg) {
	switch (gettype ( $arg )) {
		case "boolean" :
			echo "boolean";
			break;
		case "integer" :
			echo "integer";
			break;
		case "double" :
			echo "double";
			break;
		case "string" :
			echo "string";
			break;
		case "array" :
			echo "array";
			break;
		case "object" :
			echo "object";
			break;
		case "resource" :
			echo "resource";
			break;
		case "NULL" :
			echo "NULL";
			break;
		default :
			echo "unknown type";
			break;
	}
}

switchTest ( 1 );

$debug = xdebug_get_code_coverage ();
xdebug_stop_code_coverage ();
// 除外条件
$exceptions = array (
		'lib',
);

// ファイル毎のカバレッジ取得
foreach ( $debug as $file => $lines ) {

	// 除外ファイルチェック
	if (mb_ereg ( implode ( '|', $exceptions ), $file )) {
		unset ( $debug [$file] );
		continue;
	}

	$content = file_get_contents($file);	// 対象ファイル取得
	$content = explode(PHP_EOL, $content);	// 改行毎に配列化

	// カバレッジ出力
	echo <<<EOF
<table>
	<tr class="file"><th colspan="2">{$file}</th></tr>
EOF;
	foreach ($content as $key => $value){
		$class = "";
		// 通過チェック
		if(array_key_exists(($key + 1), $lines)){
			$class = "pass";
		}
		$value = htmlspecialchars($value);
		echo <<<EOF
	<tr><td class="row">{$key}</td><td class="{$class}">{$value}</td></tr>
EOF;
	}
	echo <<<EOF
</table>
EOF;
}

?>

結果

integer
C:\pleiades\xampp\htdocs\xdebug\index.php
0<style>
1table, th , .row{
2 border: 1px #000000 solid;
3 border-collapse: collapse;
4}
5table tr .row {
6 text-align:right;
7}
8td{
9 white-space : pre;
10 font-size: 12px;
11 line-height: 12px;
12 padding: 0 0 0 2px;
13}
14.pass{
15 background-color:#FFFF77;
16}
17</style>
18<?php
19xdebug_start_code_coverage ();
20function switchTest($arg) {
21 switch (gettype ( $arg )) {
22 case "boolean" :
23 echo "boolean";
24 break;
25 case "integer" :
26 echo "integer";
27 break;
28 case "double" :
29 echo "double";
30 break;
31 case "string" :
32 echo "string";
33 break;
34 case "array" :
35 echo "array";
36 break;
37 case "object" :
38 echo "object";
39 break;
40 case "resource" :
41 echo "resource";
42 break;
43 case "NULL" :
44 echo "NULL";
45 break;
46 default :
47 echo "unknown type";
48 break;
49 }
50}
51
52switchTest ( 1 );
53
54$debug = xdebug_get_code_coverage ();
55xdebug_stop_code_coverage ();
56// 除外条件
57$exceptions = array (
58 ‘lib’,
59);
60
61// ファイル毎のカバレッジ取得
62foreach ( $debug as $file => $lines ) {
63
64 // 除外ファイルチェック
65 if (mb_ereg ( implode ( ‘|’, $exceptions ), $file )) {
66 unset ( $debug [$file] );
67 continue;
68 }
69
70 $content = file_get_contents($file); // 対象ファイル取得
71 $content = explode(PHP_EOL, $content); // 改行毎に配列化
72
73 // カバレッジ出力
74 echo <<<EOF
75<table>
76 <tr class="file"><th colspan="2">{$file}</th></tr>
77EOF;
78 foreach ($content as $key => $value){
79 $class = "";
80 // 通過チェック
81 if(array_key_exists(($key + 1), $lines)){
82 $class = "pass";
83 }
84 $value = htmlspecialchars($value);
85 echo <<<EOF
86 <tr><td class="row">{$key}</td><td class="{$class}">{$value}</td></tr>
87EOF;
88 }
89 echo <<<EOF
90</table>
91EOF;
92}
93
94?>

結果のように実行したファイルを呼び出し実行した行に色が付くようにしました。
本当はもっと質素なんですが当サイトのCSSも適用されている状態です。

ライブラリや、フレームワークの仕様した部分も全てカバレッジを取ってしまうので除外するようにもなっています。

プロジェクト全体に基本機能として盛り込むことも可能です。
例えばフレームワークの基本部分に入れて、全ての処理が終了したら画面底部に出すのもいいですし、ログとしてサーバーに出力することも可能です。
テストの時など分岐に入っているかのチェックも簡単になります。

Xdebugって色んな機能がありますが、本気で使おうと思うといまいち使い方がわかりづらい面があります。
もし機会があれば挑戦してみてください。


コメントを残す