मैं एक PHP परियोजना में अप्रयुक्त कार्यों को कैसे ढूंढ सकता हूं

मैं एक PHP परियोजना में किसी भी अप्रयुक्त कार्यों को कैसे ढूंढ सकता हूं?

क्या PHP में निर्मित फीचर्स या एपीआई हैं जो मुझे मेरे कोडबेस का विश्लेषण करने की अनुमति देगी - उदाहरण के लिए प्रतिबिंब , token_get_all() ?

क्या ये एपीआई मेरे लिए पर्याप्त समृद्ध हैं, इस प्रकार के विश्लेषण करने के लिए किसी तीसरे पक्ष के उपकरण पर भरोसा नहीं करना है?

0
ro fr bn
PHP टेस्ट कवरेज टूल आज़माएं।
जोड़ा लेखक Greg Hurlman, स्रोत
मुझे लगता है कि एक साधारण फ़ाइल खोज अक्सर पहले से ही कर सकती है - जब तक आप परिवर्तनीय फ़ंक्शन नामों का उपयोग नहीं कर रहे हैं।
जोड़ा लेखक Pekka 웃, स्रोत
xdebug आपको बता सकता है कि कोड कवरेज विश्लेषण के साथ xdebug.org/docs/code_coverage
जोड़ा लेखक Mchl, स्रोत
Xdebug कोड कवरेज । आप सामान्य रूप से अपने आवेदन के कोड कवरेज को लॉग इन करने के लिए auto_prepend_file और auto_append_file इन निर्देशों के संयोजन में इसका उपयोग कर सकते हैं उपयोग।
जोड़ा लेखक Dave Marshall, स्रोत

8 उत्तर

फीडबैक के लिए धन्यवाद ग्रेग और डेव। मैं जो कुछ भी ढूंढ रहा था वह काफी नहीं था, लेकिन मैंने इसे शोधने में थोडा समय लगाने का फैसला किया और इस त्वरित और गंदे समाधान के साथ आया:

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

मैं शायद इस पर कुछ और समय बिताऊंगा ताकि मैं फ़ंक्शन परिभाषाओं और संदर्भों की फ़ाइलों और रेखा संख्याओं को तुरंत ढूंढ सकूं; यह जानकारी इकट्ठा की जा रही है, बस प्रदर्शित नहीं हुई है।

0
जोड़ा
यह समाधान अच्छा है यदि आप call_user_func() या call_user_func_array() या $ var() का कभी भी उपयोग नहीं करते हैं
जोड़ा लेखक calebbrown, स्रोत

afaik कोई रास्ता नहीं है। यह जानने के लिए कि कौन से फ़ंक्शन "किसके हैं" आपको सिस्टम को निष्पादित करने की आवश्यकता होगी (रनटाइम देर से बाइंडिंग फ़ंक्शन लुकअप)।

लेकिन रिफैक्टरिंग टूल स्थिर कोड विश्लेषण पर आधारित होते हैं। मुझे वास्तव में गतिशील टाइप की गई भाषाएं पसंद हैं, लेकिन मेरे विचार में उन्हें स्केल करना मुश्किल है। बड़े कोडबेस और गतिशील टाइप की गई भाषाओं में सुरक्षित रिफैक्टरिंग की कमी, रखरखाव और सॉफ्टवेयर विकास को संभालने के लिए एक बड़ी कमी है।

0
जोड़ा

आप सेबेस्टियन बर्गमान के डेड कोड डिटेक्टर को आजमा सकते हैं:

phpdcd PHP कोड के लिए एक डेड कोड डिटेक्टर (डीसीडी) है। यह सभी घोषित कार्यों और विधियों के लिए एक PHP प्रोजेक्ट स्कैन करता है और उनको "मृत कोड" के रूप में रिपोर्ट करता है जिन्हें कम से कम एक बार नहीं कहा जाता है।

Source: https://github.com/sebastianbergmann/phpdcd

Note that it's a static code analyzer, so it might give false positives for methods that only called dynamically, e.g. it cannot detect $foo = 'fn'; $foo();

आप इसे पीयर के माध्यम से स्थापित कर सकते हैं:

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.

      अधिक टूल्स:


      Note: as per the repository notice, this project is no longer maintained and its repository is only kept for archival purposes. So your mileage may vary.

0
जोड़ा
"अधिक टूल" लिंक के लिए धन्यवाद, जो "PHP मेस डिटेक्टर" का भी संदर्भ देता है ( phpmd.org )
जोड़ा लेखक BurninLeo, स्रोत
@ बुरहानअली अब बनाए रखा नहीं है इसका मतलब यह नहीं है कि यह अब काम नहीं कर रहा है। रखरखाव पर लेने के लिए स्वतंत्र महसूस करें।
जोड़ा लेखक Gordon, स्रोत
@ डेरेक्सन हाँ, मैं सहमत हूं। लेकिन जब तक कि कोई अंततः ऐसा नहीं करता है, यह है अभी भी कुछ भी नहीं है।
जोड़ा लेखक Gordon, स्रोत
+1: यह एक नहीं पता था
जोड़ा लेखक Mchl, स्रोत
@ गॉर्डन एक कारण है कि इसे अब और नहीं रखा गया है, विधि के बड़े स्पेक्ट्रम को यह पता नहीं चलता है: "कक्षा :: विधि ()" और रचनाकारों की रिपोर्ट उदाहरण के लिए नहीं की जाती है। प्रतिबिंब दृष्टिकोण एक गतिशील भाषा के लिए आदर्श नहीं है, और इसलिए उपकरण में सुधार करने के लिए वास्तविकता का मतलब है कि इस्तेमाल किए गए तरीकों का पता लगाने के लिए एक और दृष्टिकोण का उपयोग करके एक नया लिखें।
जोड़ा लेखक Dereckson, स्रोत
जिथब पेज से: "यह प्रोजेक्ट अब बनाए रखा नहीं गया है और इसकी रिपॉजिटरी केवल अभिलेखीय उद्देश्यों के लिए रखी गई है।"
जोड़ा लेखक Burhan Ali, स्रोत

phpxref will identify where functions are called from which would facilitate the analysis - but there's still a certain amount of manual effort involved.

0
जोड़ा

चूंकि PHP फ़ंक्शंस/विधियों को गतिशील रूप से बुलाया जा सकता है, इसलिए फ़ंक्शन को कभी भी कॉल नहीं किया जाएगा, निश्चित रूप से जानने के लिए कोई प्रोग्रामेटिक तरीका नहीं है।

मैन्युअल विश्लेषण के माध्यम से एकमात्र निश्चित तरीका है।

0
जोड़ा

USAGE: find_unused_functions.php

नोट: यह एक है? जल्दी-एन-गंदे? समस्या के लिए दृष्टिकोण। यह स्क्रिप्ट केवल फाइलों पर एक व्याख्यात्मक पास करता है, और उन स्थितियों का सम्मान नहीं करता है जहां विभिन्न मॉड्यूल समान नामित फ़ंक्शंस या विधियों को परिभाषित करते हैं। यदि आप अपने 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 \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 */
0
जोड़ा
बस कमाल, धन्यवाद एंड्री! मैं इसे आपकी बहुत मदद के साथ मानक चेकर कोडिंग में बदल रहा हूं: github.com/Symplify/Symplify/पुल/466
जोड़ा लेखक Tomáš Votruba, स्रोत

अगर मुझे सही याद है तो आप ऐसा करने के लिए phpCallGraph का उपयोग कर सकते हैं। यह आपके लिए शामिल सभी विधियों के साथ एक अच्छा ग्राफ (छवि) उत्पन्न करेगा। यदि कोई विधि किसी अन्य से जुड़ा नहीं है, तो यह एक अच्छा संकेत है कि विधि अनाथ है।

Here's an example: classGallerySystem.png

विधि getKeywordSetOfCategories() अनाथ है।

वैसे, आपको एक छवि लेने की आवश्यकता नहीं है - phpCallGraph भी एक पाठ फ़ाइल, या एक PHP सरणी, आदि उत्पन्न कर सकते हैं ..

0
जोड़ा

बैश स्क्रिप्टिंग का यह बिट मदद कर सकता है:

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

यह मूल रूप से फ़ंक्शन परिभाषाओं के लिए वर्तमान निर्देशिका को दोहराता है, हिट को अजीब से गुजरता है, जो निम्न कार्य करने के लिए कमांड बनाता है:

  • फ़ंक्शन नाम प्रिंट करें
  • इसके लिए फिर से grep
  • फ़ंक्शन परिभाषाओं को फ़िल्टर करने के लिए grep -v को आउटपुट को पाइप करना ताकि फ़ंक्शन में कॉल को बनाए रखा जा सके
  • इस आउटपुट को wc -l पर पाइप करता है जो लाइन गिनती प्रिंट करता है

यह आदेश तब निष्पादन के लिए निष्पादन के लिए भेजा जाता है और आउटपुट 0 के लिए grepped है, जो समारोह में 0 कॉल इंगित करेगा।

ध्यान दें कि यह उपरोक्त calebbrown साइट्स को हल करने के लिए नहीं करेगा, इसलिए आउटपुट में कुछ झूठे सकारात्मक हो सकते हैं।

0
जोड़ा