Токен с параметрами строки PHP - PullRequest
2 голосов
/ 22 ноября 2010

В системе, которую мы будем использовать, есть функция, которая называется «использует». Если вы знакомы с pascal, в разделе метки использования вы указываете своей программе, какие у нее зависимости (аналогично C и PHP). Эта функция используется для дальнейшего управления включением файлов, кроме include (_once) или require (_once).

В рамках процедур тестирования мне нужно написать инструмент визуализации зависимостей для статически загружаемых файлов.

Статически загруженный пример: uses('core/core.php','core/security.php');

Пример с динамической загрузкой: uses('exts/database.'.$driver.'.php');

Мне нужно отфильтровать случаи динамической загрузки, потому что код тестируется статически, а не во время работы.

Это код, который я сейчас использую:

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

Примечание. Может быть полезно узнать, что я использую движок состояний для определения аргументов.

Моя проблема? Поскольку в функцию могут входить всевозможные аргументы, очень трудно сделать это правильно. Возможно, я не использую правильный подход, но я уверен, что использование token_get_all является лучшим в этом случае. Так что, возможно, проблема в моем государственном двигателе, который на самом деле не так хорош. Возможно, я упустил легкий выход, подумал, что получу некоторую экспертную оценку.

Редактировать: Я попытался объяснить, что я делаю в этот раз, но не совсем то, что я хочу. Проще говоря, мне нужно получить массив аргументов, передаваемых в функцию с именем «использует». Дело в том, что я немного конкретен в аргументах; Мне нужен только массив прямых строк, вообще никакого динамического кода (константы, переменные, вызовы функций ...).

Ответы [ 2 ]

1 голос
/ 22 ноября 2010

Использование регулярных выражений:

<?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";
  }
}
?>

Бег против:

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

Урожайность:

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())

Очевидно, у него есть ограничения и предположения, но если вы знаете, как вызывается код, этого может быть достаточно.

1 голос
/ 22 ноября 2010

ОК, у меня все получилось. Просто некоторые мелкие исправления в государственном движке. Короче говоря, токены аргументов буферизуются, а не помещаются в массив метаданных напрямую. Далее, в каждом ',' или ')' я проверяю, является ли токен действительным или нет, и добавляю его в массив меток.

$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;
}

Редактировать: На самом деле это все еще неисправно (хотя другая проблема). Но новая идея, которая у меня появилась, должна сработать.

...