Как зайти в Даркнет?!
25th January, 01:11
6
0
Как в tkinter из поля ввода Entry получить значение в одну переменную и обновить строку кнопкой, затем получить ещё одно введённое значение и затем сложить их. Ниже пример кода
21st July, 19:00
894
0
Программа, которая создает фейковые сервера в поиске игровых серверов CS 1.6 Steam
21st March, 17:43
948
0
Очень долго работает Update запрос Oracle
27th January, 09:58
914
0
не могу запустить сервер на tomcat HTTP Status 404 – Not Found
21st January, 18:02
905
0
Где можно найти фрилансера для выполнения поступающих задач, на постоянной основе?
2nd December, 09:48
938
0
Разработка мобильной кроссплатформенной военной игры
16th July, 17:57
1724
0
период по дням
25th October, 10:44
3955
0
Пишу скрипты для BAS только на запросах
16th September, 02:42
3720
0
Некорректный скрипт для закрытия блока
14th April, 18:33
4613
0
прокидывать exception в блоках try-catch JAVA
11th March, 21:11
4381
0
Помогите пожалуйста решить задачи
24th November, 23:53
6086
0
Не понимаю почему не открывается детальное описание продукта
11th November, 11:51
4350
0
Нужно решить задачу по программированию на массивы
27th October, 18:01
4396
0
Метода Крамера С++
23rd October, 11:55
4309
0
помогите решить задачу на C++
22nd October, 17:31
4002
0
Помогите решить задачу на python с codeforces
22nd October, 11:11
4492
0
Python с нуля: полное руководство для начинающих
18th June, 13:58
2599
0
Как я могу найти неиспользуемые функции в проекте PHP
Как я могу найти неиспользуемые функции в проекте PHP?
Существуют ли функции или APIs, встроенные в PHP, которые позволят мне анализировать мою кодовую базу - например, отражение, token_get_all() ?
Достаточно ли богаты эти APIs функции, чтобы мне не приходилось полагаться на сторонний инструмент для выполнения этого типа анализа?
Вы можете попробовать детектор мертвого кода Себастьяна Бергмана:
phpdcd-это детектор мертвого кода (DCD) для кода PHP. Он сканирует проект PHP для всех объявленных функций и методов и сообщает о них как о "dead code", которые не вызываются хотя бы один раз.
Источник: https://github.com/sebastianbergmann/phpdcd
Обратите внимание, что это статический анализатор кода, поэтому он может выдавать ложные срабатывания для методов, которые вызываются только динамически, например, он не может обнаружить $foo = 'fn'; $foo();
Вы можете установить его через PEAR:
pear install phpunit/phpdcd-beta
После этого вы можете использовать следующие опции:
Usage: phpdcd [switches] <directory|file> ...
--recursive Report code as dead if it is only called by dead code.
--exclude <dir> Exclude <dir> from code analysis.
--suffixes <suffix> A comma-separated list of file suffixes to check.
--help Prints this usage information.
--version Prints the version and exits.
--verbose Print progress bar.
Дополнительные инструменты:
Примечание: согласно уведомлению о хранилище, этот проект больше не поддерживается, и его хранилище хранится только для архивных целей . Так что ваш пробег может отличаться.
Спасибо Грегу и Дэйву за обратную связь. Это было не совсем то, что я искал, но я решил потратить немного времени на его изучение и пришел к этому быстрому и грязному решению:
<?php
$functions = array();
$path = "/path/to/my/php/project";
define_dir($path, $functions);
reference_dir($path, $functions);
echo
"<table>" .
"<tr>" .
"<th>Name</th>" .
"<th>Defined</th>" .
"<th>Referenced</th>" .
"</tr>";
foreach ($functions as $name => $value) {
echo
"<tr>" .
"<td>" . htmlentities($name) . "</td>" .
"<td>" . (isset($value[0]) ? count($value[0]) : "-") . "</td>" .
"<td>" . (isset($value[1]) ? count($value[1]) : "-") . "</td>" .
"</tr>";
}
echo "</table>";
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]);
}
}
}
?>
Я, вероятно, потрачу на это еще некоторое время, чтобы быстро найти файлы и номера строк определений функций и ссылок; эта информация собирается, но не отображается.
Этот бит скриптов 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, и выходные данные будут сгруппированы для 0, что будет означать 0 вызовов функции.
Обратите внимание, что это не решит проблему, которую цитирует выше калебброун, поэтому в выводе могут быть некоторые ложные срабатывания.
Использование: find_unused_functions.php <root_directory>
NOTE: это "30-й" подход к проблеме. Этот сценарий выполняет только лексический проход над файлами и не учитывает ситуации, когда различные модули определяют идентично именованные функции или методы. Если вы используете IDE для своей разработки PHP, он может предложить более комплексное решение.
Требуется PHP 5
Чтобы сохранить вам копию и вставить, прямая загрузка и любые новые версии доступны здесь .
#!/usr/bin/php -f
<?php
// ============================================================================
//
// find_unused_functions.php
//
// Find unused functions in a set of PHP files.
// version 1.3
//
// ============================================================================
//
// Copyright (c) 2011, Andrey Butov. All Rights Reserved.
// This script is provided as is, without warranty of any kind.
//
// http://www.andreybutov.com
//
// ============================================================================
// This may take a bit of memory...
ini_set('memory_limit', '2048M');
if ( !isset($argv[1]) )
{
usage();
}
$root_dir = $argv[1];
if ( !is_dir($root_dir) || !is_readable($root_dir) )
{
echo "ERROR: '$root_dir' is not a readable directory.\n";
usage();
}
$files = php_files($root_dir);
$tokenized = array();
if ( count($files) == 0 )
{
echo "No PHP files found.\n";
exit;
}
$defined_functions = array();
foreach ( $files as $file )
{
$tokens = tokenize($file);
if ( $tokens )
{
// We retain the tokenized versions of each file,
// because we'll be using the tokens later to search
// for function 'uses', and we don't want to
// re-tokenize the same files again.
$tokenized[$file] = $tokens;
for ( $i = 0 ; $i < count($tokens) ; ++$i )
{
$current_token = $tokens[$i];
$next_token = safe_arr($tokens, $i + 2, false);
if ( is_array($current_token) && $next_token && is_array($next_token) )
{
if ( safe_arr($current_token, 0) == T_FUNCTION )
{
// Find the 'function' token, then try to grab the
// token that is the name of the function being defined.
//
// For every defined function, retain the file and line
// location where that function is defined. Since different
// modules can define a functions with the same name,
// we retain multiple definition locations for each function name.
$function_name = safe_arr($next_token, 1, false);
$line = safe_arr($next_token, 2, false);
if ( $function_name && $line )
{
$function_name = trim($function_name);
if ( $function_name != "" )
{
$defined_functions[$function_name][] = array('file' => $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 <root_directory>\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 */
Если я правильно помню, вы можете использовать для этого phpCallGraph. Он создаст для вас красивый график (изображение) со всеми задействованными методами. Если метод не связан с каким-либо другим, это хороший признак того, что метод осиротел.
Вот пример: classGallerySystem.png
Метод getKeywordSetOfCategories() осиротел.
Кстати, вам не нужно брать изображение - phpCallGraph может также генерировать текстовый файл или массив PHP и т. д..
2019+ обновление
Ответ Андрея меня вдохновил, и я превратил его в стандартный нюхательный код.
Обнаружение очень простое но мощное:
- находит все методы
public function someMethod() - затем найдите все вызовы метода
${anything}->someMethod() - и просто сообщает о тех публичных функциях, которые никогда не вызывались
Это помогло мне удалить более 20 + методов , которые я должен был бы поддерживать и тестировать.
3 шага, чтобы найти их
Установить ECS:
composer require symplify/easy-coding-standard --dev
Настройка ecs.yaml config:
# ecs.yaml
services:
Symplify\CodingStandard\Sniffs\DeadCode\UnusedPublicMethodSniff: ~
Выполнить команду:
vendor/bin/ecs check src
Смотрите отчетные методы и удаляйте те, которые вам не нравятся.
Подробнее об этом можно прочитать здесь: удаление мертвых общедоступных методов из кода
насколько мне известно, нет никакой возможности. Чтобы узнать, какие функции "are belonging to whom" вам понадобятся для выполнения системы (runtime late binding function lookup).
Но инструменты рефакторинга основаны на статическом анализе кода. Мне очень нравятся динамические типизированные языки, но, на мой взгляд, их трудно масштабировать. Отсутствие безопасных рефакторингов в больших кодовых базах и динамических типизированных языках является главным недостатком для обеспечения ремонтопригодности и обработки эволюции программного обеспечения.