PHP Constant string parameters token
In a system we will be using, there is a function called "uses". If you are familiar with pascal, the uses clause is where you tell your program what dependencies it has (similar to C and PHP includes). This function is being used in order to further control file inclusion other than include(_once) or require(_once).
As part of testing procedures, I need to write a dependency visualization tool for statically loaded files.
Statically Loaded Example: uses('core/core.php','core/security.php');
Dynamically Loaded Example: uses('exts/database.'.$driver.'.php');
I need to filter out dynamic load cases because the code is tested statically, not while running.
This is the code I'm using at this time:
$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: It may help to know that I'm using a state engine to detect the arguments.
My issue? Since there are all sorts of arguments that can go in the function, it is very difficult getting it right.
Maybe I'm not using the right approach, however, I'm pretty sure using token_get_all
is the best in 开发者_如何学Pythonthis case. So maybe the issue is my state engine which really isn't that good.
I might be missing the easy way out, thought I'd get some peer review off it.
Edit: I took the approach of explaining what I'm doing this time, but not exactly what I want. Put in simple words, I need to get an array of the arguments being passed to a function named "uses". The thing is I'm a bit specific about the arguments; I only need an array of straight strings, no dynamic code at all (constants, variables, function calls...).
Using regular expressions:
<?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";
}
}
?>
Running against:
uses('bar.php', 'test.php', $foo->bar());
uses(bar('test.php'), 'file.php');
uses(bar(foo('a','b','c')), zed());
Yields:
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())
Obviously it has limitations and assumptions, but if you know how the code is called, it could be sufficient.
OK I got it working. Just some minor fixes to the state engine. In short, argument tokens are buffered instead of put in the uses array directly. Next, at each ',' or ')' I check if the token is valid or not and add it to the uses array.
$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;
}
Edit: Actually it is still faulty (a different issue though). But the new idea I've had ought to work out nicely.
精彩评论