Chris@13
|
1 <?php declare(strict_types=1);
|
Chris@0
|
2
|
Chris@0
|
3 namespace PhpParser;
|
Chris@0
|
4
|
Chris@13
|
5 use PHPUnit\Framework\TestCase;
|
Chris@13
|
6
|
Chris@13
|
7 class DummyNode extends NodeAbstract
|
Chris@13
|
8 {
|
Chris@0
|
9 public $subNode1;
|
Chris@0
|
10 public $subNode2;
|
Chris@0
|
11
|
Chris@0
|
12 public function __construct($subNode1, $subNode2, $attributes) {
|
Chris@0
|
13 parent::__construct($attributes);
|
Chris@0
|
14 $this->subNode1 = $subNode1;
|
Chris@0
|
15 $this->subNode2 = $subNode2;
|
Chris@0
|
16 }
|
Chris@0
|
17
|
Chris@13
|
18 public function getSubNodeNames() : array {
|
Chris@13
|
19 return ['subNode1', 'subNode2'];
|
Chris@0
|
20 }
|
Chris@0
|
21
|
Chris@0
|
22 // This method is only overwritten because the node is located in an unusual namespace
|
Chris@13
|
23 public function getType() : string {
|
Chris@0
|
24 return 'Dummy';
|
Chris@0
|
25 }
|
Chris@0
|
26 }
|
Chris@0
|
27
|
Chris@13
|
28 class NodeAbstractTest extends TestCase
|
Chris@0
|
29 {
|
Chris@0
|
30 public function provideNodes() {
|
Chris@13
|
31 $attributes = [
|
Chris@0
|
32 'startLine' => 10,
|
Chris@13
|
33 'endLine' => 11,
|
Chris@13
|
34 'startTokenPos' => 12,
|
Chris@13
|
35 'endTokenPos' => 13,
|
Chris@13
|
36 'startFilePos' => 14,
|
Chris@13
|
37 'endFilePos' => 15,
|
Chris@13
|
38 'comments' => [
|
Chris@0
|
39 new Comment('// Comment' . "\n"),
|
Chris@0
|
40 new Comment\Doc('/** doc comment */'),
|
Chris@13
|
41 ],
|
Chris@13
|
42 ];
|
Chris@0
|
43
|
Chris@0
|
44 $node = new DummyNode('value1', 'value2', $attributes);
|
Chris@0
|
45 $node->notSubNode = 'value3';
|
Chris@0
|
46
|
Chris@13
|
47 return [
|
Chris@13
|
48 [$attributes, $node],
|
Chris@13
|
49 ];
|
Chris@0
|
50 }
|
Chris@0
|
51
|
Chris@0
|
52 /**
|
Chris@0
|
53 * @dataProvider provideNodes
|
Chris@0
|
54 */
|
Chris@0
|
55 public function testConstruct(array $attributes, Node $node) {
|
Chris@0
|
56 $this->assertSame('Dummy', $node->getType());
|
Chris@13
|
57 $this->assertSame(['subNode1', 'subNode2'], $node->getSubNodeNames());
|
Chris@0
|
58 $this->assertSame(10, $node->getLine());
|
Chris@13
|
59 $this->assertSame(10, $node->getStartLine());
|
Chris@13
|
60 $this->assertSame(11, $node->getEndLine());
|
Chris@13
|
61 $this->assertSame(12, $node->getStartTokenPos());
|
Chris@13
|
62 $this->assertSame(13, $node->getEndTokenPos());
|
Chris@13
|
63 $this->assertSame(14, $node->getStartFilePos());
|
Chris@13
|
64 $this->assertSame(15, $node->getEndFilePos());
|
Chris@0
|
65 $this->assertSame('/** doc comment */', $node->getDocComment()->getText());
|
Chris@0
|
66 $this->assertSame('value1', $node->subNode1);
|
Chris@0
|
67 $this->assertSame('value2', $node->subNode2);
|
Chris@13
|
68 $this->assertObjectHasAttribute('subNode1', $node);
|
Chris@13
|
69 $this->assertObjectHasAttribute('subNode2', $node);
|
Chris@13
|
70 $this->assertObjectNotHasAttribute('subNode3', $node);
|
Chris@0
|
71 $this->assertSame($attributes, $node->getAttributes());
|
Chris@13
|
72 $this->assertSame($attributes['comments'], $node->getComments());
|
Chris@0
|
73
|
Chris@0
|
74 return $node;
|
Chris@0
|
75 }
|
Chris@0
|
76
|
Chris@0
|
77 /**
|
Chris@0
|
78 * @dataProvider provideNodes
|
Chris@0
|
79 */
|
Chris@0
|
80 public function testGetDocComment(array $attributes, Node $node) {
|
Chris@0
|
81 $this->assertSame('/** doc comment */', $node->getDocComment()->getText());
|
Chris@13
|
82 $comments = $node->getComments();
|
Chris@13
|
83
|
Chris@13
|
84 array_pop($comments); // remove doc comment
|
Chris@13
|
85 $node->setAttribute('comments', $comments);
|
Chris@0
|
86 $this->assertNull($node->getDocComment());
|
Chris@13
|
87
|
Chris@13
|
88 array_pop($comments); // remove comment
|
Chris@13
|
89 $node->setAttribute('comments', $comments);
|
Chris@0
|
90 $this->assertNull($node->getDocComment());
|
Chris@0
|
91 }
|
Chris@0
|
92
|
Chris@0
|
93 public function testSetDocComment() {
|
Chris@0
|
94 $node = new DummyNode(null, null, []);
|
Chris@0
|
95
|
Chris@0
|
96 // Add doc comment to node without comments
|
Chris@0
|
97 $docComment = new Comment\Doc('/** doc */');
|
Chris@0
|
98 $node->setDocComment($docComment);
|
Chris@0
|
99 $this->assertSame($docComment, $node->getDocComment());
|
Chris@0
|
100
|
Chris@0
|
101 // Replace it
|
Chris@0
|
102 $docComment = new Comment\Doc('/** doc 2 */');
|
Chris@0
|
103 $node->setDocComment($docComment);
|
Chris@0
|
104 $this->assertSame($docComment, $node->getDocComment());
|
Chris@0
|
105
|
Chris@0
|
106 // Add docmment to node with other comments
|
Chris@0
|
107 $c1 = new Comment('/* foo */');
|
Chris@0
|
108 $c2 = new Comment('/* bar */');
|
Chris@0
|
109 $docComment = new Comment\Doc('/** baz */');
|
Chris@0
|
110 $node->setAttribute('comments', [$c1, $c2]);
|
Chris@0
|
111 $node->setDocComment($docComment);
|
Chris@0
|
112 $this->assertSame([$c1, $c2, $docComment], $node->getAttribute('comments'));
|
Chris@0
|
113 }
|
Chris@0
|
114
|
Chris@0
|
115 /**
|
Chris@0
|
116 * @dataProvider provideNodes
|
Chris@0
|
117 */
|
Chris@0
|
118 public function testChange(array $attributes, Node $node) {
|
Chris@0
|
119 // direct modification
|
Chris@0
|
120 $node->subNode = 'newValue';
|
Chris@0
|
121 $this->assertSame('newValue', $node->subNode);
|
Chris@0
|
122
|
Chris@0
|
123 // indirect modification
|
Chris@0
|
124 $subNode =& $node->subNode;
|
Chris@0
|
125 $subNode = 'newNewValue';
|
Chris@0
|
126 $this->assertSame('newNewValue', $node->subNode);
|
Chris@0
|
127
|
Chris@0
|
128 // removal
|
Chris@0
|
129 unset($node->subNode);
|
Chris@13
|
130 $this->assertObjectNotHasAttribute('subNode', $node);
|
Chris@0
|
131 }
|
Chris@0
|
132
|
Chris@0
|
133 /**
|
Chris@0
|
134 * @dataProvider provideNodes
|
Chris@0
|
135 */
|
Chris@0
|
136 public function testIteration(array $attributes, Node $node) {
|
Chris@0
|
137 // Iteration is simple object iteration over properties,
|
Chris@0
|
138 // not over subnodes
|
Chris@0
|
139 $i = 0;
|
Chris@0
|
140 foreach ($node as $key => $value) {
|
Chris@0
|
141 if ($i === 0) {
|
Chris@0
|
142 $this->assertSame('subNode1', $key);
|
Chris@0
|
143 $this->assertSame('value1', $value);
|
Chris@13
|
144 } elseif ($i === 1) {
|
Chris@0
|
145 $this->assertSame('subNode2', $key);
|
Chris@0
|
146 $this->assertSame('value2', $value);
|
Chris@13
|
147 } elseif ($i === 2) {
|
Chris@0
|
148 $this->assertSame('notSubNode', $key);
|
Chris@0
|
149 $this->assertSame('value3', $value);
|
Chris@0
|
150 } else {
|
Chris@0
|
151 throw new \Exception;
|
Chris@0
|
152 }
|
Chris@0
|
153 $i++;
|
Chris@0
|
154 }
|
Chris@0
|
155 $this->assertSame(3, $i);
|
Chris@0
|
156 }
|
Chris@0
|
157
|
Chris@0
|
158 public function testAttributes() {
|
Chris@0
|
159 /** @var $node Node */
|
Chris@13
|
160 $node = $this->getMockForAbstractClass(NodeAbstract::class);
|
Chris@0
|
161
|
Chris@0
|
162 $this->assertEmpty($node->getAttributes());
|
Chris@0
|
163
|
Chris@0
|
164 $node->setAttribute('key', 'value');
|
Chris@0
|
165 $this->assertTrue($node->hasAttribute('key'));
|
Chris@0
|
166 $this->assertSame('value', $node->getAttribute('key'));
|
Chris@0
|
167
|
Chris@0
|
168 $this->assertFalse($node->hasAttribute('doesNotExist'));
|
Chris@0
|
169 $this->assertNull($node->getAttribute('doesNotExist'));
|
Chris@0
|
170 $this->assertSame('default', $node->getAttribute('doesNotExist', 'default'));
|
Chris@0
|
171
|
Chris@0
|
172 $node->setAttribute('null', null);
|
Chris@0
|
173 $this->assertTrue($node->hasAttribute('null'));
|
Chris@0
|
174 $this->assertNull($node->getAttribute('null'));
|
Chris@0
|
175 $this->assertNull($node->getAttribute('null', 'default'));
|
Chris@0
|
176
|
Chris@0
|
177 $this->assertSame(
|
Chris@13
|
178 [
|
Chris@0
|
179 'key' => 'value',
|
Chris@0
|
180 'null' => null,
|
Chris@13
|
181 ],
|
Chris@13
|
182 $node->getAttributes()
|
Chris@13
|
183 );
|
Chris@13
|
184
|
Chris@13
|
185 $node->setAttributes(
|
Chris@13
|
186 [
|
Chris@13
|
187 'a' => 'b',
|
Chris@13
|
188 'c' => null,
|
Chris@13
|
189 ]
|
Chris@13
|
190 );
|
Chris@13
|
191 $this->assertSame(
|
Chris@13
|
192 [
|
Chris@13
|
193 'a' => 'b',
|
Chris@13
|
194 'c' => null,
|
Chris@13
|
195 ],
|
Chris@0
|
196 $node->getAttributes()
|
Chris@0
|
197 );
|
Chris@0
|
198 }
|
Chris@0
|
199
|
Chris@0
|
200 public function testJsonSerialization() {
|
Chris@0
|
201 $code = <<<'PHP'
|
Chris@0
|
202 <?php
|
Chris@0
|
203 // comment
|
Chris@0
|
204 /** doc comment */
|
Chris@0
|
205 function functionName(&$a = 0, $b = 1.0) {
|
Chris@0
|
206 echo 'Foo';
|
Chris@0
|
207 }
|
Chris@0
|
208 PHP;
|
Chris@0
|
209 $expected = <<<'JSON'
|
Chris@0
|
210 [
|
Chris@0
|
211 {
|
Chris@0
|
212 "nodeType": "Stmt_Function",
|
Chris@0
|
213 "byRef": false,
|
Chris@13
|
214 "name": {
|
Chris@13
|
215 "nodeType": "Identifier",
|
Chris@13
|
216 "name": "functionName",
|
Chris@13
|
217 "attributes": {
|
Chris@13
|
218 "startLine": 4,
|
Chris@13
|
219 "endLine": 4
|
Chris@13
|
220 }
|
Chris@13
|
221 },
|
Chris@0
|
222 "params": [
|
Chris@0
|
223 {
|
Chris@0
|
224 "nodeType": "Param",
|
Chris@0
|
225 "type": null,
|
Chris@0
|
226 "byRef": true,
|
Chris@0
|
227 "variadic": false,
|
Chris@13
|
228 "var": {
|
Chris@13
|
229 "nodeType": "Expr_Variable",
|
Chris@13
|
230 "name": "a",
|
Chris@13
|
231 "attributes": {
|
Chris@13
|
232 "startLine": 4,
|
Chris@13
|
233 "endLine": 4
|
Chris@13
|
234 }
|
Chris@13
|
235 },
|
Chris@0
|
236 "default": {
|
Chris@0
|
237 "nodeType": "Scalar_LNumber",
|
Chris@0
|
238 "value": 0,
|
Chris@0
|
239 "attributes": {
|
Chris@0
|
240 "startLine": 4,
|
Chris@0
|
241 "endLine": 4,
|
Chris@0
|
242 "kind": 10
|
Chris@0
|
243 }
|
Chris@0
|
244 },
|
Chris@0
|
245 "attributes": {
|
Chris@0
|
246 "startLine": 4,
|
Chris@0
|
247 "endLine": 4
|
Chris@0
|
248 }
|
Chris@0
|
249 },
|
Chris@0
|
250 {
|
Chris@0
|
251 "nodeType": "Param",
|
Chris@0
|
252 "type": null,
|
Chris@0
|
253 "byRef": false,
|
Chris@0
|
254 "variadic": false,
|
Chris@13
|
255 "var": {
|
Chris@13
|
256 "nodeType": "Expr_Variable",
|
Chris@13
|
257 "name": "b",
|
Chris@13
|
258 "attributes": {
|
Chris@13
|
259 "startLine": 4,
|
Chris@13
|
260 "endLine": 4
|
Chris@13
|
261 }
|
Chris@13
|
262 },
|
Chris@0
|
263 "default": {
|
Chris@0
|
264 "nodeType": "Scalar_DNumber",
|
Chris@0
|
265 "value": 1,
|
Chris@0
|
266 "attributes": {
|
Chris@0
|
267 "startLine": 4,
|
Chris@0
|
268 "endLine": 4
|
Chris@0
|
269 }
|
Chris@0
|
270 },
|
Chris@0
|
271 "attributes": {
|
Chris@0
|
272 "startLine": 4,
|
Chris@0
|
273 "endLine": 4
|
Chris@0
|
274 }
|
Chris@0
|
275 }
|
Chris@0
|
276 ],
|
Chris@0
|
277 "returnType": null,
|
Chris@0
|
278 "stmts": [
|
Chris@0
|
279 {
|
Chris@0
|
280 "nodeType": "Stmt_Echo",
|
Chris@0
|
281 "exprs": [
|
Chris@0
|
282 {
|
Chris@0
|
283 "nodeType": "Scalar_String",
|
Chris@0
|
284 "value": "Foo",
|
Chris@0
|
285 "attributes": {
|
Chris@0
|
286 "startLine": 5,
|
Chris@0
|
287 "endLine": 5,
|
Chris@0
|
288 "kind": 1
|
Chris@0
|
289 }
|
Chris@0
|
290 }
|
Chris@0
|
291 ],
|
Chris@0
|
292 "attributes": {
|
Chris@0
|
293 "startLine": 5,
|
Chris@0
|
294 "endLine": 5
|
Chris@0
|
295 }
|
Chris@0
|
296 }
|
Chris@0
|
297 ],
|
Chris@0
|
298 "attributes": {
|
Chris@0
|
299 "startLine": 4,
|
Chris@0
|
300 "comments": [
|
Chris@0
|
301 {
|
Chris@0
|
302 "nodeType": "Comment",
|
Chris@0
|
303 "text": "\/\/ comment\n",
|
Chris@0
|
304 "line": 2,
|
Chris@13
|
305 "filePos": 6,
|
Chris@13
|
306 "tokenPos": 1
|
Chris@0
|
307 },
|
Chris@0
|
308 {
|
Chris@0
|
309 "nodeType": "Comment_Doc",
|
Chris@0
|
310 "text": "\/** doc comment *\/",
|
Chris@0
|
311 "line": 3,
|
Chris@13
|
312 "filePos": 17,
|
Chris@13
|
313 "tokenPos": 2
|
Chris@0
|
314 }
|
Chris@0
|
315 ],
|
Chris@0
|
316 "endLine": 6
|
Chris@0
|
317 }
|
Chris@0
|
318 }
|
Chris@0
|
319 ]
|
Chris@0
|
320 JSON;
|
Chris@0
|
321
|
Chris@0
|
322 $parser = new Parser\Php7(new Lexer());
|
Chris@0
|
323 $stmts = $parser->parse(canonicalize($code));
|
Chris@0
|
324 $json = json_encode($stmts, JSON_PRETTY_PRINT);
|
Chris@0
|
325 $this->assertEquals(canonicalize($expected), canonicalize($json));
|
Chris@0
|
326 }
|
Chris@0
|
327 }
|