mirror of
https://github.com/danog/PHP-Parser.git
synced 2024-11-26 20:04:48 +01:00
Add NameResolver
The NameResolver visitor tries to resolve all names to fully qualified names. It will resolve all non-dynamic names, apart from unqualified function and constant names. The latter can not be resolved properly without running the code.
This commit is contained in:
parent
62183807ee
commit
ab72f98570
121
lib/PHPParser/NodeVisitor/NameResolver.php
Normal file
121
lib/PHPParser/NodeVisitor/NameResolver.php
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class PHPParser_NodeVisitor_NameResolver extends PHPParser_NodeVisitorAbstract
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var null|PHPParser_Node_Name Current namespace
|
||||||
|
*/
|
||||||
|
protected $namespace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array Currently defined namespace and class aliases
|
||||||
|
*/
|
||||||
|
protected $aliases;
|
||||||
|
|
||||||
|
public function beforeTraverse(array $nodes) {
|
||||||
|
$this->namespace = null;
|
||||||
|
$this->aliases = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enterNode(PHPParser_Node $node) {
|
||||||
|
if ($node instanceof PHPParser_Node_Stmt_Namespace) {
|
||||||
|
$this->namespace = $node->name;
|
||||||
|
$this->aliases = array();
|
||||||
|
} elseif ($node instanceof PHPParser_Node_Stmt_UseUse) {
|
||||||
|
if (isset($this->aliases[$node->alias])) {
|
||||||
|
throw new PHPParser_Error(
|
||||||
|
sprintf(
|
||||||
|
'Cannot use %s as %s because the name is already in use',
|
||||||
|
$node->name, $node->alias
|
||||||
|
),
|
||||||
|
$node->getLine()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->aliases[$node->alias] = $node->name;
|
||||||
|
} elseif ($node instanceof PHPParser_Node_Stmt_Class) {
|
||||||
|
if (null !== $node->extends) {
|
||||||
|
$node->extends = $this->resolveClassName($node->extends);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($node->implements as &$interface) {
|
||||||
|
$interface = $this->resolveClassName($interface);
|
||||||
|
}
|
||||||
|
} elseif ($node instanceof PHPParser_Node_Stmt_Interface) {
|
||||||
|
foreach ($node->extends as &$interface) {
|
||||||
|
$interface = $this->resolveClassName($interface);
|
||||||
|
}
|
||||||
|
} elseif ($node instanceof PHPParser_Node_Expr_StaticCall
|
||||||
|
|| $node instanceof PHPParser_Node_Expr_StaticPropertyFetch
|
||||||
|
|| $node instanceof PHPParser_Node_Expr_ClassConstFetch
|
||||||
|
|| $node instanceof PHPParser_Node_Expr_New
|
||||||
|
|| $node instanceof PHPParser_Node_Expr_Instanceof
|
||||||
|
) {
|
||||||
|
$node->class = $this->resolveClassName($node->class);
|
||||||
|
} elseif ($node instanceof PHPParser_Node_Expr_FuncCall) {
|
||||||
|
$node->name = $this->resolveOtherName($node->name);
|
||||||
|
} elseif ($node instanceof PHPParser_Node_Expr_ConstFetch) {
|
||||||
|
$node->name = $this->resolveOtherName($node->name);
|
||||||
|
} elseif ($node instanceof PHPParser_Node_Param
|
||||||
|
&& $node->type instanceof PHPParser_Node_Name
|
||||||
|
) {
|
||||||
|
$node->type = $this->resolveClassName($node->type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function resolveClassName(PHPParser_Node $name) {
|
||||||
|
// can't resolve dynamic names at compile-time
|
||||||
|
if (!$name instanceof PHPParser_Node_Name) {
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't resolve special class names
|
||||||
|
if (in_array((string) $name, array('self', 'parent', 'static'))) {
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fully qualified names are already resolved
|
||||||
|
if ($name->isFullyQualified()) {
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve aliases (for non-relative names)
|
||||||
|
if (!$name->isRelative() && isset($this->aliases[$name->getFirst()])) {
|
||||||
|
$name->setFirst($this->aliases[$name->getFirst()]);
|
||||||
|
// if no alias exists prepend current namespace
|
||||||
|
} elseif (null !== $this->namespace) {
|
||||||
|
$name->prepend($this->namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return fully qualified name
|
||||||
|
return new PHPParser_Node_Name_FullyQualified($name->parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function resolveOtherName(PHPParser_Node $name) {
|
||||||
|
// can't resolve dynamic names at compile-time
|
||||||
|
if (!$name instanceof PHPParser_Node_Name) {
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fully qualified names are already resolved
|
||||||
|
if ($name->isFullyQualified()) {
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve aliases for qualified names
|
||||||
|
if ($name->isQualified() && isset($this->aliases[$name->getFirst()])) {
|
||||||
|
$name->setFirst($this->aliases[$name->getFirst()]);
|
||||||
|
return new PHPParser_Node_Name_FullyQualified($name->parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no alias exists prepend namespace for qualified anf relative names
|
||||||
|
if (!$name->isUnqualified()) {
|
||||||
|
if (null !== $this->namespace) {
|
||||||
|
$name->prepend($this->namespace);
|
||||||
|
}
|
||||||
|
return new PHPParser_Node_Name_FullyQualified($name->parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
}
|
83
test/PHPParser/Tests/NodeVisitor/NameResolverTest.php
Normal file
83
test/PHPParser/Tests/NodeVisitor/NameResolverTest.php
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class PHPParser_Tests_NodeVisitor_NameResolverTest extends PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testResolve() {
|
||||||
|
$code = <<<EOC
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Foo {
|
||||||
|
use Hallo as Hi;
|
||||||
|
|
||||||
|
new Bar();
|
||||||
|
new Hi();
|
||||||
|
new Hi\\Bar();
|
||||||
|
new \\Bar();
|
||||||
|
new namespace\\Bar();
|
||||||
|
|
||||||
|
bar();
|
||||||
|
hi();
|
||||||
|
Hi\\bar();
|
||||||
|
foo\\bar();
|
||||||
|
\\bar();
|
||||||
|
namespace\\bar();
|
||||||
|
}
|
||||||
|
namespace {
|
||||||
|
use Hallo as Hi;
|
||||||
|
|
||||||
|
new Bar();
|
||||||
|
new Hi();
|
||||||
|
new Hi\\Bar();
|
||||||
|
new \\Bar();
|
||||||
|
new namespace\\Bar();
|
||||||
|
|
||||||
|
bar();
|
||||||
|
hi();
|
||||||
|
Hi\\bar();
|
||||||
|
foo\\bar();
|
||||||
|
\\bar();
|
||||||
|
namespace\\bar();
|
||||||
|
}
|
||||||
|
EOC;
|
||||||
|
$expectedCode = <<<EOC
|
||||||
|
namespace Foo {
|
||||||
|
use Hallo as Hi;
|
||||||
|
new \\Foo\\Bar();
|
||||||
|
new \\Hallo();
|
||||||
|
new \\Hallo\\Bar();
|
||||||
|
new \\Bar();
|
||||||
|
new \\Foo\\Bar();
|
||||||
|
bar();
|
||||||
|
hi();
|
||||||
|
\\Hallo\\bar();
|
||||||
|
\\Foo\\foo\\bar();
|
||||||
|
\\bar();
|
||||||
|
\\Foo\\bar();
|
||||||
|
}
|
||||||
|
namespace {
|
||||||
|
use Hallo as Hi;
|
||||||
|
new \\Bar();
|
||||||
|
new \\Hallo();
|
||||||
|
new \\Hallo\\Bar();
|
||||||
|
new \\Bar();
|
||||||
|
new \\Bar();
|
||||||
|
bar();
|
||||||
|
hi();
|
||||||
|
\\Hallo\\bar();
|
||||||
|
\\foo\\bar();
|
||||||
|
\\bar();
|
||||||
|
\\bar();
|
||||||
|
}
|
||||||
|
EOC;
|
||||||
|
|
||||||
|
$parser = new PHPParser_Parser;
|
||||||
|
$prettyPrinter = new PHPParser_PrettyPrinter_Zend;
|
||||||
|
$traverser = new PHPParser_NodeTraverser;
|
||||||
|
$traverser->addVisitor(new PHPParser_NodeVisitor_NameResolver);
|
||||||
|
|
||||||
|
$stmts = $parser->parse(new PHPParser_Lexer($code));
|
||||||
|
$stmts = $traverser->traverse($stmts);
|
||||||
|
|
||||||
|
$this->assertEquals($expectedCode, $prettyPrinter->prettyPrint($stmts));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user