@jorgsowa/php-parser 3.2.5-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/LICENSE +27 -0
  2. package/README.md +108 -0
  3. package/dist/@jorgsowa/php-parser.js +11239 -0
  4. package/dist/@jorgsowa/php-parser.min.js +2 -0
  5. package/dist/@jorgsowa/php-parser.min.js.LICENSE.txt +10 -0
  6. package/package.json +86 -0
  7. package/src/ast/array.js +44 -0
  8. package/src/ast/arrowfunc.js +43 -0
  9. package/src/ast/assign.js +28 -0
  10. package/src/ast/assignref.js +27 -0
  11. package/src/ast/attrgroup.js +21 -0
  12. package/src/ast/attribute.js +26 -0
  13. package/src/ast/bin.js +27 -0
  14. package/src/ast/block.js +24 -0
  15. package/src/ast/boolean.js +23 -0
  16. package/src/ast/break.js +21 -0
  17. package/src/ast/byref.js +21 -0
  18. package/src/ast/call.js +26 -0
  19. package/src/ast/case.js +26 -0
  20. package/src/ast/cast.js +28 -0
  21. package/src/ast/catch.js +29 -0
  22. package/src/ast/class.js +36 -0
  23. package/src/ast/classconstant.js +71 -0
  24. package/src/ast/clone.js +21 -0
  25. package/src/ast/closure.js +47 -0
  26. package/src/ast/comment.js +23 -0
  27. package/src/ast/commentblock.js +22 -0
  28. package/src/ast/commentline.js +22 -0
  29. package/src/ast/constant.js +26 -0
  30. package/src/ast/constantstatement.js +24 -0
  31. package/src/ast/continue.js +24 -0
  32. package/src/ast/declaration.js +60 -0
  33. package/src/ast/declare.js +71 -0
  34. package/src/ast/declaredirective.js +26 -0
  35. package/src/ast/do.js +26 -0
  36. package/src/ast/echo.js +26 -0
  37. package/src/ast/empty.js +23 -0
  38. package/src/ast/encapsed.js +75 -0
  39. package/src/ast/encapsedpart.js +28 -0
  40. package/src/ast/entry.js +30 -0
  41. package/src/ast/enum.js +30 -0
  42. package/src/ast/enumcase.js +26 -0
  43. package/src/ast/error.js +30 -0
  44. package/src/ast/eval.js +24 -0
  45. package/src/ast/exit.js +26 -0
  46. package/src/ast/expression.js +20 -0
  47. package/src/ast/expressionstatement.js +24 -0
  48. package/src/ast/for.js +33 -0
  49. package/src/ast/foreach.js +33 -0
  50. package/src/ast/function.js +34 -0
  51. package/src/ast/global.js +24 -0
  52. package/src/ast/goto.js +22 -0
  53. package/src/ast/halt.js +22 -0
  54. package/src/ast/identifier.js +26 -0
  55. package/src/ast/if.js +30 -0
  56. package/src/ast/include.js +28 -0
  57. package/src/ast/inline.js +23 -0
  58. package/src/ast/interface.js +28 -0
  59. package/src/ast/intersectiontype.js +24 -0
  60. package/src/ast/isset.js +23 -0
  61. package/src/ast/label.js +21 -0
  62. package/src/ast/list.js +26 -0
  63. package/src/ast/literal.js +28 -0
  64. package/src/ast/location.js +22 -0
  65. package/src/ast/lookup.js +26 -0
  66. package/src/ast/magic.js +22 -0
  67. package/src/ast/match.js +26 -0
  68. package/src/ast/matcharm.js +26 -0
  69. package/src/ast/method.js +24 -0
  70. package/src/ast/name.js +55 -0
  71. package/src/ast/namedargument.js +27 -0
  72. package/src/ast/namespace.js +26 -0
  73. package/src/ast/new.js +26 -0
  74. package/src/ast/node.js +111 -0
  75. package/src/ast/noop.js +20 -0
  76. package/src/ast/nowdoc.js +26 -0
  77. package/src/ast/nullkeyword.js +20 -0
  78. package/src/ast/nullsafepropertylookup.js +22 -0
  79. package/src/ast/number.js +23 -0
  80. package/src/ast/offsetlookup.js +22 -0
  81. package/src/ast/operation.js +19 -0
  82. package/src/ast/parameter.js +61 -0
  83. package/src/ast/parentreference.js +24 -0
  84. package/src/ast/position.js +22 -0
  85. package/src/ast/post.js +26 -0
  86. package/src/ast/pre.js +26 -0
  87. package/src/ast/print.js +23 -0
  88. package/src/ast/program.js +32 -0
  89. package/src/ast/property.js +46 -0
  90. package/src/ast/propertyhook.js +33 -0
  91. package/src/ast/propertylookup.js +22 -0
  92. package/src/ast/propertystatement.js +59 -0
  93. package/src/ast/reference.js +21 -0
  94. package/src/ast/retif.js +28 -0
  95. package/src/ast/return.js +21 -0
  96. package/src/ast/selfreference.js +24 -0
  97. package/src/ast/silent.js +24 -0
  98. package/src/ast/statement.js +19 -0
  99. package/src/ast/static.js +24 -0
  100. package/src/ast/staticlookup.js +22 -0
  101. package/src/ast/staticreference.js +24 -0
  102. package/src/ast/staticvariable.js +26 -0
  103. package/src/ast/string.js +28 -0
  104. package/src/ast/switch.js +28 -0
  105. package/src/ast/throw.js +21 -0
  106. package/src/ast/trait.js +24 -0
  107. package/src/ast/traitalias.js +44 -0
  108. package/src/ast/traitprecedence.js +28 -0
  109. package/src/ast/traituse.js +26 -0
  110. package/src/ast/try.js +28 -0
  111. package/src/ast/typereference.js +40 -0
  112. package/src/ast/unary.js +26 -0
  113. package/src/ast/uniontype.js +24 -0
  114. package/src/ast/unset.js +23 -0
  115. package/src/ast/usegroup.js +30 -0
  116. package/src/ast/useitem.js +45 -0
  117. package/src/ast/variable.js +36 -0
  118. package/src/ast/variadic.js +25 -0
  119. package/src/ast/variadicplaceholder.js +24 -0
  120. package/src/ast/while.js +28 -0
  121. package/src/ast/yield.js +27 -0
  122. package/src/ast/yieldfrom.js +25 -0
  123. package/src/ast.js +593 -0
  124. package/src/index.js +239 -0
  125. package/src/lexer/attribute.js +85 -0
  126. package/src/lexer/comments.js +63 -0
  127. package/src/lexer/initial.js +64 -0
  128. package/src/lexer/numbers.js +171 -0
  129. package/src/lexer/property.js +96 -0
  130. package/src/lexer/scripting.js +114 -0
  131. package/src/lexer/strings.js +524 -0
  132. package/src/lexer/tokens.js +356 -0
  133. package/src/lexer/utils.js +112 -0
  134. package/src/lexer.js +561 -0
  135. package/src/parser/array.js +113 -0
  136. package/src/parser/class.js +718 -0
  137. package/src/parser/comment.js +52 -0
  138. package/src/parser/enum.js +56 -0
  139. package/src/parser/expr.js +848 -0
  140. package/src/parser/function.js +507 -0
  141. package/src/parser/if.js +94 -0
  142. package/src/parser/loops.js +168 -0
  143. package/src/parser/main.js +21 -0
  144. package/src/parser/namespace.js +231 -0
  145. package/src/parser/scalar.js +492 -0
  146. package/src/parser/statement.js +444 -0
  147. package/src/parser/switch.js +99 -0
  148. package/src/parser/try.js +43 -0
  149. package/src/parser/utils.js +203 -0
  150. package/src/parser/variable.js +363 -0
  151. package/src/parser.js +748 -0
  152. package/src/tokens.js +177 -0
  153. package/types.d.ts +1457 -0
@@ -0,0 +1,848 @@
1
+ /**
2
+ * Copyright (C) 2018 Glayzzle (BSD3 License)
3
+ * @authors https://github.com/glayzzle/php-parser/graphs/contributors
4
+ * @url http://glayzzle.com
5
+ */
6
+ "use strict";
7
+
8
+ module.exports = {
9
+ read_expr(expr) {
10
+ const result = this.node();
11
+ if (this.token === "@") {
12
+ if (!expr) {
13
+ expr = this.next().read_expr();
14
+ }
15
+ return result("silent", expr);
16
+ }
17
+ if (!expr) {
18
+ expr = this.read_expr_item();
19
+ }
20
+ // binary operations
21
+ if (this.token === "|") {
22
+ return result("bin", "|", expr, this.next().read_expr());
23
+ }
24
+ if (this.token === "&") {
25
+ return result("bin", "&", expr, this.next().read_expr());
26
+ }
27
+ if (this.token === "^") {
28
+ return result("bin", "^", expr, this.next().read_expr());
29
+ }
30
+ if (this.token === ".") {
31
+ return result("bin", ".", expr, this.next().read_expr());
32
+ }
33
+ if (this.token === "+") {
34
+ return result("bin", "+", expr, this.next().read_expr());
35
+ }
36
+ if (this.token === "-") {
37
+ return result("bin", "-", expr, this.next().read_expr());
38
+ }
39
+ if (this.token === "*") {
40
+ return result("bin", "*", expr, this.next().read_expr());
41
+ }
42
+ if (this.token === "/") {
43
+ return result("bin", "/", expr, this.next().read_expr());
44
+ }
45
+ if (this.token === "%") {
46
+ return result("bin", "%", expr, this.next().read_expr());
47
+ }
48
+ if (this.token === this.tok.T_POW) {
49
+ return result("bin", "**", expr, this.next().read_expr());
50
+ }
51
+ if (this.token === this.tok.T_SL) {
52
+ return result("bin", "<<", expr, this.next().read_expr());
53
+ }
54
+ if (this.token === this.tok.T_SR) {
55
+ return result("bin", ">>", expr, this.next().read_expr());
56
+ }
57
+ // more binary operations (formerly bool)
58
+ if (this.token === this.tok.T_BOOLEAN_OR) {
59
+ return result("bin", "||", expr, this.next().read_expr());
60
+ }
61
+ if (this.token === this.tok.T_LOGICAL_OR) {
62
+ return result("bin", "or", expr, this.next().read_expr());
63
+ }
64
+ if (this.token === this.tok.T_BOOLEAN_AND) {
65
+ return result("bin", "&&", expr, this.next().read_expr());
66
+ }
67
+ if (this.token === this.tok.T_LOGICAL_AND) {
68
+ return result("bin", "and", expr, this.next().read_expr());
69
+ }
70
+ if (this.token === this.tok.T_LOGICAL_XOR) {
71
+ return result("bin", "xor", expr, this.next().read_expr());
72
+ }
73
+ if (this.token === this.tok.T_IS_IDENTICAL) {
74
+ return result("bin", "===", expr, this.next().read_expr());
75
+ }
76
+ if (this.token === this.tok.T_IS_NOT_IDENTICAL) {
77
+ return result("bin", "!==", expr, this.next().read_expr());
78
+ }
79
+ if (this.token === this.tok.T_IS_EQUAL) {
80
+ return result("bin", "==", expr, this.next().read_expr());
81
+ }
82
+ if (this.token === this.tok.T_IS_NOT_EQUAL) {
83
+ return result("bin", "!=", expr, this.next().read_expr());
84
+ }
85
+ if (this.token === "<") {
86
+ return result("bin", "<", expr, this.next().read_expr());
87
+ }
88
+ if (this.token === ">") {
89
+ return result("bin", ">", expr, this.next().read_expr());
90
+ }
91
+ if (this.token === this.tok.T_IS_SMALLER_OR_EQUAL) {
92
+ return result("bin", "<=", expr, this.next().read_expr());
93
+ }
94
+ if (this.token === this.tok.T_IS_GREATER_OR_EQUAL) {
95
+ return result("bin", ">=", expr, this.next().read_expr());
96
+ }
97
+ if (this.token === this.tok.T_SPACESHIP) {
98
+ return result("bin", "<=>", expr, this.next().read_expr());
99
+ }
100
+
101
+ if (this.token === this.tok.T_INSTANCEOF) {
102
+ expr = result(
103
+ "bin",
104
+ "instanceof",
105
+ expr,
106
+ this.next().read_class_name_reference(),
107
+ );
108
+ if (
109
+ this.token !== ";" &&
110
+ this.token !== this.tok.T_INLINE_HTML &&
111
+ this.token !== this.EOF
112
+ ) {
113
+ expr = this.read_expr(expr);
114
+ }
115
+ }
116
+
117
+ // extra operations :
118
+ // $username = $_GET['user'] ?? 'nobody';
119
+ if (this.token === this.tok.T_COALESCE) {
120
+ return result("bin", "??", expr, this.next().read_expr());
121
+ }
122
+
123
+ // extra operations :
124
+ // $username = $_GET['user'] ? true : false;
125
+ if (this.token === "?") {
126
+ let trueArg = null;
127
+ if (this.next().token !== ":") {
128
+ trueArg = this.read_expr();
129
+ }
130
+ this.expect(":") && this.next();
131
+ return result("retif", expr, trueArg, this.read_expr());
132
+ } else {
133
+ // see #193
134
+ result.destroy(expr);
135
+ }
136
+
137
+ return expr;
138
+ },
139
+
140
+ /*
141
+ * Reads a cast expression
142
+ */
143
+ read_expr_cast(type) {
144
+ return this.node("cast")(type, this.text(), this.next().read_expr());
145
+ },
146
+
147
+ /*
148
+ * Read a isset variable
149
+ */
150
+ read_isset_variable() {
151
+ return this.read_expr();
152
+ },
153
+
154
+ /*
155
+ * Reads isset variables
156
+ */
157
+ read_isset_variables() {
158
+ return this.read_function_list(this.read_isset_variable, ",");
159
+ },
160
+
161
+ /*
162
+ * Reads internal PHP functions
163
+ */
164
+ read_internal_functions_in_yacc() {
165
+ let result = null;
166
+ switch (this.token) {
167
+ case this.tok.T_ISSET:
168
+ {
169
+ result = this.node("isset");
170
+ if (this.next().expect("(")) {
171
+ this.next();
172
+ }
173
+ const variables = this.read_isset_variables();
174
+ if (this.expect(")")) {
175
+ this.next();
176
+ }
177
+ result = result(variables);
178
+ }
179
+ break;
180
+ case this.tok.T_EMPTY:
181
+ {
182
+ result = this.node("empty");
183
+ if (this.next().expect("(")) {
184
+ this.next();
185
+ }
186
+ const expression = this.read_expr();
187
+ if (this.expect(")")) {
188
+ this.next();
189
+ }
190
+ result = result(expression);
191
+ }
192
+ break;
193
+ case this.tok.T_INCLUDE:
194
+ result = this.node("include")(false, false, this.next().read_expr());
195
+ break;
196
+ case this.tok.T_INCLUDE_ONCE:
197
+ result = this.node("include")(true, false, this.next().read_expr());
198
+ break;
199
+ case this.tok.T_EVAL:
200
+ {
201
+ result = this.node("eval");
202
+ if (this.next().expect("(")) {
203
+ this.next();
204
+ }
205
+ const expr = this.read_expr();
206
+ if (this.expect(")")) {
207
+ this.next();
208
+ }
209
+ result = result(expr);
210
+ }
211
+ break;
212
+ case this.tok.T_REQUIRE:
213
+ result = this.node("include")(false, true, this.next().read_expr());
214
+ break;
215
+ case this.tok.T_REQUIRE_ONCE:
216
+ result = this.node("include")(true, true, this.next().read_expr());
217
+ break;
218
+ }
219
+
220
+ return result;
221
+ },
222
+
223
+ /*
224
+ * Reads optional expression
225
+ */
226
+ read_optional_expr(stopToken) {
227
+ if (this.token !== stopToken) {
228
+ return this.read_expr();
229
+ }
230
+
231
+ return null;
232
+ },
233
+
234
+ /*
235
+ * Reads exit expression
236
+ */
237
+ read_exit_expr() {
238
+ let expression = null;
239
+
240
+ if (this.token === "(") {
241
+ this.next();
242
+ expression = this.read_optional_expr(")");
243
+ this.expect(")") && this.next();
244
+ }
245
+
246
+ return expression;
247
+ },
248
+
249
+ /*
250
+ * ```ebnf
251
+ * Reads an expression
252
+ * expr ::= @todo
253
+ * ```
254
+ */
255
+ read_expr_item() {
256
+ let result,
257
+ expr,
258
+ attrs = [];
259
+ if (this.token === "+") {
260
+ return this.node("unary")("+", this.next().read_expr());
261
+ }
262
+ if (this.token === "-") {
263
+ return this.node("unary")("-", this.next().read_expr());
264
+ }
265
+ if (this.token === "!") {
266
+ return this.node("unary")("!", this.next().read_expr());
267
+ }
268
+ if (this.token === "~") {
269
+ return this.node("unary")("~", this.next().read_expr());
270
+ }
271
+
272
+ if (this.token === "(") {
273
+ expr = this.next().read_expr();
274
+ expr.parenthesizedExpression = true;
275
+ this.expect(")") && this.next();
276
+ return this.handleDereferencable(expr);
277
+ }
278
+
279
+ if (this.token === "`") {
280
+ // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1048
281
+ return this.read_encapsed_string("`");
282
+ }
283
+
284
+ if (this.token === this.tok.T_LIST) {
285
+ let assign = null;
286
+ const isInner = this.innerList;
287
+ result = this.node("list");
288
+ if (!isInner) {
289
+ assign = this.node("assign");
290
+ }
291
+ if (this.next().expect("(")) {
292
+ this.next();
293
+ }
294
+
295
+ if (!this.innerList) this.innerList = true;
296
+
297
+ // reads inner items
298
+ const assignList = this.read_array_pair_list(false);
299
+ if (this.expect(")")) {
300
+ this.next();
301
+ }
302
+
303
+ // check if contains at least one assignment statement
304
+ let hasItem = false;
305
+ for (let i = 0; i < assignList.length; i++) {
306
+ if (assignList[i] !== null && assignList[i].kind !== "noop") {
307
+ hasItem = true;
308
+ break;
309
+ }
310
+ }
311
+ if (!hasItem) {
312
+ /* istanbul ignore next */
313
+ this.raiseError(
314
+ "Fatal Error : Cannot use empty list on line " +
315
+ this.lexer.yylloc.first_line,
316
+ );
317
+ }
318
+
319
+ // handles the node resolution
320
+ if (!isInner) {
321
+ this.innerList = false;
322
+ if (this.expect("=")) {
323
+ return assign(
324
+ result(assignList, false),
325
+ this.next().read_expr(),
326
+ "=",
327
+ );
328
+ } else {
329
+ // error fallback : list($a, $b);
330
+ /* istanbul ignore next */
331
+ return result(assignList, false);
332
+ }
333
+ } else {
334
+ return result(assignList, false);
335
+ }
336
+ }
337
+
338
+ if (this.token === this.tok.T_ATTRIBUTE) {
339
+ attrs = this.read_attr_list();
340
+ }
341
+
342
+ if (this.token === this.tok.T_CLONE) {
343
+ return this.node("clone")(this.next().read_expr());
344
+ }
345
+
346
+ switch (this.token) {
347
+ case this.tok.T_INC:
348
+ return this.node("pre")("+", this.next().read_variable(false, false));
349
+
350
+ case this.tok.T_DEC:
351
+ return this.node("pre")("-", this.next().read_variable(false, false));
352
+
353
+ case this.tok.T_NEW:
354
+ expr = this.read_new_expr();
355
+ if (this.token === this.tok.T_OBJECT_OPERATOR && this.version < 804) {
356
+ this.raiseError(
357
+ "New without parenthesis is not allowed before PHP 8.4",
358
+ );
359
+ }
360
+ return this.handleDereferencable(expr);
361
+
362
+ case this.tok.T_ISSET:
363
+ case this.tok.T_EMPTY:
364
+ case this.tok.T_INCLUDE:
365
+ case this.tok.T_INCLUDE_ONCE:
366
+ case this.tok.T_EVAL:
367
+ case this.tok.T_REQUIRE:
368
+ case this.tok.T_REQUIRE_ONCE:
369
+ return this.read_internal_functions_in_yacc();
370
+
371
+ case this.tok.T_MATCH:
372
+ return this.read_match_expression();
373
+ case this.tok.T_INT_CAST:
374
+ return this.read_expr_cast("int");
375
+
376
+ case this.tok.T_DOUBLE_CAST:
377
+ return this.read_expr_cast("float");
378
+
379
+ case this.tok.T_STRING_CAST:
380
+ return this.read_expr_cast(
381
+ this.text().indexOf("binary") !== -1 ? "binary" : "string",
382
+ );
383
+
384
+ case this.tok.T_ARRAY_CAST:
385
+ return this.read_expr_cast("array");
386
+
387
+ case this.tok.T_OBJECT_CAST:
388
+ return this.read_expr_cast("object");
389
+
390
+ case this.tok.T_BOOL_CAST:
391
+ return this.read_expr_cast("bool");
392
+
393
+ case this.tok.T_UNSET_CAST:
394
+ return this.read_expr_cast("unset");
395
+
396
+ case this.tok.T_THROW: {
397
+ if (this.version < 800) {
398
+ this.raiseError("PHP 8+ is required to use throw as an expression");
399
+ }
400
+ const result = this.node("throw");
401
+ const expr = this.next().read_expr();
402
+ return result(expr);
403
+ }
404
+ case this.tok.T_EXIT: {
405
+ const useDie = this.lexer.yytext.toLowerCase() === "die";
406
+ result = this.node("exit");
407
+ this.next();
408
+ const expression = this.read_exit_expr();
409
+ return result(expression, useDie);
410
+ }
411
+
412
+ case this.tok.T_PRINT:
413
+ return this.node("print")(this.next().read_expr());
414
+
415
+ // T_YIELD (expr (T_DOUBLE_ARROW expr)?)?
416
+ case this.tok.T_YIELD: {
417
+ let value = null;
418
+ let key = null;
419
+ result = this.node("yield");
420
+ if (this.next().is("EXPR")) {
421
+ // reads the yield return value
422
+ value = this.read_expr();
423
+ if (this.token === this.tok.T_DOUBLE_ARROW) {
424
+ // reads the yield returned key
425
+ key = value;
426
+ value = this.next().read_expr();
427
+ }
428
+ }
429
+ return result(value, key);
430
+ }
431
+
432
+ // T_YIELD_FROM expr
433
+ case this.tok.T_YIELD_FROM:
434
+ result = this.node("yieldfrom");
435
+ expr = this.next().read_expr();
436
+ return result(expr);
437
+
438
+ case this.tok.T_FN:
439
+ case this.tok.T_FUNCTION:
440
+ return this.read_inline_function(undefined, attrs);
441
+
442
+ case this.tok.T_STATIC: {
443
+ const backup = [this.token, this.lexer.getState()];
444
+ this.next();
445
+ if (
446
+ this.token === this.tok.T_FUNCTION ||
447
+ (this.version >= 704 && this.token === this.tok.T_FN)
448
+ ) {
449
+ // handles static function
450
+ return this.read_inline_function([0, 1, 0], attrs);
451
+ } else {
452
+ // rollback
453
+ this.lexer.tokens.push(backup);
454
+ this.next();
455
+ }
456
+ }
457
+ }
458
+
459
+ // SCALAR | VARIABLE
460
+ if (this.is("VARIABLE")) {
461
+ result = this.node();
462
+ expr = this.read_variable(false, false);
463
+
464
+ // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L877
465
+ // should accept only a variable
466
+ const isConst =
467
+ expr.kind === "identifier" ||
468
+ (expr.kind === "staticlookup" && expr.offset.kind === "identifier");
469
+
470
+ // VARIABLES SPECIFIC OPERATIONS
471
+ switch (this.token) {
472
+ case "=": {
473
+ if (isConst) this.error("VARIABLE");
474
+ if (this.next().token == "&") {
475
+ return this.read_assignref(result, expr);
476
+ }
477
+ return result("assign", expr, this.read_expr(), "=");
478
+ }
479
+
480
+ // operations :
481
+ case this.tok.T_PLUS_EQUAL:
482
+ if (isConst) this.error("VARIABLE");
483
+ return result("assign", expr, this.next().read_expr(), "+=");
484
+
485
+ case this.tok.T_MINUS_EQUAL:
486
+ if (isConst) this.error("VARIABLE");
487
+ return result("assign", expr, this.next().read_expr(), "-=");
488
+
489
+ case this.tok.T_MUL_EQUAL:
490
+ if (isConst) this.error("VARIABLE");
491
+ return result("assign", expr, this.next().read_expr(), "*=");
492
+
493
+ case this.tok.T_POW_EQUAL:
494
+ if (isConst) this.error("VARIABLE");
495
+ return result("assign", expr, this.next().read_expr(), "**=");
496
+
497
+ case this.tok.T_DIV_EQUAL:
498
+ if (isConst) this.error("VARIABLE");
499
+ return result("assign", expr, this.next().read_expr(), "/=");
500
+
501
+ case this.tok.T_CONCAT_EQUAL:
502
+ if (isConst) this.error("VARIABLE");
503
+ return result("assign", expr, this.next().read_expr(), ".=");
504
+
505
+ case this.tok.T_MOD_EQUAL:
506
+ if (isConst) this.error("VARIABLE");
507
+ return result("assign", expr, this.next().read_expr(), "%=");
508
+
509
+ case this.tok.T_AND_EQUAL:
510
+ if (isConst) this.error("VARIABLE");
511
+ return result("assign", expr, this.next().read_expr(), "&=");
512
+
513
+ case this.tok.T_OR_EQUAL:
514
+ if (isConst) this.error("VARIABLE");
515
+ return result("assign", expr, this.next().read_expr(), "|=");
516
+
517
+ case this.tok.T_XOR_EQUAL:
518
+ if (isConst) this.error("VARIABLE");
519
+ return result("assign", expr, this.next().read_expr(), "^=");
520
+
521
+ case this.tok.T_SL_EQUAL:
522
+ if (isConst) this.error("VARIABLE");
523
+ return result("assign", expr, this.next().read_expr(), "<<=");
524
+
525
+ case this.tok.T_SR_EQUAL:
526
+ if (isConst) this.error("VARIABLE");
527
+ return result("assign", expr, this.next().read_expr(), ">>=");
528
+
529
+ case this.tok.T_COALESCE_EQUAL:
530
+ if (isConst) this.error("VARIABLE");
531
+ return result("assign", expr, this.next().read_expr(), "??=");
532
+
533
+ case this.tok.T_INC:
534
+ if (isConst) this.error("VARIABLE");
535
+ this.next();
536
+ return result("post", "+", expr);
537
+ case this.tok.T_DEC:
538
+ if (isConst) this.error("VARIABLE");
539
+ this.next();
540
+ return result("post", "-", expr);
541
+ default:
542
+ // see #193
543
+ result.destroy(expr);
544
+ }
545
+ } else if (this.is("SCALAR")) {
546
+ result = this.node();
547
+ expr = this.read_scalar();
548
+ if (expr.kind === "array" && expr.shortForm && this.token === "=") {
549
+ // list assign
550
+ const list = this.convertToList(expr);
551
+ if (expr.loc) list.loc = expr.loc;
552
+ const right = this.next().read_expr();
553
+ return result("assign", list, right, "=");
554
+ } else {
555
+ // see #189 - swap docs on nodes
556
+ result.destroy(expr);
557
+ }
558
+ // classic array
559
+ return this.handleDereferencable(expr);
560
+ } else {
561
+ this.error("EXPR");
562
+ this.next();
563
+ }
564
+
565
+ // returns variable | scalar
566
+ return expr;
567
+ },
568
+
569
+ /*
570
+ * Recursively convert nested array to nested list.
571
+ */
572
+ convertToList(array) {
573
+ const convertedItems = array.items.map((entry) => {
574
+ if (
575
+ entry.value &&
576
+ entry.value.kind === "array" &&
577
+ entry.value.shortForm
578
+ ) {
579
+ entry.value = this.convertToList(entry.value);
580
+ }
581
+ return entry;
582
+ });
583
+ const node = this.node("list")(convertedItems, true);
584
+ if (array.loc) node.loc = array.loc;
585
+ if (array.leadingComments) node.leadingComments = array.leadingComments;
586
+ if (array.trailingComments) node.trailingComments = array.trailingComments;
587
+ return node;
588
+ },
589
+
590
+ /*
591
+ * Reads assignment
592
+ * @param {*} left
593
+ */
594
+ read_assignref(result, left) {
595
+ this.next();
596
+ let right;
597
+ if (this.token === this.tok.T_NEW) {
598
+ if (this.version >= 700) {
599
+ this.error();
600
+ }
601
+ right = this.read_new_expr();
602
+ } else {
603
+ right = this.read_variable(false, false);
604
+ }
605
+
606
+ return result("assignref", left, right);
607
+ },
608
+
609
+ /*
610
+ *
611
+ * inline_function:
612
+ * function returns_ref backup_doc_comment '(' parameter_list ')' lexical_vars return_type
613
+ * backup_fn_flags '{' inner_statement_list '}' backup_fn_flags
614
+ * { $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $2 | $13, $1, $3,
615
+ * zend_string_init("{closure}", sizeof("{closure}") - 1, 0),
616
+ * $5, $7, $11, $8); CG(extra_fn_flags) = $9; }
617
+ * | fn returns_ref '(' parameter_list ')' return_type backup_doc_comment T_DOUBLE_ARROW backup_fn_flags backup_lex_pos expr backup_fn_flags
618
+ * { $$ = zend_ast_create_decl(ZEND_AST_ARROW_FUNC, $2 | $12, $1, $7,
619
+ * zend_string_init("{closure}", sizeof("{closure}") - 1, 0), $4, NULL,
620
+ * zend_ast_create(ZEND_AST_RETURN, $11), $6);
621
+ * ((zend_ast_decl *) $$)->lex_pos = $10;
622
+ * CG(extra_fn_flags) = $9; } *
623
+ */
624
+ read_inline_function(flags, attrs) {
625
+ if (this.token === this.tok.T_FUNCTION) {
626
+ const result = this.read_function(true, flags, attrs);
627
+ result.attrGroups = attrs;
628
+ return result;
629
+ }
630
+ // introduced in PHP 7.4
631
+ if (!this.version >= 704) {
632
+ this.raiseError("Arrow Functions are not allowed");
633
+ }
634
+ // as an arrowfunc
635
+ const node = this.node("arrowfunc");
636
+ // eat T_FN
637
+ if (this.expect(this.tok.T_FN)) this.next();
638
+ // check the &
639
+ const isRef = this.is_reference();
640
+ // ...
641
+ if (this.expect("(")) this.next();
642
+ const params = this.read_parameter_list();
643
+ if (this.expect(")")) this.next();
644
+ let nullable = false;
645
+ let returnType = null;
646
+ if (this.token === ":") {
647
+ if (this.next().token === "?") {
648
+ nullable = true;
649
+ this.next();
650
+ }
651
+ returnType = this.read_types();
652
+ }
653
+ if (this.expect(this.tok.T_DOUBLE_ARROW)) this.next();
654
+ const body = this.read_expr();
655
+ const result = node(
656
+ params,
657
+ isRef,
658
+ body,
659
+ returnType,
660
+ nullable,
661
+ flags ? true : false,
662
+ );
663
+ result.attrGroups = attrs;
664
+ return result;
665
+ },
666
+
667
+ read_match_expression() {
668
+ const node = this.node("match");
669
+ this.expect(this.tok.T_MATCH) && this.next();
670
+ if (this.version < 800) {
671
+ this.raiseError("Match statements are not allowed before PHP 8");
672
+ }
673
+ let cond = null;
674
+ let arms = [];
675
+ if (this.expect("(")) this.next();
676
+ cond = this.read_expr();
677
+ if (this.expect(")")) this.next();
678
+ if (this.expect("{")) this.next();
679
+ arms = this.read_match_arms();
680
+ if (this.expect("}")) this.next();
681
+ return node(cond, arms);
682
+ },
683
+
684
+ read_match_arms() {
685
+ return this.read_list(() => this.read_match_arm(), ",", true);
686
+ },
687
+
688
+ read_match_arm() {
689
+ if (this.token === "}") {
690
+ return;
691
+ }
692
+ return this.node("matcharm")(this.read_match_arm_conds(), this.read_expr());
693
+ },
694
+
695
+ read_match_arm_conds() {
696
+ let conds = [];
697
+ if (this.token === this.tok.T_DEFAULT) {
698
+ conds = null;
699
+ this.next();
700
+ } else {
701
+ conds.push(this.read_expr());
702
+ while (this.token === ",") {
703
+ this.next();
704
+ if (this.token === this.tok.T_DOUBLE_ARROW) {
705
+ this.next();
706
+ return conds;
707
+ }
708
+ conds.push(this.read_expr());
709
+ }
710
+ }
711
+ if (this.expect(this.tok.T_DOUBLE_ARROW)) {
712
+ this.next();
713
+ }
714
+ return conds;
715
+ },
716
+
717
+ read_attribute() {
718
+ const name = this.text();
719
+ let args = [];
720
+ this.next();
721
+ if (this.token === "(") {
722
+ args = this.read_argument_list();
723
+ }
724
+ return this.node("attribute")(name, args);
725
+ },
726
+ read_attr_list() {
727
+ const list = [];
728
+ if (this.token === this.tok.T_ATTRIBUTE) {
729
+ do {
730
+ const attrGr = this.node("attrgroup")([]);
731
+ this.next();
732
+ attrGr.attrs.push(this.read_attribute());
733
+ while (this.token === ",") {
734
+ this.next();
735
+ if (this.token !== "]") attrGr.attrs.push(this.read_attribute());
736
+ }
737
+ list.push(attrGr);
738
+ this.expect("]");
739
+ this.next();
740
+ } while (this.token === this.tok.T_ATTRIBUTE);
741
+ }
742
+ return list;
743
+ },
744
+
745
+ /*
746
+ * ```ebnf
747
+ * new_expr ::= T_NEW (namespace_name function_argument_list) | (T_CLASS ... class declaration)
748
+ * ```
749
+ * https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L850
750
+ */
751
+ read_new_expr() {
752
+ const result = this.node("new");
753
+ this.expect(this.tok.T_NEW) && this.next();
754
+ let args = [];
755
+ if (this.token === "(") {
756
+ this.next();
757
+ const newExp = this.read_expr();
758
+ this.expect(")");
759
+ this.next();
760
+ if (this.token === "(") {
761
+ args = this.read_argument_list();
762
+ }
763
+ return result(newExp, args);
764
+ }
765
+ const attrs = this.read_attr_list();
766
+ const isReadonly = this.token === this.tok.T_READ_ONLY;
767
+ if (
768
+ this.token === this.tok.T_CLASS ||
769
+ (isReadonly && this.next().token === this.tok.T_CLASS)
770
+ ) {
771
+ const what = this.node("class");
772
+ // Annonymous class declaration
773
+ if (this.next().token === "(") {
774
+ args = this.read_argument_list();
775
+ }
776
+ const propExtends = this.read_extends_from();
777
+ const propImplements = this.read_implements_list();
778
+ let body = null;
779
+ if (this.expect("{")) {
780
+ body = this.next().read_class_body(true, false);
781
+ }
782
+ const whatNode = what(null, propExtends, propImplements, body, [
783
+ 0,
784
+ 0,
785
+ 0,
786
+ isReadonly ? 1 : 0,
787
+ ]);
788
+ whatNode.attrGroups = attrs;
789
+ return result(whatNode, args);
790
+ }
791
+ // Already existing class
792
+ let name = this.read_new_class_name();
793
+ while (this.token === "[") {
794
+ const offsetNode = this.node("offsetlookup");
795
+ const offset = this.next().read_encaps_var_offset();
796
+ this.expect("]") && this.next();
797
+ name = offsetNode(name, offset);
798
+ }
799
+ if (this.token === "(") {
800
+ args = this.read_argument_list();
801
+ }
802
+ return result(name, args);
803
+ },
804
+ /*
805
+ * Reads a class name
806
+ * ```ebnf
807
+ * read_new_class_name ::= namespace_name | variable
808
+ * ```
809
+ */
810
+ read_new_class_name() {
811
+ if (
812
+ this.token === this.tok.T_NS_SEPARATOR ||
813
+ this.token === this.tok.T_NAME_RELATIVE ||
814
+ this.token === this.tok.T_NAME_QUALIFIED ||
815
+ this.token === this.tok.T_NAME_FULLY_QUALIFIED ||
816
+ this.token === this.tok.T_STRING ||
817
+ this.token === this.tok.T_NAMESPACE
818
+ ) {
819
+ let result = this.read_namespace_name(true);
820
+ if (this.token === this.tok.T_DOUBLE_COLON) {
821
+ result = this.read_static_getter(result);
822
+ }
823
+ return result;
824
+ } else if (this.is("VARIABLE")) {
825
+ return this.read_variable(true, false);
826
+ } else {
827
+ this.expect([this.tok.T_STRING, "VARIABLE"]);
828
+ }
829
+ },
830
+ handleDereferencable(expr) {
831
+ while (this.token !== this.EOF) {
832
+ if (
833
+ this.token === this.tok.T_OBJECT_OPERATOR ||
834
+ this.token === this.tok.T_DOUBLE_COLON
835
+ ) {
836
+ expr = this.recursive_variable_chain_scan(expr, false, false, true);
837
+ } else if (this.token === this.tok.T_CURLY_OPEN || this.token === "[") {
838
+ expr = this.read_dereferencable(expr);
839
+ } else if (this.token === "(") {
840
+ // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1118
841
+ expr = this.node("call")(expr, this.read_argument_list());
842
+ } else {
843
+ return expr;
844
+ }
845
+ }
846
+ return expr;
847
+ },
848
+ };