2010-11-22 7 views
2

Dans un système que nous allons utiliser, il y a une fonction appelée "uses". Si vous êtes familier avec pascal, la clause uses est l'endroit où vous indiquez à votre programme quelles dépendances il a (comme C et PHP). Cette fonction est utilisée pour contrôler l'inclusion des fichiers autre que include (_once) ou require (_once).PHP Constante chaîne de caractères jeton

Dans le cadre des procédures de test, j'ai besoin d'écrire un outil de visualisation des dépendances pour les fichiers chargés statiquement.

Loaded Exemple statiquement: uses('core/core.php','core/security.php');

Loaded Exemple Dynamiquement: uses('exts/database.'.$driver.'.php');

Je dois filtrer les cas de charge dynamique, car le code est testé de manière statique, et non lors de l'exécution.

C'est le code que je utilise à ce moment:

$inuses=false; // whether currently in uses function or not 
$uses=array(); // holds dependencies (line=>file) 
$tknbuf=array(); // last token 
foreach(token_get_all(file_get_contents($file)) as $token){ 
    // detect uses function 
    if(!$inuses && is_array($token) && $token[0]==T_STRING && $token[1]=='uses')$inuses=true; 
    // detect uses argument (dependency file) 
    if($inuses && is_array($token) && $token[0]==T_CONSTANT_ENCAPSED_STRING)$tknbuf=$token; 
    // detect the end of uses function 
    if($inuses && is_string($token) && $token==')'){ 
     $inuses=false; 
     isset($uses[$tknbuf[2]]) 
      ? $uses[$tknbuf[2]][]=$tknbuf[1] 
      : $uses[$tknbuf[2]]=array($tknbuf[1]); 
    } 
    // a new argument (dependency) is found 
    if($inuses && is_string($token) && $token==',') 
     isset($uses[$tknbuf[2]]) 
      ? $uses[$tknbuf[2]][]=$tknbuf[1] 
      : $uses[$tknbuf[2]]=array($tknbuf[1]); 
} 

Note: Il peut être utile de savoir que j'utilise un moteur d'état pour détecter les arguments.

Mon problème? Comme il y a toutes sortes d'arguments qui peuvent entrer dans la fonction, il est très difficile de bien faire les choses. Peut-être que je n'utilise pas la bonne approche, cependant, je suis assez sûr que l'utilisation token_get_all est le meilleur dans ce cas. Alors peut-être que le problème est mon moteur d'état qui n'est vraiment pas très bon. J'ai peut-être manqué la solution de facilité, je pensais que j'aurais une critique par les pairs.

Editer: J'ai pris l'approche d'expliquer ce que je fais cette fois, mais pas exactement ce que je veux. Mettez en mots simples, j'ai besoin d'obtenir un tableau des arguments transmis à une fonction nommée "uses". La chose est que je suis un peu spécifique sur les arguments; J'ai juste besoin d'un tableau de chaînes droites, pas de code dynamique du tout (constantes, variables, appels de fonctions ...).

+1

Puis-je demander pourquoi ne pas utiliser simplement le chargement automatique des classes? – Mchl

+0

Vraiment, oubliez le regex = mème diabolique. Ceci est ** un cas d'utilisation pour eux. – mario

+0

@Mchl - Parce que cela ne concerne pas spécifiquement les classes. @Mario - Certes, je ne suis pas très bon avec les expressions rationnelles. Dans les deux cas, je regex pour analyser le code PHP serait difficile à créer et à maintenir, et assez lent à fonctionner. – Christian

Répondre

1

OK Je l'ai fonctionné. Juste quelques corrections mineures au moteur d'état. En résumé, les jetons d'argument sont tamponnés au lieu d'être placés directement dans le tableau uses. Ensuite, à chaque ',' ou ')', je vérifie si le jeton est valide ou non et l'ajoute au tableau uses.

$inuses=false; // whether currently in uses function or not 
$uses=array(); // holds dependencies (line=>file) 
$tknbuf=array(); // last token 
$tknbad=false; // whether last token is good or not 
foreach(token_get_all(file_get_contents($file)) as $token){ 
    // detect uses function 
    if(!$inuses && is_array($token) && $token[0]==T_STRING && $token[1]=='uses')$inuses=true; 
    // token found, put it in buffer 
    if($inuses && is_array($token) && $token[0]==T_CONSTANT_ENCAPSED_STRING)$tknbuf=$token; 
    // end-of-function found check buffer and throw into $uses 
    if($inuses && is_string($token) && $token==')'){ 
     $inuses=false; 
     if(count($tknbuf)==3 && !$tknbad)isset($GLOBALS['uses'][$file][$tknbuf[2]]) 
       ? $GLOBALS['uses'][$file][$tknbuf[2]][]=$tknbuf[1] 
       : $GLOBALS['uses'][$file][$tknbuf[2]]=array($tknbuf[1]); 
     $tknbuf=array(); $tknbad=false; 
    } 
    // end-of-argument check token and add to $uses 
    if($inuses && is_string($token) && $token==','){ 
     if(count($tknbuf)==3 && !$tknbad)isset($GLOBALS['uses'][$file][$tknbuf[2]]) 
      ? $GLOBALS['uses'][$file][$tknbuf[2]][]=$tknbuf[1] 
      : $GLOBALS['uses'][$file][$tknbuf[2]]=array($tknbuf[1]); 
     $tknbuf=array(); $tknbad=false; 
    } 
    // if current token is not an a simple string, flag all tokens as bad 
    if($inuses && is_array($token) && $token[0]!=T_CONSTANT_ENCAPSED_STRING)$tknbad=true; 
} 

Modifier: En fait, il est toujours défectueux (un problème différent cependant). Mais la nouvelle idée que j'ai eu devrait bien fonctionner.

+0

Une fois dans une fonction 'uses', je répète: a) lire le jeton, b) si token == '(', ajouter un au compteur, c) si counter> 0: if ')', alors diminuer le compteur else ignorer. d) si compteur == 0: si ')' est fait. si ',' recommencez; Sinon, ajoutez un jeton à la liste. Si la liste actuelle (après l'étape d) n'est rien d'autre qu'une chaîne de caractères constante, vous pouvez l'ajouter aux dépendances. Encore une fois, pas une preuve infaillible, mais probablement assez bon. – Matthew

1

Utilisation d'expressions régulières:

<?php 
preg_match_all('/uses\s*\((.+)\s*\)/', 
    file_get_contents('uses.php'), $matches, PREG_SET_ORDER); 

foreach ($matches as $set) { 
    list($full, $match) = $set; 

    echo "$full\n"; 

    // try to remove function arguments 
    $new = $match; 
    do { 
    $match = $new; 
    $new = preg_replace('/\([^()]*\)/', '', $match); 
    } while ($new != $match); 

    // iterate over each of the uses() args 
    foreach (explode(',', $match) as $arg) { 
    $arg = trim($arg); 
    if (($arg[0] == "'" || $arg[0] == '"') && substr($arg,-1) == $arg[0]) 
    echo " ".substr($arg,1,-1)."\n"; 
    } 
} 
?> 

course contre:

uses('bar.php', 'test.php', $foo->bar()); 
uses(bar('test.php'), 'file.php'); 
uses(bar(foo('a','b','c')), zed()); 

rendements:

uses('bar.php', 'test.php', $foo->bar()) 
    bar.php 
    test.php 
uses(bar('test.php'), 'file.php') 
    file.php 
uses(bar(foo('a','b','c')), zed()) 

De toute évidence, il a des limites et des hypothèses, mais si vous savez comment le code est appelé , ça pourrait être suffisant.

Questions connexes