私は単純な未定義の変数を検出するために一緒にハッキング小さなスクリプト。
<?php
error_reporting(E_ALL);
$dir = './foo';
require_once './lib/bootstrap.php';
class Scope {
protected $stack;
protected $pos;
public function __construct() {
$this->stack = array();
$this->pos = -1;
}
public function addVar($name) {
$this->stack[$this->pos][$name] = true;
}
public function hasVar($name) {
return isset($this->stack[$this->pos][$name]);
}
public function pushScope() {
$this->stack[++$this->pos] = array();
}
public function popScope() {
--$this->pos;
}
}
class UndefinedVariableVisitor extends PHPParser_NodeVisitorAbstract {
protected $scope;
protected $parser;
protected $traverser;
public function __construct(Scope $scope, PHPParser_Parser $parser, PHPParser_NodeTraverser $traverser) {
$this->scope = $scope;
$this->parser = $parser;
$this->traverser = $traverser;
}
public function enterNode(PHPParser_Node $node) {
if (($node instanceof PHPParser_Node_Expr_Assign || $node instanceof PHPParser_Node_Expr_AssignRef)
&& $node->var instanceof PHPParser_Node_Expr_Variable
&& is_string($node->var->name)
) {
$this->scope->addVar($node->var->name);
} elseif ($node instanceof PHPParser_Node_Stmt_Global || $node instanceof PHPParser_Node_Stmt_Static) {
foreach ($node->vars as $var) {
if (is_string($var->name)) {
$this->scope->addVar($var->name);
}
}
} elseif ($node instanceof PHPParser_Node_Expr_Variable && is_string($node->name)) {
if (!$this->scope->hasVar($node->name)) {
echo 'Undefined variable $' . $node->name . ' on line ' . $node->getLine() . "\n";
}
} elseif ($node instanceof PHPParser_Node_Stmt_Function || $node instanceof PHPParser_Node_Stmt_ClassMethod) {
$this->scope->pushScope();
// params are always available
foreach ($node->params as $param) {
$this->scope->addVar($param->name);
}
// methods always have $this
if ($node instanceof PHPParser_Node_Stmt_ClassMethod) {
$this->scope->addVar('this');
}
} elseif ($node instanceof PHPParser_Node_Expr_Include && $node->expr instanceof PHPParser_Node_Scalar_String) {
$file = $node->expr->value;
$code = file_get_contents($file);
$stmts = $this->parser->parse($code);
// for includes within the file
$cwd = getcwd();
chdir(dirname($file));
$this->traverser->traverse($stmts);
chdir($cwd);
}
}
public function leaveNode(PHPParser_Node $node) {
if ($node instanceof PHPParser_Node_Stmt_Function || $node instanceof PHPParser_Node_Stmt_ClassMethod) {
$this->scope->popScope();
}
}
}
$parser = new PHPParser_Parser(new PHPParser_Lexer());
$scope = new Scope;
$traverser = new PHPParser_NodeTraverser;
$traverser->addVisitor(new UndefinedVariableVisitor($scope, $parser, $traverser));
foreach (new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir),
RecursiveIteratorIterator::LEAVES_ONLY)
as $file
) {
if (!preg_match('/\.php$/', $file)) continue;
echo 'Checking ' . $file . ':', "\n";
$code = file_get_contents($file);
$stmts = $parser->parse($code);
// for includes within the file
$cwd = getcwd();
chdir(dirname($file));
$scope->pushScope();
$traverser->traverse($stmts);
$scope->popScope();
chdir($cwd);
echo "\n";
}
それはちょうど非常に基本的な実装だと、私は広範囲にそれをテストしていないが、それは$GLOBALS
と$$varVars
と野生行っていないスクリプトのために働く必要があります:あなたはこのためPHP-Parserが必要になります。基本的なインクルード解決を行います。
可変変数と 'extract()'のような関数を考慮する必要があるので難しいでしょう –
私は「いいえ」と考えています。 @MikeBが正しく指摘しているように、 'extract()'や変数変数(さらに '$ GLOBALS')のようなものは、コードを実際には実行しないコード解析のための巨大なスパナです。はい、バッチでスクリプトを実行して、設定されていない変数の使用に関する苦情を探すことができますが、コードはちょうど壊れている可能性があります(この場合はまだ修正が必要です)。残念ながら、ここでは手作業によるアプローチがおそらく唯一の選択肢です。お楽しみください... – DaveRandom
PHP 5.4.0から 'register_globals'が削除されましたので注意してください。 – Florent