كيف يمكنني العثور على وظائف غير المستخدمة في مشروع PHP

StackOverflow https://stackoverflow.com/questions/11532

  •  08-06-2019
  •  | 
  •  

سؤال

كيف يمكنني العثور على أي وظائف غير المستخدمة في PHP المشروع ؟

هل هناك ميزات أو واجهات برمجة التطبيقات التي بنيت في PHP التي تسمح لي لتحليل بلدي تعليمات البرمجة الأساسية - على سبيل المثال انعكاس, token_get_all()?

هذه واجهات برمجة التطبيقات ميزة غنية بما فيه الكفاية بالنسبة لي ليس لديك إلى الاعتماد على طرف ثالث أداة لتنفيذ هذا النوع من التحليل ؟

هل كانت مفيدة؟

المحلول 2

شكرا جريج و ديف لردود الفعل.لم يكن تماما ما كنت أبحث عنه, ولكن قررت أن أضع قليلا من الوقت في البحث و جاء مع هذا سريعة وقذرة الحل:

<?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]);
            }
        }
    }
?>

أنا ربما سوف تنفق المزيد من الوقت حتى أستطيع العثور بسرعة على الملفات خط الأرقام الدالة التعريفات والمراجع ؛ هذه المعلومات يتم جمعها فقط لا يتم عرض.

نصائح أخرى

يمكنك محاولة سيباستيان برغمان ميت مدونة الكاشف:

phpdcd هو رمز ميت كاشف (DCD) كود PHP.فإنه بفحص PHP المشروع لجميع الوظائف المعلنة والأساليب تلك التقارير بأنها "ميتة كود" أن لا تسمى على الأقل مرة واحدة.

المصدر: https://github.com/sebastianbergmann/phpdcd

علما أنه رمز ثابت محلل ، لذلك قد تعطي ايجابيات كاذبة طرق فقط يسمى حيوي, مثلأنه لا يمكن الكشف عن $foo = 'fn'; $foo();

يمكنك تثبيته عن طريق الكمثرى:

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.

المزيد من الأدوات:


ملاحظة: كما في مستودع إشعار ، هذا المشروع يعد يحتفظ به مستودع فقط الاحتفاظ بها لأغراض الأرشيف.حتى الأميال الخاص بك قد تختلف.

هذا قليلا من باش البرمجة قد تساعد:

grep -rhio ^function\ .*\(  .|awk -F'[( ]'  '{print "echo -n " $2 " && grep -rin " $2 " .|grep -v function|wc -l"}'|bash|grep 0

هذا الأساس بشكل متكرر greps الدليل الحالي عن وظيفة التعاريف يمر يضرب awk ، الذي يشكل الأمر إلى القيام بما يلي:

  • طباعة اسم الدالة
  • بشكل متكرر البقرى مرة أخرى ،
  • الأنابيب الذي خرج إلى grep -v لتصفية تعريفات الدالة بحيث تحتفظ يدعو إلى وظيفة
  • الأنابيب هذا الإخراج إلى wc-l الذي يطبع خط العد

هذا الأمر ثم أرسل بإعدام باش والإخراج هو grepped 0, التي من شأنها أن تشير إلى 0 المكالمات إلى وظيفة.

لاحظ أن هذا سوف لا حل مشكلة calebbrown يستشهد أعلاه, لذلك قد يكون هناك بعض ايجابيات كاذبة في الإخراج.

الاستخدام: find_unused_functions.php <root_directory>

ملاحظة:هذا هو 'سريعة-n-القذرة' النهج.هذا البرنامج النصي فقط ينفذ المعجمية تمرير أكثر من الملفات ، ولا تحترم حالات وحدات مختلفة تحدد اسمه مماثل الوظائف أو الطرق.إذا كنت تستخدم 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, الخ..

لأن وظائف PHP/طرق يمكن بشكل حيوي الاحتجاج ليس هناك طريقة برمجية أن نعرف على وجه اليقين إذا كانت وظيفة أبدا أن يسمى.

السبيل الوحيد هو من خلال التحليل اليدوي.

2019+ التحديث

حصلت inspied من قبل أندري الجواب و تحولت هذا الترميز القياسية شم.

الكشف هو بسيطة لكنها قوية جدا:

  • يجد كل الطرق public function someMethod()
  • ثم ابحث عن كل طريقة المكالمات ${anything}->someMethod()
  • و ببساطة تقارير تلك المهام العامة التي لم تكن تسمى

لقد ساعدني لإزالة أكثر 20+ طرق وأود أن الحفاظ على الاختبار.


3 خطوات للعثور عليهم

تثبيت ECS:

composer require symplify/easy-coding-standard --dev

إعداد ecs.yaml التكوين:

# ecs.yaml
services:
    Symplify\CodingStandard\Sniffs\DeadCode\UnusedPublicMethodSniff: ~

تشغيل الأمر:

vendor/bin/ecs check src

انظر ذكرت طرق إزالة تلك التي لا غرامة مفيدة 👍


يمكنك قراءة المزيد عن ذلك هنا: إزالة الخلايا الميتة على الطرق العامة من التعليمات البرمجية الخاصة بك

afaik لا توجد وسيلة.معرفة وظائف "هي تابعة لمن" سوف تحتاج إلى تنفيذ النظام (وقت متأخر ملزمة وظيفة البحث).

ولكن إعادة بيع ديون أدوات تستند إلى تحليل رمز ثابت.أنا حقا أحب ديناميكية كتبته لغات, ولكن في رأيي أنها صعبة على نطاق واسع.عدم وجود آمنة refactorings كبيرة codebases وديناميكية كتبته لغات هي العائق الرئيسي الصيانة والتعامل مع البرامج التطور.

phpxref تحديد حيث يتم استدعاء وظائف من شأنها تسهيل تحليل - ولكن لا يزال هناك قدر معين من الجهد اليدوي المعنية.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top