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; | |
| } |