Skip to content

Instantly share code, notes, and snippets.

@edvakf
Last active December 22, 2015 08:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save edvakf/6445404 to your computer and use it in GitHub Desktop.
Save edvakf/6445404 to your computer and use it in GitHub Desktop.
PHPコードの複雑さチェック

complexity.php

使い方

php complexity.php filename

出力形式

以下のカラムを持つ tsv

  1. 関数の行数
  2. 関数内の制御構文の数(if, while, switch, for, foreach, try, function)
  3. 関数内の制御レベルの深さ(else節がif節より1レベル深くなる)
  4. 関数名
  5. ファイル名

備考

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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment