68 votes

Comment trouver les fonctions inutilisées dans un projet PHP ?

Comment trouver les fonctions inutilisées dans un projet PHP ?

Existe-t-il des fonctionnalités ou des API intégrées à PHP qui me permettraient d'analyser ma base de code - par exemple Réflexion , token_get_all() ?

Ces API sont-elles suffisamment riches en fonctionnalités pour que je ne doive pas recourir à un outil tiers pour effectuer ce type d'analyse ?

1 votes

0 votes

Xdebug peut également fournir couverture du code . Vous pouvez l'utiliser en combinaison avec les fonctions auto_prepend_file et auto_append_file. directives ini pour enregistrer la couverture de code de votre application lors d'une utilisation générale.

0 votes

xdebug Je peux vous dire qu'avec l'analyse de la couverture du code xdebug.org/docs/code_coverage

35voto

Gordon Points 156415

Vous pouvez essayer le Dead Code Detector de Sebastian Bergmann :

phpdcd est un détecteur de codes morts (DCD) pour le code PHP. Il recherche dans un projet PHP toutes les fonctions et méthodes déclarées et signale comme "code mort" celles qui ne sont pas appelées au moins une fois.

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

Notez qu'il s'agit d'un analyseur de code statique, et qu'il peut donc donner de faux positifs pour des méthodes qui ne sont appelées que dynamiquement, par exemple il ne peut pas détecter $foo = 'fn'; $foo();

Vous pouvez l'installer via PEAR :

pear install phpunit/phpdcd-beta

Après cela, vous pouvez utiliser les options suivantes :

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.

Plus d'outils :


Note : conformément à l'avis de dépôt, ce projet n'est plus maintenu et son dépôt n'est conservé qu'à des fins d'archivage . Votre kilométrage peut donc varier.

2 votes

De la page github : "Ce projet n'est plus maintenu et son dépôt n'est conservé qu'à des fins d'archivage."

0 votes

@BurhanAli ne plus être maintenu ne signifie pas nécessairement qu'il ne fonctionne plus. N'hésitez pas à reprendre la maintenance.

1 votes

@Gordon Il y a une raison pour laquelle il n'est plus maintenu, le large spectre d'appels de méthodes qu'il ne détecte pas : "Class::method()" et les constructeurs sont signalés comme non utilisés par exemple. L'approche par réflexion n'est pas l'idéal pour un langage dynamique, et donc améliorer l'outil signifierait en réalité en réécrire un nouveau en utilisant une autre approche pour détecter les méthodes utilisées.

25voto

Stacey Richards Points 2635

Merci Greg et Dave pour les commentaires. Ce n'était pas tout à fait ce que je cherchais, mais j'ai décidé de prendre un peu de temps pour faire des recherches et j'ai trouvé cette solution rapide et sale :

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

Je vais probablement passer un peu plus de temps dessus afin de pouvoir trouver rapidement les fichiers et les numéros de ligne des définitions de fonctions et des références ; ces informations sont recueillies, mais ne sont pas affichées.

1 votes

Cette solution est bonne si vous n'utilisez jamais call_user_func() ou call_user_func_array() ou $var()

0 votes

J'ai dû remplacer les deux die() avec continue pour que ça ne s'étouffe pas sur les fonctions anonymes.

21voto

Tim Cullen Points 91

Ce petit script bash pourrait vous aider :

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

Cela consiste à rechercher récursivement dans le répertoire courant les définitions de fonctions, à passer les résultats à awk, qui forme une commande pour faire ce qui suit :

  • imprimer le nom de la fonction
  • Récursivement, chercher à nouveau
  • en passant cette sortie à grep -v pour filtrer les définitions de fonction afin de conserver les appels à la fonction
  • envoie cette sortie à wc -l qui affiche le nombre de lignes.

Cette commande est ensuite envoyée pour exécution à bash et la sortie est parcourue pour 0, ce qui indiquerait 0 appel à la fonction.

Notez que cela pas résoudre le problème que calebbrown cite ci-dessus, donc il pourrait y avoir quelques faux positifs dans la sortie.

0 votes

Je l'adore ! Rapide, sale et pratique. Voici une version mise à jour pour le PHP moderne. Enlevez le sort -u (supprimer les doublons) si vous préférez les fonctions dans l'ordre du fichier plutôt que dans l'ordre alphabétique. egrep -rhio 'function\ \w+\(' .|sort -u |awk -F'[( ]' '{print "echo -n " $2 " && grep -rin " $2 " .|grep -v function |wc -l"}'| bash | grep ' 0$'

3voto

Till Points 14673

Si je me souviens bien, vous pouvez utiliser phpCallGraph pour le faire. Il générera pour vous un joli graphique (image) avec toutes les méthodes concernées. Si une méthode n'est connectée à aucune autre, c'est un bon signe que la méthode est orpheline.

Voici un exemple : classGallerySystem.png

La méthode getKeywordSetOfCategories() est orphelin.

Au fait, il n'est pas nécessaire de prendre une image : phpCallGraph peut également générer un fichier texte, ou un tableau PHP, etc.

3voto

webbiedave Points 28781

Comme les fonctions/méthodes PHP peuvent être invoquées dynamiquement, il n'existe aucun moyen programmatique de savoir avec certitude si une fonction ne sera jamais appelée.

Le seul moyen sûr est l'analyse manuelle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X