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