如何在PHP项目中找到任何未使用的函数?
是否有功能或API内置到PHP,让我分析我的代码库-例如反射,token_get_all()
?
这些API功能是否足够丰富,我不必依赖第三方工具来执行此类分析?
你可以试试Sebastian Bergmann的死码探测器:
phpdcd
是用于PHP代码的死代码检测器(DCD).它扫描PHP项目中所有已声明的函数和方法,并将其报告为"死代码",至少不会调用一次.
资料来源:https://github.com/sebastianbergmann/phpdcd
请注意,它是一个静态代码分析器,因此它可能会为仅动态调用的方法提供误报,例如它无法检测 $foo = 'fn'; $foo();
你可以通过PEAR安装它:
pear install phpunit/phpdcd-beta
之后,您可以使用以下选项:
Usage: phpdcd [switches]... --recursive Report code as dead if it is only called by dead code. --exclude Exclude from code analysis. --suffixes A comma-separated list of file suffixes to check. --help Prints this usage information. --version Prints the version and exits. --verbose Print progress bar.
更多工具:
https://phpqa.io/
注意:根据存储库通知,此项目不再维护,其存储库仅用于存档目的.所以你的里程可能不一样.
感谢Greg和Dave的反馈.不是我想要的,但我决定花一点时间研究它,并提出了这个快速而肮脏的解决方案:
" . "" . " "; foreach ($functions as $name => $value) { echo "Name " . "Defined " . "Referenced " . "" . " "; } echo ""; function define_dir($path, &$functions) { if ($dir = opendir($path)) { while (($file = readdir($dir)) !== false) { if (substr($file, 0, 1) == ".") continue; if (is_dir($path . "/" . $file)) { define_dir($path . "/" . $file, $functions); } else { if (substr($file, - 4, 4) != ".php") continue; define_file($path . "/" . $file, $functions); } } } } function define_file($path, &$functions) { $tokens = token_get_all(file_get_contents($path)); for ($i = 0; $i < count($tokens); $i++) { $token = $tokens[$i]; if (is_array($token)) { if ($token[0] != T_FUNCTION) continue; $i++; $token = $tokens[$i]; if ($token[0] != T_WHITESPACE) die("T_WHITESPACE"); $i++; $token = $tokens[$i]; if ($token[0] != T_STRING) die("T_STRING"); $functions[$token[1]][0][] = array($path, $token[2]); } } } function reference_dir($path, &$functions) { if ($dir = opendir($path)) { while (($file = readdir($dir)) !== false) { if (substr($file, 0, 1) == ".") continue; if (is_dir($path . "/" . $file)) { reference_dir($path . "/" . $file, $functions); } else { if (substr($file, - 4, 4) != ".php") continue; reference_file($path . "/" . $file, $functions); } } } } function reference_file($path, &$functions) { $tokens = token_get_all(file_get_contents($path)); for ($i = 0; $i < count($tokens); $i++) { $token = $tokens[$i]; if (is_array($token)) { if ($token[0] != T_STRING) continue; if ($tokens[$i + 1] != "(") continue; $functions[$token[1]][1][] = array($path, $token[2]); } } } ?>" . htmlentities($name) . " " . "" . (isset($value[0]) ? count($value[0]) : "-") . " " . "" . (isset($value[1]) ? count($value[1]) : "-") . " " . "
我可能会花更多的时间在上面,这样我就可以快速找到函数定义和引用的文件和行号; 这些信息正在收集,只是没有显示.
这一点bash脚本可能会有所帮助:
grep -rhio ^function\ .*\( .|awk -F'[( ]' '{print "echo -n " $2 " && grep -rin " $2 " .|grep -v function|wc -l"}'|bash|grep 0
这基本上递归地抓取当前目录的函数定义,将命中传递给awk,这形成了执行以下操作的命令:
打印功能名称
递归grep再次
输出到grep -v的管道,用于过滤掉函数定义,以便保留对函数的调用
将此输出传递给wc -l,它打印行数
然后将此命令发送到bash执行,输出被grepped为0,这表示0调用该函数.
请注意,这不是解决问题calebbrown上述列举,所以有可能在输出一些误报.
用法:find_unused_functions.php
注意:这是解决问题的"快速肮脏"方法.此脚本仅对文件执行词法传递,并且不考虑不同模块定义具有相同名称的函数或方法的情况.如果您使用IDE进行PHP开发,它可能会提供更全面的解决方案.
需要PHP 5
为了节省您的复制和粘贴,可以在此处获得直接下载和任何新版本.
#!/usr/bin/php -f $file, 'line' => $line); } } } } } } } // We now have a collection of defined functions and // their definition locations. Go through the tokens again, // and find 'uses' of the function names. foreach ( $tokenized as $file => $tokens ) { foreach ( $tokens as $token ) { if ( is_array($token) && safe_arr($token, 0) == T_STRING ) { $function_name = safe_arr($token, 1, false); $function_line = safe_arr($token, 2, false);; if ( $function_name && $function_line ) { $locations_of_defined_function = safe_arr($defined_functions, $function_name, false); if ( $locations_of_defined_function ) { $found_function_definition = false; foreach ( $locations_of_defined_function as $location_of_defined_function ) { $function_defined_in_file = $location_of_defined_function['file']; $function_defined_on_line = $location_of_defined_function['line']; if ( $function_defined_in_file == $file && $function_defined_on_line == $function_line ) { $found_function_definition = true; break; } } if ( !$found_function_definition ) { // We found usage of the function name in a context // that is not the definition of that function. // Consider the function as 'used'. unset($defined_functions[$function_name]); } } } } } } print_report($defined_functions); exit; // ============================================================================ function php_files($path) { // Get a listing of all the .php files contained within the $path // directory and its subdirectories. $matches = array(); $folders = array(rtrim($path, DIRECTORY_SEPARATOR)); while( $folder = array_shift($folders) ) { $matches = array_merge($matches, glob($folder.DIRECTORY_SEPARATOR."*.php", 0)); $moreFolders = glob($folder.DIRECTORY_SEPARATOR.'*', GLOB_ONLYDIR); $folders = array_merge($folders, $moreFolders); } return $matches; } // ============================================================================ function safe_arr($arr, $i, $default = "") { return isset($arr[$i]) ? $arr[$i] : $default; } // ============================================================================ function tokenize($file) { $file_contents = file_get_contents($file); if ( !$file_contents ) { return false; } $tokens = token_get_all($file_contents); return ($tokens && count($tokens) > 0) ? $tokens : false; } // ============================================================================ function usage() { global $argv; $file = (isset($argv[0])) ? basename($argv[0]) : "find_unused_functions.php"; die("USAGE: $file\n\n"); } // ============================================================================ function print_report($unused_functions) { if ( count($unused_functions) == 0 ) { echo "No unused functions found.\n"; } $count = 0; foreach ( $unused_functions as $function => $locations ) { foreach ( $locations as $location ) { echo "'$function' in {$location['file']} on line {$location['line']}\n"; $count++; } } echo "=======================================\n"; echo "Found $count unused function" . (($count == 1) ? '' : 's') . ".\n\n"; } // ============================================================================ /* EOF */