328 lines
		
	
	
	
		
			9.8 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
		
		
			
		
	
	
			328 lines
		
	
	
	
		
			9.8 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
|   | <?php declare(strict_types=1); | ||
|  | 
 | ||
|  | namespace PhpParser; | ||
|  | 
 | ||
|  | use PHPUnit\Framework\TestCase; | ||
|  | 
 | ||
|  | class DummyNode extends NodeAbstract | ||
|  | { | ||
|  |     public $subNode1; | ||
|  |     public $subNode2; | ||
|  | 
 | ||
|  |     public function __construct($subNode1, $subNode2, $attributes) { | ||
|  |         parent::__construct($attributes); | ||
|  |         $this->subNode1 = $subNode1; | ||
|  |         $this->subNode2 = $subNode2; | ||
|  |     } | ||
|  | 
 | ||
|  |     public function getSubNodeNames() : array { | ||
|  |         return ['subNode1', 'subNode2']; | ||
|  |     } | ||
|  | 
 | ||
|  |     // This method is only overwritten because the node is located in an unusual namespace
 | ||
|  |     public function getType() : string { | ||
|  |         return 'Dummy'; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | class NodeAbstractTest extends TestCase | ||
|  | { | ||
|  |     public function provideNodes() { | ||
|  |         $attributes = [ | ||
|  |             'startLine' => 10, | ||
|  |             'endLine' => 11, | ||
|  |             'startTokenPos' => 12, | ||
|  |             'endTokenPos' => 13, | ||
|  |             'startFilePos' => 14, | ||
|  |             'endFilePos' => 15, | ||
|  |             'comments'  => [ | ||
|  |                 new Comment('// Comment' . "\n"), | ||
|  |                 new Comment\Doc('/** doc comment */'), | ||
|  |             ], | ||
|  |         ]; | ||
|  | 
 | ||
|  |         $node = new DummyNode('value1', 'value2', $attributes); | ||
|  |         $node->notSubNode = 'value3'; | ||
|  | 
 | ||
|  |         return [ | ||
|  |             [$attributes, $node], | ||
|  |         ]; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @dataProvider provideNodes | ||
|  |      */ | ||
|  |     public function testConstruct(array $attributes, Node $node) { | ||
|  |         $this->assertSame('Dummy', $node->getType()); | ||
|  |         $this->assertSame(['subNode1', 'subNode2'], $node->getSubNodeNames()); | ||
|  |         $this->assertSame(10, $node->getLine()); | ||
|  |         $this->assertSame(10, $node->getStartLine()); | ||
|  |         $this->assertSame(11, $node->getEndLine()); | ||
|  |         $this->assertSame(12, $node->getStartTokenPos()); | ||
|  |         $this->assertSame(13, $node->getEndTokenPos()); | ||
|  |         $this->assertSame(14, $node->getStartFilePos()); | ||
|  |         $this->assertSame(15, $node->getEndFilePos()); | ||
|  |         $this->assertSame('/** doc comment */', $node->getDocComment()->getText()); | ||
|  |         $this->assertSame('value1', $node->subNode1); | ||
|  |         $this->assertSame('value2', $node->subNode2); | ||
|  |         $this->assertObjectHasAttribute('subNode1', $node); | ||
|  |         $this->assertObjectHasAttribute('subNode2', $node); | ||
|  |         $this->assertObjectNotHasAttribute('subNode3', $node); | ||
|  |         $this->assertSame($attributes, $node->getAttributes()); | ||
|  |         $this->assertSame($attributes['comments'], $node->getComments()); | ||
|  | 
 | ||
|  |         return $node; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @dataProvider provideNodes | ||
|  |      */ | ||
|  |     public function testGetDocComment(array $attributes, Node $node) { | ||
|  |         $this->assertSame('/** doc comment */', $node->getDocComment()->getText()); | ||
|  |         $comments = $node->getComments(); | ||
|  | 
 | ||
|  |         array_pop($comments); // remove doc comment
 | ||
|  |         $node->setAttribute('comments', $comments); | ||
|  |         $this->assertNull($node->getDocComment()); | ||
|  | 
 | ||
|  |         array_pop($comments); // remove comment
 | ||
|  |         $node->setAttribute('comments', $comments); | ||
|  |         $this->assertNull($node->getDocComment()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testSetDocComment() { | ||
|  |         $node = new DummyNode(null, null, []); | ||
|  | 
 | ||
|  |         // Add doc comment to node without comments
 | ||
|  |         $docComment = new Comment\Doc('/** doc */'); | ||
|  |         $node->setDocComment($docComment); | ||
|  |         $this->assertSame($docComment, $node->getDocComment()); | ||
|  | 
 | ||
|  |         // Replace it
 | ||
|  |         $docComment = new Comment\Doc('/** doc 2 */'); | ||
|  |         $node->setDocComment($docComment); | ||
|  |         $this->assertSame($docComment, $node->getDocComment()); | ||
|  | 
 | ||
|  |         // Add docmment to node with other comments
 | ||
|  |         $c1 = new Comment('/* foo */'); | ||
|  |         $c2 = new Comment('/* bar */'); | ||
|  |         $docComment = new Comment\Doc('/** baz */'); | ||
|  |         $node->setAttribute('comments', [$c1, $c2]); | ||
|  |         $node->setDocComment($docComment); | ||
|  |         $this->assertSame([$c1, $c2, $docComment], $node->getAttribute('comments')); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @dataProvider provideNodes | ||
|  |      */ | ||
|  |     public function testChange(array $attributes, Node $node) { | ||
|  |         // direct modification
 | ||
|  |         $node->subNode = 'newValue'; | ||
|  |         $this->assertSame('newValue', $node->subNode); | ||
|  | 
 | ||
|  |         // indirect modification
 | ||
|  |         $subNode =& $node->subNode; | ||
|  |         $subNode = 'newNewValue'; | ||
|  |         $this->assertSame('newNewValue', $node->subNode); | ||
|  | 
 | ||
|  |         // removal
 | ||
|  |         unset($node->subNode); | ||
|  |         $this->assertObjectNotHasAttribute('subNode', $node); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @dataProvider provideNodes | ||
|  |      */ | ||
|  |     public function testIteration(array $attributes, Node $node) { | ||
|  |         // Iteration is simple object iteration over properties,
 | ||
|  |         // not over subnodes
 | ||
|  |         $i = 0; | ||
|  |         foreach ($node as $key => $value) { | ||
|  |             if ($i === 0) { | ||
|  |                 $this->assertSame('subNode1', $key); | ||
|  |                 $this->assertSame('value1', $value); | ||
|  |             } elseif ($i === 1) { | ||
|  |                 $this->assertSame('subNode2', $key); | ||
|  |                 $this->assertSame('value2', $value); | ||
|  |             } elseif ($i === 2) { | ||
|  |                 $this->assertSame('notSubNode', $key); | ||
|  |                 $this->assertSame('value3', $value); | ||
|  |             } else { | ||
|  |                 throw new \Exception; | ||
|  |             } | ||
|  |             $i++; | ||
|  |         } | ||
|  |         $this->assertSame(3, $i); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testAttributes() { | ||
|  |         /** @var $node Node */ | ||
|  |         $node = $this->getMockForAbstractClass(NodeAbstract::class); | ||
|  | 
 | ||
|  |         $this->assertEmpty($node->getAttributes()); | ||
|  | 
 | ||
|  |         $node->setAttribute('key', 'value'); | ||
|  |         $this->assertTrue($node->hasAttribute('key')); | ||
|  |         $this->assertSame('value', $node->getAttribute('key')); | ||
|  | 
 | ||
|  |         $this->assertFalse($node->hasAttribute('doesNotExist')); | ||
|  |         $this->assertNull($node->getAttribute('doesNotExist')); | ||
|  |         $this->assertSame('default', $node->getAttribute('doesNotExist', 'default')); | ||
|  | 
 | ||
|  |         $node->setAttribute('null', null); | ||
|  |         $this->assertTrue($node->hasAttribute('null')); | ||
|  |         $this->assertNull($node->getAttribute('null')); | ||
|  |         $this->assertNull($node->getAttribute('null', 'default')); | ||
|  | 
 | ||
|  |         $this->assertSame( | ||
|  |             [ | ||
|  |                 'key'  => 'value', | ||
|  |                 'null' => null, | ||
|  |             ], | ||
|  |             $node->getAttributes() | ||
|  |         ); | ||
|  | 
 | ||
|  |         $node->setAttributes( | ||
|  |             [ | ||
|  |                 'a' => 'b', | ||
|  |                 'c' => null, | ||
|  |             ] | ||
|  |         ); | ||
|  |         $this->assertSame( | ||
|  |             [ | ||
|  |                 'a' => 'b', | ||
|  |                 'c' => null, | ||
|  |             ], | ||
|  |             $node->getAttributes() | ||
|  |         ); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testJsonSerialization() { | ||
|  |         $code = <<<'PHP' | ||
|  | <?php | ||
|  | // comment
 | ||
|  | /** doc comment */ | ||
|  | function functionName(&$a = 0, $b = 1.0) { | ||
|  |     echo 'Foo'; | ||
|  | } | ||
|  | PHP; | ||
|  |         $expected = <<<'JSON' | ||
|  | [ | ||
|  |     { | ||
|  |         "nodeType": "Stmt_Function", | ||
|  |         "byRef": false, | ||
|  |         "name": { | ||
|  |             "nodeType": "Identifier", | ||
|  |             "name": "functionName", | ||
|  |             "attributes": { | ||
|  |                 "startLine": 4, | ||
|  |                 "endLine": 4 | ||
|  |             } | ||
|  |         }, | ||
|  |         "params": [ | ||
|  |             { | ||
|  |                 "nodeType": "Param", | ||
|  |                 "type": null, | ||
|  |                 "byRef": true, | ||
|  |                 "variadic": false, | ||
|  |                 "var": { | ||
|  |                     "nodeType": "Expr_Variable", | ||
|  |                     "name": "a", | ||
|  |                     "attributes": { | ||
|  |                         "startLine": 4, | ||
|  |                         "endLine": 4 | ||
|  |                     } | ||
|  |                 }, | ||
|  |                 "default": { | ||
|  |                     "nodeType": "Scalar_LNumber", | ||
|  |                     "value": 0, | ||
|  |                     "attributes": { | ||
|  |                         "startLine": 4, | ||
|  |                         "endLine": 4, | ||
|  |                         "kind": 10 | ||
|  |                     } | ||
|  |                 }, | ||
|  |                 "attributes": { | ||
|  |                     "startLine": 4, | ||
|  |                     "endLine": 4 | ||
|  |                 } | ||
|  |             }, | ||
|  |             { | ||
|  |                 "nodeType": "Param", | ||
|  |                 "type": null, | ||
|  |                 "byRef": false, | ||
|  |                 "variadic": false, | ||
|  |                 "var": { | ||
|  |                     "nodeType": "Expr_Variable", | ||
|  |                     "name": "b", | ||
|  |                     "attributes": { | ||
|  |                         "startLine": 4, | ||
|  |                         "endLine": 4 | ||
|  |                     } | ||
|  |                 }, | ||
|  |                 "default": { | ||
|  |                     "nodeType": "Scalar_DNumber", | ||
|  |                     "value": 1, | ||
|  |                     "attributes": { | ||
|  |                         "startLine": 4, | ||
|  |                         "endLine": 4 | ||
|  |                     } | ||
|  |                 }, | ||
|  |                 "attributes": { | ||
|  |                     "startLine": 4, | ||
|  |                     "endLine": 4 | ||
|  |                 } | ||
|  |             } | ||
|  |         ], | ||
|  |         "returnType": null, | ||
|  |         "stmts": [ | ||
|  |             { | ||
|  |                 "nodeType": "Stmt_Echo", | ||
|  |                 "exprs": [ | ||
|  |                     { | ||
|  |                         "nodeType": "Scalar_String", | ||
|  |                         "value": "Foo", | ||
|  |                         "attributes": { | ||
|  |                             "startLine": 5, | ||
|  |                             "endLine": 5, | ||
|  |                             "kind": 1 | ||
|  |                         } | ||
|  |                     } | ||
|  |                 ], | ||
|  |                 "attributes": { | ||
|  |                     "startLine": 5, | ||
|  |                     "endLine": 5 | ||
|  |                 } | ||
|  |             } | ||
|  |         ], | ||
|  |         "attributes": { | ||
|  |             "startLine": 4, | ||
|  |             "comments": [ | ||
|  |                 { | ||
|  |                     "nodeType": "Comment", | ||
|  |                     "text": "\/\/ comment\n", | ||
|  |                     "line": 2, | ||
|  |                     "filePos": 6, | ||
|  |                     "tokenPos": 1 | ||
|  |                 }, | ||
|  |                 { | ||
|  |                     "nodeType": "Comment_Doc", | ||
|  |                     "text": "\/** doc comment *\/", | ||
|  |                     "line": 3, | ||
|  |                     "filePos": 17, | ||
|  |                     "tokenPos": 2 | ||
|  |                 } | ||
|  |             ], | ||
|  |             "endLine": 6 | ||
|  |         } | ||
|  |     } | ||
|  | ] | ||
|  | JSON; | ||
|  | 
 | ||
|  |         $parser = new Parser\Php7(new Lexer()); | ||
|  |         $stmts = $parser->parse(canonicalize($code)); | ||
|  |         $json = json_encode($stmts, JSON_PRETTY_PRINT); | ||
|  |         $this->assertEquals(canonicalize($expected), canonicalize($json)); | ||
|  |     } | ||
|  | } |