난 그냥 간단하게 정의되지 않은 변수를 감지하기 위해 함께 해킹 작은 스크립트.
<?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 ')는 코드를 실제로 실행하지 않는 코드 분석을 위해 거대한 스패너를 던졌습니다. 네, 일괄 처리로 스크립트를 실행 해보고 설정되지 않은 변수의 사용에 대한 불만을 찾아 볼 수는 있지만 어쨌든 코드가 깨져있을 가능성은 항상 있습니다 (이 경우 아직 수정이 필요함). 불행히도 수동 접근 방식이 여기있는 유일한 옵션 일 것입니다. Enjoy ... – DaveRandom
PHP 5.4.0부터'register_globals'가 삭제되었습니다. – Florent