php complexity.php filename
以下のカラムを持つ tsv
- 関数の行数
- 関数内の制御構文の数(if, while, switch, for, foreach, try, function)
- 関数内の制御レベルの深さ(else節がif節より1レベル深くなる)
- 関数名
- ファイル名
https://github.com/squizlabs/PHP_CodeSniffer 内の関数を無理やり使っている。
php complexity.php filename
以下のカラムを持つ tsv
https://github.com/squizlabs/PHP_CodeSniffer 内の関数を無理やり使っている。
<?php | |
require_once './CodeSniffer.php'; | |
if (count($argv) < 1) { | |
echo "please specify a file\n"; | |
exit(1); | |
} | |
$file = $argv[1]; | |
new PHP_CodeSniffer; // コンストラクタで定数を定義したりしてるので | |
$tokenizer = new PHP_CodeSniffer_Tokenizers_PHP; | |
$contents = file_get_contents($file); | |
$eol = PHP_CodeSniffer_File::detectLineEndings(null, $contents); | |
$tokens = PHP_CodeSniffer_File::tokenizeString($contents, $tokenizer, $eol); | |
$funcs = countBlocksInFunctions($tokens); | |
//echo "lines\tcomplexity\tdepth\tfunc_name\tfile\n"; | |
foreach ($funcs as $func) { | |
echo "{$func['lines']}\t{$func['complexity']}\t{$func['depth']}\t{$func['name']}\t{$file}\n"; | |
} | |
function countBlocksInFunctions($tokens) { | |
$funcs = array(); | |
$pre_func = false; | |
$in_func = false; | |
$func_name = null; | |
$func_open_pos = 0; | |
$func_close_pos = 0; | |
$func_level = 0; | |
$func_deepest_level = 0; | |
$func_complexity = 0; | |
$func_start_line = 0; | |
$func_end_line = 0; | |
for ($i = 0, $c = count($tokens); $i < $c; $i++) { | |
$token = $tokens[$i]; | |
if ($pre_func && $i === $func_open_pos) { | |
$pre_func = false; | |
$in_func = true; | |
$func_start_line = $token['line']; | |
} | |
if ($in_func && $i === $func_close_pos) { | |
$in_func = false; | |
$func_end_line = $token['line']; | |
$funcs[] = array( | |
'name' => $func_name, | |
'depth' => $func_deepest_level - $func_level + 1, | |
'lines' => $func_end_line - $func_start_line + 1, | |
'complexity' => $func_complexity, | |
); | |
// 色々初期化 | |
$func_name = null; | |
$func_open_pos = 0; | |
$func_close_pos = 0; | |
$func_level = 0; | |
$func_deepest_level = 0; | |
$func_complexity = 0; | |
$func_start_line = 0; | |
$func_end_line = 0; | |
} | |
if (!$in_func) { | |
// 関数内でないときは関数を探すのみ | |
switch ($token['code']) { | |
case T_FUNCTION: | |
// interfaceのメソッドの場合scope_openerとscope_closerが無いため | |
if (isset($token['scope_opener']) && isset($token['scope_closer'])) { | |
$func_open_pos = $token['scope_opener']; | |
$func_close_pos = $token['scope_closer']; | |
$func_deepest_level = $func_level = $token['level']; | |
$pre_func = true; | |
} | |
break; | |
case T_STRING: | |
// "function"が現れると$pre_funcをオンにし、このときに現れるT_STRINGを関数名とする | |
if ($pre_func && $func_name === null) { | |
$func_name = $token['content']; | |
} | |
break; | |
} | |
} else { | |
// 関数内であれば、複雑さと深さを求める | |
switch ($token['code']) { | |
case T_FUNCTION: | |
case T_IF: | |
case T_SWITCH: | |
case T_TRY: | |
case T_FOR: | |
case T_WHILE: | |
case T_FOREACH: | |
$func_complexity++; | |
break; | |
} | |
if (isset($token['level']) && $token['level'] > $func_deepest_level) { | |
$func_deepest_level = $token['level']; | |
} | |
} | |
} | |
return $funcs; | |
} |