3 namespace PhpParser\NodeVisitor;
7 use PhpParser\Node\Expr;
8 use PhpParser\Node\Name;
9 use PhpParser\Node\Stmt;
11 class NameResolverTest extends \PHPUnit_Framework_TestCase
13 private function canonicalize($string) {
14 return str_replace("\r\n", "\n", $string);
18 * @covers PhpParser\NodeVisitor\NameResolver
20 public function testResolveNames() {
57 use function foo\bar as baz;
58 use const foo\BAR as BAZ;
81 use function X\T\{b\c, d\e};
82 use const Y\T\{B\C, D\E};
83 use Z\T\{G, function f, const K};
99 $expectedCode = <<<'EOC'
129 use function foo\bar as baz;
130 use const foo\BAR as BAZ;
151 use function X\T\{b\c, d\e};
152 use const Y\T\{B\C, D\E};
153 use Z\T\{G, function f, const K};
168 $parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative);
169 $prettyPrinter = new PhpParser\PrettyPrinter\Standard;
170 $traverser = new PhpParser\NodeTraverser;
171 $traverser->addVisitor(new NameResolver);
173 $stmts = $parser->parse($code);
174 $stmts = $traverser->traverse($stmts);
177 $this->canonicalize($expectedCode),
178 $prettyPrinter->prettyPrint($stmts)
183 * @covers PhpParser\NodeVisitor\NameResolver
185 public function testResolveLocations() {
190 class A extends B implements C, D {
198 interface A extends C, D {
199 public function a(A $a) : A;
202 function fn(A $a) : A {}
203 function fn2(array $a) : array {}
204 function(A $a) : A {};
206 function fn3(?A $a) : ?A {}
207 function fn4(?array $a) : ?array {}
224 $expectedCode = <<<'EOC'
227 class A extends \NS\B implements \NS\C, \NS\D
229 use \NS\E, \NS\F, \NS\G {
232 \NS\E::j insteadof \NS\F, \NS\G;
235 interface A extends \NS\C, \NS\D
237 public function a(\NS\A $a) : \NS\A;
239 function fn(\NS\A $a) : \NS\A
242 function fn2(array $a) : array
245 function (\NS\A $a) : \NS\A {
247 function fn3(?\NS\A $a) : ?\NS\A
250 function fn4(?array $a) : ?array
267 $parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative);
268 $prettyPrinter = new PhpParser\PrettyPrinter\Standard;
269 $traverser = new PhpParser\NodeTraverser;
270 $traverser->addVisitor(new NameResolver);
272 $stmts = $parser->parse($code);
273 $stmts = $traverser->traverse($stmts);
276 $this->canonicalize($expectedCode),
277 $prettyPrinter->prettyPrint($stmts)
281 public function testNoResolveSpecialName() {
282 $stmts = array(new Node\Expr\New_(new Name('self')));
284 $traverser = new PhpParser\NodeTraverser;
285 $traverser->addVisitor(new NameResolver);
287 $this->assertEquals($stmts, $traverser->traverse($stmts));
290 public function testAddDeclarationNamespacedName() {
292 new Stmt\Class_('A'),
293 new Stmt\Interface_('B'),
294 new Stmt\Function_('C'),
295 new Stmt\Const_(array(
296 new Node\Const_('D', new Node\Scalar\LNumber(42))
298 new Stmt\Trait_('E'),
299 new Expr\New_(new Stmt\Class_(null)),
302 $traverser = new PhpParser\NodeTraverser;
303 $traverser->addVisitor(new NameResolver);
305 $stmts = $traverser->traverse([new Stmt\Namespace_(new Name('NS'), $nsStmts)]);
306 $this->assertSame('NS\\A', (string) $stmts[0]->stmts[0]->namespacedName);
307 $this->assertSame('NS\\B', (string) $stmts[0]->stmts[1]->namespacedName);
308 $this->assertSame('NS\\C', (string) $stmts[0]->stmts[2]->namespacedName);
309 $this->assertSame('NS\\D', (string) $stmts[0]->stmts[3]->consts[0]->namespacedName);
310 $this->assertSame('NS\\E', (string) $stmts[0]->stmts[4]->namespacedName);
311 $this->assertObjectNotHasAttribute('namespacedName', $stmts[0]->stmts[5]->class);
313 $stmts = $traverser->traverse([new Stmt\Namespace_(null, $nsStmts)]);
314 $this->assertSame('A', (string) $stmts[0]->stmts[0]->namespacedName);
315 $this->assertSame('B', (string) $stmts[0]->stmts[1]->namespacedName);
316 $this->assertSame('C', (string) $stmts[0]->stmts[2]->namespacedName);
317 $this->assertSame('D', (string) $stmts[0]->stmts[3]->consts[0]->namespacedName);
318 $this->assertSame('E', (string) $stmts[0]->stmts[4]->namespacedName);
319 $this->assertObjectNotHasAttribute('namespacedName', $stmts[0]->stmts[5]->class);
322 public function testAddRuntimeResolvedNamespacedName() {
324 new Stmt\Namespace_(new Name('NS'), array(
325 new Expr\FuncCall(new Name('foo')),
326 new Expr\ConstFetch(new Name('FOO')),
328 new Stmt\Namespace_(null, array(
329 new Expr\FuncCall(new Name('foo')),
330 new Expr\ConstFetch(new Name('FOO')),
334 $traverser = new PhpParser\NodeTraverser;
335 $traverser->addVisitor(new NameResolver);
336 $stmts = $traverser->traverse($stmts);
338 $this->assertSame('NS\\foo', (string) $stmts[0]->stmts[0]->name->getAttribute('namespacedName'));
339 $this->assertSame('NS\\FOO', (string) $stmts[0]->stmts[1]->name->getAttribute('namespacedName'));
341 $this->assertFalse($stmts[1]->stmts[0]->name->hasAttribute('namespacedName'));
342 $this->assertFalse($stmts[1]->stmts[1]->name->hasAttribute('namespacedName'));
346 * @dataProvider provideTestError
348 public function testError(Node $stmt, $errorMsg) {
349 $this->setExpectedException('PhpParser\Error', $errorMsg);
351 $traverser = new PhpParser\NodeTraverser;
352 $traverser->addVisitor(new NameResolver);
353 $traverser->traverse(array($stmt));
356 public function provideTestError() {
360 new Stmt\UseUse(new Name('A\B'), 'B', 0, array('startLine' => 1)),
361 new Stmt\UseUse(new Name('C\D'), 'B', 0, array('startLine' => 2)),
362 ), Stmt\Use_::TYPE_NORMAL),
363 'Cannot use C\D as B because the name is already in use on line 2'
367 new Stmt\UseUse(new Name('a\b'), 'b', 0, array('startLine' => 1)),
368 new Stmt\UseUse(new Name('c\d'), 'B', 0, array('startLine' => 2)),
369 ), Stmt\Use_::TYPE_FUNCTION),
370 'Cannot use function c\d as B because the name is already in use on line 2'
374 new Stmt\UseUse(new Name('A\B'), 'B', 0, array('startLine' => 1)),
375 new Stmt\UseUse(new Name('C\D'), 'B', 0, array('startLine' => 2)),
376 ), Stmt\Use_::TYPE_CONSTANT),
377 'Cannot use const C\D as B because the name is already in use on line 2'
380 new Expr\New_(new Name\FullyQualified('self', array('startLine' => 3))),
381 "'\\self' is an invalid class name on line 3"
384 new Expr\New_(new Name\Relative('self', array('startLine' => 3))),
385 "'\\self' is an invalid class name on line 3"
388 new Expr\New_(new Name\FullyQualified('PARENT', array('startLine' => 3))),
389 "'\\PARENT' is an invalid class name on line 3"
392 new Expr\New_(new Name\Relative('STATIC', array('startLine' => 3))),
393 "'\\STATIC' is an invalid class name on line 3"
398 public function testClassNameIsCaseInsensitive()
407 $parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative);
408 $stmts = $parser->parse($source);
410 $traverser = new PhpParser\NodeTraverser;
411 $traverser->addVisitor(new NameResolver);
413 $stmts = $traverser->traverse($stmts);
416 $this->assertSame(array('Bar', 'Baz'), $stmt->stmts[1]->expr->class->parts);
419 public function testSpecialClassNamesAreCaseInsensitive() {
426 public static function method()
435 $parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative);
436 $stmts = $parser->parse($source);
438 $traverser = new PhpParser\NodeTraverser;
439 $traverser->addVisitor(new NameResolver);
441 $stmts = $traverser->traverse($stmts);
442 $classStmt = $stmts[0];
443 $methodStmt = $classStmt->stmts[0]->stmts[0];
445 $this->assertSame('SELF', (string)$methodStmt->stmts[0]->class);
446 $this->assertSame('PARENT', (string)$methodStmt->stmts[1]->class);
447 $this->assertSame('STATIC', (string)$methodStmt->stmts[2]->class);
450 public function testAddOriginalNames() {
451 $traverser = new PhpParser\NodeTraverser;
452 $traverser->addVisitor(new NameResolver(null, ['preserveOriginalNames' => true]));
454 $n1 = new Name('Bar');
455 $n2 = new Name('bar');
457 new Stmt\Namespace_(new Name('Foo'), [
458 new Expr\ClassConstFetch($n1, 'FOO'),
459 new Expr\FuncCall($n2),
463 $stmts = $traverser->traverse($origStmts);
465 $this->assertSame($n1, $stmts[0]->stmts[0]->class->getAttribute('originalName'));
466 $this->assertSame($n2, $stmts[0]->stmts[1]->name->getAttribute('originalName'));