Frage

Wie finde ich alle nicht verwendeten Funktionen in eine PHP-Projekt?

Gibt es Funktionen oder APIs gebaut in PHP, die es mir ermöglichen, zu analysieren, meine Codebasis - zum Beispiel Reflexion, token_get_all()?

Sind diese APIs bieten Reich genug, um mich nicht darauf zu verlassen, ein Drittanbieter-tool zur Durchführung dieser Art von Analyse?

War es hilfreich?

Lösung 2

Dank Greg und Dave für das feedback.War nicht ganz das, was ich suchte, aber ich entschied mich, ein bisschen Zeit in die Erforschung und kam mit dieser quick-and-dirty-Lösung:

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

Ich werde wahrscheinlich zu verbringen mehr Zeit auf es, so kann ich Sie schnell finden die Dateien und Zeilennummern der Funktion Definitionen und Referenzen;diese Informationen gesammelt werden nur nicht angezeigt.

Andere Tipps

Sie können versuchen, Sebastian Bergmann ist Tot-Code-Detektor:

phpdcd ist Dead Code Detector (DCD) für PHP-code.Es scannt ein PHP-Projekt für alle deklarierten Funktionen und Methoden und meldet diese als "Toter code", der nicht mindestens einmal aufgerufen wurde.

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

Beachten Sie, dass es ein statischer code-Analysator, so könnte es geben falsche positive für Methoden, die nur aufgerufen, dynamisch, z.B.es kann nicht erkennen, $foo = 'fn'; $foo();

Sie können installieren es über BIRNE:

pear install phpunit/phpdcd-beta

Nach, dass, Sie verwenden können die folgenden Optionen:

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.

Weitere tools:


Hinweis: wie pro die repository bemerken, dieses Projekt wird nicht mehr gepflegt und dem repository ist nur für Archivierungszwecke aufbewahrt.So kann Ihre Laufleistung variieren.

Das bisschen bash-Skript helfen könnte:

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

Dies ist im Grunde rekursiv greps das aktuelle Verzeichnis für die Funktionsdefinitionen, geht das trifft zu awk, Formen, die einen Befehl ausführen, um die folgenden:

  • drucken Sie den Namen der Funktion
  • rekursiv grep für es wieder
  • Rohrleitungen, die Ausgabe an grep -v filter aus Funktionsdefinitionen, so behalten Aufrufe der Funktion
  • Rohre dieser Ausgabe zu wc -l die prints der Linie zählen

Dieser Befehl ist dann gesendet, die für die Ausführung der bash und der Ausgang ist grep für 0, was darauf hindeuten würde 0 Aufrufe der Funktion.

Beachten Sie, dass dies nicht lösen die problem calebbrown zitiert oben, so könnte es einige falsche positive in der Ausgabe.

VERWENDUNG: find_unused_functions.php <root_directory>

HINWEIS:Dies ist ein "quick-n-dirty" - Ansatz, um das problem.Dieses Skript führt nur eine lexikalische pass über die Dateien-und nicht die Achtung Situationen, in denen die verschiedenen Module definieren, identisch benannte Funktionen oder Methoden.Wenn Sie eine IDE für die PHP-Entwicklung, es kann bieten eine mehr umfassende Lösung.

Benötigt PHP 5

Speichern Sie eine Kopie und einfügen, einen direkten download, und alle neuen Versionen sind hier erhältlich.

#!/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 */

Wenn ich mich richtig erinnere, die Sie verwenden können phpCallGraph zu tun.Es werden erzeugen eine schöne Grafik (Bild) für Sie alle die Methoden beteiligt.Wenn eine Methode nicht mit einem anderen, das ist ein gutes Zeichen, dass die Methode, die ist verwaist.

Hier ist ein Beispiel: classGallerySystem.png

Die Methode getKeywordSetOfCategories() ist verwaist.

Nur durch die Art und Weise, die Sie nicht haben, um ein Bild zu machen -- phpCallGraph können auch generieren eine Textdatei oder ein PHP-array, etc..

Da PHP die Funktionen/Methoden dynamisch aufgerufen wird, gibt es keine programmgesteuerte Möglichkeit, mit Sicherheit zu wissen, wenn eine Funktion wird nie aufgerufen.

Die einzig sichere Methode ist durch die manuelle Analyse.

2019+ Update

Ich habe inspied by Andrey Antwort und wandelte diese in ein standard zur Codierung schnuppern.

Die Erkennung ist sehr einfach aber leistungsstark:

  • findet alle Methoden public function someMethod()
  • dann finden Sie alle Methodenaufrufe ${anything}->someMethod()
  • und einfach Berichte diejenigen öffentlichen Aufgaben, die wurden nie genannt

Es hat mir geholfen, Sie zu entfernen über 20+ Methoden Hätte ich zu pflegen und zu testen.


3 Schritte, um Sie zu Finden

Bei der Installation von ECS:

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

Einrichten ecs.yaml config:

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

Führen Sie den Befehl:

vendor/bin/ecs check src

Siehe berichtet, Methoden und entfernen Sie diejenigen, die Sie nicht fein nützlich 👍


Sie können mehr darüber Lesen Sie hier: Entfernen Sie Tote Public Methoden aus dem Code

afaik gibt es keine Möglichkeit.Zu wissen, welche Funktionen "gehören zu dem" man würde ausführen müssen dem system (Laufzeit, late binding-Funktion lookup).

Aber Refactoring-Werkzeuge basieren auf der statischen code-Analyse.Ich wirklich wie dynamisch typisierte Sprachen, aber in meinem view, Sie sind schwer zu skalieren ist.Die fehlende sichere refactorings in großen codebases und dynamisch typisierten Sprachen ist ein großer Nachteil für die Wartbarkeit und Handhabung der software-evolution.

phpxref identifizieren, wo Funktionen aufgerufen werden, von denen würde es ermöglichen, die Analyse - aber es gibt noch eine Menge manuellen Aufwand.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top