diff options
Diffstat (limited to 'vendor/nikic/php-parser/test/PhpParser/NodeVisitor/NameResolverTest.php')
-rw-r--r-- | vendor/nikic/php-parser/test/PhpParser/NodeVisitor/NameResolverTest.php | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/vendor/nikic/php-parser/test/PhpParser/NodeVisitor/NameResolverTest.php b/vendor/nikic/php-parser/test/PhpParser/NodeVisitor/NameResolverTest.php new file mode 100644 index 0000000..a9c6cb5 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/NodeVisitor/NameResolverTest.php @@ -0,0 +1,449 @@ +<?php + +namespace PhpParser\NodeVisitor; + +use PhpParser; +use PhpParser\Node; +use PhpParser\Node\Expr; +use PhpParser\Node\Name; +use PhpParser\Node\Stmt; + +class NameResolverTest extends \PHPUnit_Framework_TestCase +{ + private function canonicalize($string) { + return str_replace("\r\n", "\n", $string); + } + + /** + * @covers PhpParser\NodeVisitor\NameResolver + */ + public function testResolveNames() { + $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(); +} +namespace Bar { + use function foo\bar as baz; + use const foo\BAR as BAZ; + use foo as bar; + + bar(); + baz(); + bar\foo(); + baz\foo(); + BAR(); + BAZ(); + BAR\FOO(); + BAZ\FOO(); + + bar; + baz; + bar\foo; + baz\foo; + BAR; + BAZ; + BAR\FOO; + BAZ\FOO; +} +namespace Baz { + use A\T\{B\C, D\E}; + use function X\T\{b\c, d\e}; + use const Y\T\{B\C, D\E}; + use Z\T\{G, function f, const K}; + + new C; + new E; + new C\D; + new E\F; + new G; + + c(); + e(); + f(); + C; + E; + K; +} +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(); +} +namespace Bar { + use function foo\bar as baz; + use const foo\BAR as BAZ; + use foo as bar; + bar(); + \foo\bar(); + \foo\foo(); + \Bar\baz\foo(); + BAR(); + \foo\bar(); + \foo\FOO(); + \Bar\BAZ\FOO(); + bar; + baz; + \foo\foo; + \Bar\baz\foo; + BAR; + \foo\BAR; + \foo\FOO; + \Bar\BAZ\FOO; +} +namespace Baz { + use A\T\{B\C, D\E}; + use function X\T\{b\c, d\e}; + use const Y\T\{B\C, D\E}; + use Z\T\{G, function f, const K}; + new \A\T\B\C(); + new \A\T\D\E(); + new \A\T\B\C\D(); + new \A\T\D\E\F(); + new \Z\T\G(); + \X\T\b\c(); + \X\T\d\e(); + \Z\T\f(); + \Y\T\B\C; + \Y\T\D\E; + \Z\T\K; +} +EOC; + + $parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative); + $prettyPrinter = new PhpParser\PrettyPrinter\Standard; + $traverser = new PhpParser\NodeTraverser; + $traverser->addVisitor(new NameResolver); + + $stmts = $parser->parse($code); + $stmts = $traverser->traverse($stmts); + + $this->assertSame( + $this->canonicalize($expectedCode), + $prettyPrinter->prettyPrint($stmts) + ); + } + + /** + * @covers PhpParser\NodeVisitor\NameResolver + */ + public function testResolveLocations() { + $code = <<<'EOC' +<?php +namespace NS; + +class A extends B implements C, D { + use E, F, G { + f as private g; + E::h as i; + E::j insteadof F, G; + } +} + +interface A extends C, D { + public function a(A $a) : A; +} + +function fn(A $a) : A {} +function fn2(array $a) : array {} +function(A $a) : A {}; + +function fn3(?A $a) : ?A {} +function fn4(?array $a) : ?array {} + +A::b(); +A::$b; +A::B; +new A; +$a instanceof A; + +namespace\a(); +namespace\A; + +try { + $someThing; +} catch (A $a) { + $someThingElse; +} +EOC; + $expectedCode = <<<'EOC' +namespace NS; + +class A extends \NS\B implements \NS\C, \NS\D +{ + use \NS\E, \NS\F, \NS\G { + f as private g; + \NS\E::h as i; + \NS\E::j insteadof \NS\F, \NS\G; + } +} +interface A extends \NS\C, \NS\D +{ + public function a(\NS\A $a) : \NS\A; +} +function fn(\NS\A $a) : \NS\A +{ +} +function fn2(array $a) : array +{ +} +function (\NS\A $a) : \NS\A { +}; +function fn3(?\NS\A $a) : ?\NS\A +{ +} +function fn4(?array $a) : ?array +{ +} +\NS\A::b(); +\NS\A::$b; +\NS\A::B; +new \NS\A(); +$a instanceof \NS\A; +\NS\a(); +\NS\A; +try { + $someThing; +} catch (\NS\A $a) { + $someThingElse; +} +EOC; + + $parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative); + $prettyPrinter = new PhpParser\PrettyPrinter\Standard; + $traverser = new PhpParser\NodeTraverser; + $traverser->addVisitor(new NameResolver); + + $stmts = $parser->parse($code); + $stmts = $traverser->traverse($stmts); + + $this->assertSame( + $this->canonicalize($expectedCode), + $prettyPrinter->prettyPrint($stmts) + ); + } + + public function testNoResolveSpecialName() { + $stmts = array(new Node\Expr\New_(new Name('self'))); + + $traverser = new PhpParser\NodeTraverser; + $traverser->addVisitor(new NameResolver); + + $this->assertEquals($stmts, $traverser->traverse($stmts)); + } + + public function testAddDeclarationNamespacedName() { + $nsStmts = array( + new Stmt\Class_('A'), + new Stmt\Interface_('B'), + new Stmt\Function_('C'), + new Stmt\Const_(array( + new Node\Const_('D', new Node\Scalar\LNumber(42)) + )), + new Stmt\Trait_('E'), + new Expr\New_(new Stmt\Class_(null)), + ); + + $traverser = new PhpParser\NodeTraverser; + $traverser->addVisitor(new NameResolver); + + $stmts = $traverser->traverse([new Stmt\Namespace_(new Name('NS'), $nsStmts)]); + $this->assertSame('NS\\A', (string) $stmts[0]->stmts[0]->namespacedName); + $this->assertSame('NS\\B', (string) $stmts[0]->stmts[1]->namespacedName); + $this->assertSame('NS\\C', (string) $stmts[0]->stmts[2]->namespacedName); + $this->assertSame('NS\\D', (string) $stmts[0]->stmts[3]->consts[0]->namespacedName); + $this->assertSame('NS\\E', (string) $stmts[0]->stmts[4]->namespacedName); + $this->assertObjectNotHasAttribute('namespacedName', $stmts[0]->stmts[5]->class); + + $stmts = $traverser->traverse([new Stmt\Namespace_(null, $nsStmts)]); + $this->assertSame('A', (string) $stmts[0]->stmts[0]->namespacedName); + $this->assertSame('B', (string) $stmts[0]->stmts[1]->namespacedName); + $this->assertSame('C', (string) $stmts[0]->stmts[2]->namespacedName); + $this->assertSame('D', (string) $stmts[0]->stmts[3]->consts[0]->namespacedName); + $this->assertSame('E', (string) $stmts[0]->stmts[4]->namespacedName); + $this->assertObjectNotHasAttribute('namespacedName', $stmts[0]->stmts[5]->class); + } + + public function testAddRuntimeResolvedNamespacedName() { + $stmts = array( + new Stmt\Namespace_(new Name('NS'), array( + new Expr\FuncCall(new Name('foo')), + new Expr\ConstFetch(new Name('FOO')), + )), + new Stmt\Namespace_(null, array( + new Expr\FuncCall(new Name('foo')), + new Expr\ConstFetch(new Name('FOO')), + )), + ); + + $traverser = new PhpParser\NodeTraverser; + $traverser->addVisitor(new NameResolver); + $stmts = $traverser->traverse($stmts); + + $this->assertSame('NS\\foo', (string) $stmts[0]->stmts[0]->name->getAttribute('namespacedName')); + $this->assertSame('NS\\FOO', (string) $stmts[0]->stmts[1]->name->getAttribute('namespacedName')); + + $this->assertFalse($stmts[1]->stmts[0]->name->hasAttribute('namespacedName')); + $this->assertFalse($stmts[1]->stmts[1]->name->hasAttribute('namespacedName')); + } + + /** + * @dataProvider provideTestError + */ + public function testError(Node $stmt, $errorMsg) { + $this->setExpectedException('PhpParser\Error', $errorMsg); + + $traverser = new PhpParser\NodeTraverser; + $traverser->addVisitor(new NameResolver); + $traverser->traverse(array($stmt)); + } + + public function provideTestError() { + return array( + array( + new Stmt\Use_(array( + new Stmt\UseUse(new Name('A\B'), 'B', 0, array('startLine' => 1)), + new Stmt\UseUse(new Name('C\D'), 'B', 0, array('startLine' => 2)), + ), Stmt\Use_::TYPE_NORMAL), + 'Cannot use C\D as B because the name is already in use on line 2' + ), + array( + new Stmt\Use_(array( + new Stmt\UseUse(new Name('a\b'), 'b', 0, array('startLine' => 1)), + new Stmt\UseUse(new Name('c\d'), 'B', 0, array('startLine' => 2)), + ), Stmt\Use_::TYPE_FUNCTION), + 'Cannot use function c\d as B because the name is already in use on line 2' + ), + array( + new Stmt\Use_(array( + new Stmt\UseUse(new Name('A\B'), 'B', 0, array('startLine' => 1)), + new Stmt\UseUse(new Name('C\D'), 'B', 0, array('startLine' => 2)), + ), Stmt\Use_::TYPE_CONSTANT), + 'Cannot use const C\D as B because the name is already in use on line 2' + ), + array( + new Expr\New_(new Name\FullyQualified('self', array('startLine' => 3))), + "'\\self' is an invalid class name on line 3" + ), + array( + new Expr\New_(new Name\Relative('self', array('startLine' => 3))), + "'\\self' is an invalid class name on line 3" + ), + array( + new Expr\New_(new Name\FullyQualified('PARENT', array('startLine' => 3))), + "'\\PARENT' is an invalid class name on line 3" + ), + array( + new Expr\New_(new Name\Relative('STATIC', array('startLine' => 3))), + "'\\STATIC' is an invalid class name on line 3" + ), + ); + } + + public function testClassNameIsCaseInsensitive() + { + $source = <<<'EOC' +<?php +namespace Foo; +use Bar\Baz; +$test = new baz(); +EOC; + + $parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative); + $stmts = $parser->parse($source); + + $traverser = new PhpParser\NodeTraverser; + $traverser->addVisitor(new NameResolver); + + $stmts = $traverser->traverse($stmts); + $stmt = $stmts[0]; + + $this->assertSame(array('Bar', 'Baz'), $stmt->stmts[1]->expr->class->parts); + } + + public function testSpecialClassNamesAreCaseInsensitive() { + $source = <<<'EOC' +<?php +namespace Foo; + +class Bar +{ + public static function method() + { + SELF::method(); + PARENT::method(); + STATIC::method(); + } +} +EOC; + + $parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative); + $stmts = $parser->parse($source); + + $traverser = new PhpParser\NodeTraverser; + $traverser->addVisitor(new NameResolver); + + $stmts = $traverser->traverse($stmts); + $classStmt = $stmts[0]; + $methodStmt = $classStmt->stmts[0]->stmts[0]; + + $this->assertSame('SELF', (string)$methodStmt->stmts[0]->class); + $this->assertSame('PARENT', (string)$methodStmt->stmts[1]->class); + $this->assertSame('STATIC', (string)$methodStmt->stmts[2]->class); + } +} |