38 votes

Détermination des classes définies dans un fichier de classe PHP.

Étant donné que chaque fichier PHP de notre projet contient une seule définition de classe, comment puis-je déterminer quelle classe ou quelles classes sont définies dans le fichier ?

Je sais que je pourrais simplement utiliser une expression régulière pour rechercher les déclarations de class, mais je préférerais faire quelque chose de plus efficace.

0 votes

Quel est le but d'obtenir le nom de classe de chaque fichier? La meilleure solution devrait être adaptée à votre espace de problème. Tel quel, j'ai l'impression qu'il y a probablement une meilleure solution en fonction de ce que vous cherchez à faire.

6 votes

Cela fait un moment, mais toujours : vous pourriez appeler get_declared_classes, le sauvegarder, inclure le fichier de classe, et rappeler get_declared_classes à nouveau. La différence se trouve dans ce fichier. Simple.

64voto

Venkat D. Points 1103

J'avais besoin de quelque chose comme ça pour un projet sur lequel je travaille, et voici les fonctions que j'ai écrites :

function file_get_php_classes($chemin_fichier) {
  $code_php = file_get_contents($chemin_fichier);
  $classes = get_php_classes($code_php);
  return $classes;
}

function get_php_classes($code_php) {
  $classes = array();
  $jetons = token_get_all($code_php);
  $count = count($jetons);
  for ($i = 2; $i < $count; $i++) {
    if (   $jetons[$i - 2][0] == T_CLASS
        && $jetons[$i - 1][0] == T_WHITESPACE
        && $jetons[$i][0] == T_STRING) {

        $nom_classe = $jetons[$i][1];
        $classes[] = $nom_classe;
    }
  }
  return $classes;
}

0 votes

Tous les jetons ne sont pas des tableaux, donc cela peut provoquer quelques avertissements.

0 votes

@hakre : Ce ne serait pas le cas car si $tokens[$i] est une chaîne de caractères, la syntaxe $tokens[i][0] est toujours autorisée.

0 votes

Bien, mais je suggère d'utiliser === pour les comparaisons dans ce cas.

16voto

cletus Points 276888

Si vous souhaitez simplement vérifier un fichier sans le charger, utilisez token_get_all() :

Fondamentalement, il s'agit d'une simple machine à états finis. En PHP, la séquence de jetons sera :

  • T_CLASS : mot-clé 'class' ;
  • T_WHITESPACE : espace(s) après 'class' ;
  • T_STRING : nom de la classe.

Donc ce code gérera très bien les espaces ou les sauts de ligne bizarres que vous pourriez obtenir car il utilise le même analyseur que PHP utilise pour exécuter le fichier. Si token_get_all() ne peut pas l'analyser, PHP non plus.

En passant, vous pouvez utiliser token_name() pour convertir un numéro de jeton en son nom de constante.

Voici mon c2.php :

Sortie :

Classe trouvée : MyClass
Classe trouvée : MyOtherClass

0 votes

Veuillez noter que quelque chose comme User::class est une syntaxe valide pour récupérer la chaîne FQCN (comme "App\Model\User") depuis PHP 5.5. Vous devez donc soit vérifier explicitement le T_WHITESPACE (et rien d'autre) entre les deux, soit vérifier si le T_COLON est absent avant le T_CLASS.

5voto

frosty22 Points 82

J'avais besoin d'analyser des classes à partir d'un fichier avec des espaces de noms, donc j'ai modifié le code. Si quelqu'un en a aussi besoin, le voici :

public function getPhpClasses($phpcode) {
    $classes = array();

    $namespace = 0;  
    $tokens = token_get_all($phpcode); 
    $count = count($tokens); 
    $dlm = false;
    for ($i = 2; $i < $count; $i++) { 
        if ((isset($tokens[$i - 2][1]) && ($tokens[$i - 2][1] == "phpnamespace" || $tokens[$i - 2][1] == "namespace")) || 
            ($dlm && $tokens[$i - 1][0] == T_NS_SEPARATOR && $tokens[$i][0] == T_STRING)) { 
            if (!$dlm) $namespace = 0; 
            if (isset($tokens[$i][1])) {
                $namespace = $namespace ? $namespace . "\\" . $tokens[$i][1] : $tokens[$i][1];
                $dlm = true; 
            }   
        }       
        elseif ($dlm && ($tokens[$i][0] != T_NS_SEPARATOR) && ($tokens[$i][0] != T_STRING)) {
            $dlm = false; 
        } 
        if (($tokens[$i - 2][0] == T_CLASS || (isset($tokens[$i - 2][1]) && $tokens[$i - 2][1] == "phpclass")) 
                && $tokens[$i - 1][0] == T_WHITESPACE && $tokens[$i][0] == T_STRING) {
            $class_name = $tokens[$i][1]; 
            if (!isset($classes[$namespace])) $classes[$namespace] = array();
            $classes[$namespace][] = $class_name;
        }
    } 
    return $classes;
}

0 votes

N'a pas réussi à faire fonctionner votre extrait avec des fichiers comportant plusieurs espaces de noms

4voto

Tivie Points 4581

Mon extrait aussi. Peut analyser des fichiers avec plusieurs classes, interfaces, tableaux et espaces de noms. Renvoie un tableau avec des classes+types (classe, interface, abstraite) divisés par des espaces de noms.

= 0 && $tokens[$i - 2][0] == T_CLASS && $tokens[$i - 1][0] == T_WHITESPACE && $tokens[$i][0] == T_STRING) 
            {
                if($i-4 >=0 && $tokens[$i - 4][0] == T_ABSTRACT)
                {
                    $classes[$ii][] = array('name' => $tokens[$i][1], 'type' => 'CLASSE ABSTRAITE');
                }
                else
                {
                    $classes[$ii][] = array('name' => $tokens[$i][1], 'type' => 'CLASSE');
                }
            }
            elseif ($i-2 >= 0 && $tokens[$i - 2][0] == T_INTERFACE && $tokens[$i - 1][0] == T_WHITESPACE && $tokens[$i][0] == T_STRING)
            {
                $classes[$ii][] = array('name' => $tokens[$i][1], 'type' => 'INTERFACE');
            }
        }
        error_reporting($er);
        if (empty($classes)) return NULL;

        if(!empty($nsPos))
        {
            foreach($nsPos as $k => $p)
            {
                $ns = '';
                for($i = $p['start'] + 1; $i < $p['end']; $i++)
                    $ns .= $tokens[$i][1];

                $ns = trim($ns);
                $final[$k] = array('namespace' => $ns, 'classes' => $classes[$k+1]);
            }
            $classes = $final;
        }
        return $classes;
    }

Produit quelque chose comme ceci...

array
  'namespace' => string 'test\foo' (length=8)
  'classes' => 
    array
      0 => 
        array
          'name' => string 'bar' (length=3)
          'type' => string 'CLASSE' (length=6)
      1 => 
        array
          'name' => string 'baz' (length=3)
          'type' => string 'INTERFACE' (length=9)
array
  'namespace' => string 'this\is\a\really\big\namespace\for\testing\dont\you\think' (length=57)
  'classes' => 
    array
      0 => 
        array
          'name' => string 'yes_it_is' (length=9)
          'type' => string 'CLASSE' (length=6)
      1 => 
        array
          'name' => string 'damn_too_big' (length=12)
          'type' => string 'CLASSE ABSTRAITE' (length=15)
      2 => 
        array
          'name' => string 'fogo' (length=6)
          'type' => string 'INTERFACE' (length=9)

Peut aider quelqu'un !

4voto

Ondrej Machulda Points 393

Ou vous pouvez facilement utiliser AnnotationsParser de Nette\Reflection (installable via composer):

use Nette\Reflection\AnnotationsParser;
$classes = AnnotationsParser::parsePhp(file_get_contents($fileName));
var_dump($classes);

La sortie ressemblerait alors à ceci:

array(1) {
  ["Votre\Nom\De\Classe"] =>
  array(...) {
      // propriété => commentaire
  },
  ["Votre\Deuxième\Classe"] =>
  array(...) {
      // propriété => commentaire
  },
}

La méthode parsePhp() fait essentiellement quelque chose de similaire aux exemples dans d'autres réponses, mais vous n'avez pas à déclarer ni à tester l'analyse vous-même.

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