@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,718 @@
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
+ /*
10
+ * reading a class
11
+ * ```ebnf
12
+ * class ::= class_scope? T_CLASS T_STRING (T_EXTENDS NAMESPACE_NAME)? (T_IMPLEMENTS (NAMESPACE_NAME ',')* NAMESPACE_NAME)? '{' CLASS_BODY '}'
13
+ * ```
14
+ */
15
+ read_class_declaration_statement(attrs) {
16
+ const result = this.node("class");
17
+ const flag = this.read_class_modifiers();
18
+ // graceful mode : ignore token & go next
19
+ if (this.token !== this.tok.T_CLASS) {
20
+ this.error(this.tok.T_CLASS);
21
+ this.next();
22
+ return null;
23
+ }
24
+ this.next().expect(this.tok.T_STRING);
25
+ let propName = this.node("identifier");
26
+ const name = this.text();
27
+ this.next();
28
+ propName = propName(name);
29
+ const propExtends = this.read_extends_from();
30
+ const propImplements = this.read_implements_list();
31
+ this.expect("{");
32
+ const body = this.next().read_class_body(true, false);
33
+ const node = result(propName, propExtends, propImplements, body, flag);
34
+ if (attrs) node.attrGroups = attrs;
35
+ return node;
36
+ },
37
+
38
+ read_class_modifiers() {
39
+ const modifier = this.read_class_modifier({
40
+ readonly: 0,
41
+ final_or_abstract: 0,
42
+ });
43
+ return [0, 0, modifier.final_or_abstract, modifier.readonly];
44
+ },
45
+
46
+ read_class_modifier(memo) {
47
+ if (this.token === this.tok.T_READ_ONLY) {
48
+ this.next();
49
+ memo.readonly = 1;
50
+ memo = this.read_class_modifier(memo);
51
+ } else if (
52
+ memo.final_or_abstract === 0 &&
53
+ this.token === this.tok.T_ABSTRACT
54
+ ) {
55
+ this.next();
56
+ memo.final_or_abstract = 1;
57
+ memo = this.read_class_modifier(memo);
58
+ } else if (
59
+ memo.final_or_abstract === 0 &&
60
+ this.token === this.tok.T_FINAL
61
+ ) {
62
+ this.next();
63
+ memo.final_or_abstract = 2;
64
+ memo = this.read_class_modifier(memo);
65
+ }
66
+
67
+ return memo;
68
+ },
69
+
70
+ /*
71
+ * Reads a class body
72
+ * ```ebnf
73
+ * class_body ::= (member_flags? (T_VAR | T_STRING | T_FUNCTION))*
74
+ * ```
75
+ */
76
+ read_class_body(allow_variables, allow_enum_cases) {
77
+ let result = [];
78
+ let attrs = [];
79
+ while (this.token !== this.EOF && this.token !== "}") {
80
+ if (this.token === this.tok.T_COMMENT) {
81
+ result.push(this.read_comment());
82
+ continue;
83
+ }
84
+
85
+ if (this.token === this.tok.T_DOC_COMMENT) {
86
+ result.push(this.read_doc_comment());
87
+ continue;
88
+ }
89
+
90
+ // check T_USE trait
91
+ if (this.token === this.tok.T_USE) {
92
+ result = result.concat(this.read_trait_use_statement());
93
+ continue;
94
+ }
95
+
96
+ // check enum cases
97
+ if (allow_enum_cases && this.token === this.tok.T_CASE) {
98
+ const enumcase = this.read_enum_case();
99
+ if (this.expect(";")) {
100
+ this.next();
101
+ }
102
+ result = result.concat(enumcase);
103
+ continue;
104
+ }
105
+
106
+ if (this.token === this.tok.T_ATTRIBUTE) {
107
+ attrs = this.read_attr_list();
108
+ }
109
+
110
+ const locStart = this.position();
111
+
112
+ // read member flags
113
+ const flags = this.read_member_flags(false);
114
+
115
+ // check constant
116
+ if (this.token === this.tok.T_CONST) {
117
+ const constants = this.read_constant_list(flags, attrs);
118
+ if (this.expect(";")) {
119
+ this.next();
120
+ }
121
+ result = result.concat(constants);
122
+ continue;
123
+ }
124
+
125
+ // jump over T_VAR then land on T_VARIABLE
126
+ if (allow_variables && this.token === this.tok.T_VAR) {
127
+ this.next().expect(this.tok.T_VARIABLE);
128
+ flags[0] = null; // public (as null)
129
+ flags[1] = 0; // non static var
130
+ }
131
+
132
+ if (this.token === this.tok.T_FUNCTION) {
133
+ // reads a function
134
+ result.push(this.read_function(false, flags, attrs, locStart));
135
+ attrs = [];
136
+ } else if (
137
+ allow_variables &&
138
+ (this.token === this.tok.T_VARIABLE ||
139
+ (this.version >= 801 && this.token === this.tok.T_READ_ONLY) ||
140
+ // support https://wiki.php.net/rfc/typed_properties_v2
141
+ (this.version >= 704 &&
142
+ (this.token === "?" ||
143
+ this.token === this.tok.T_ARRAY ||
144
+ this.token === this.tok.T_CALLABLE ||
145
+ this.token === this.tok.T_NAMESPACE ||
146
+ this.token === this.tok.T_NAME_FULLY_QUALIFIED ||
147
+ this.token === this.tok.T_NAME_QUALIFIED ||
148
+ this.token === this.tok.T_NAME_RELATIVE ||
149
+ this.token === this.tok.T_NS_SEPARATOR ||
150
+ this.token === this.tok.T_STRING)))
151
+ ) {
152
+ // reads a variable
153
+ const variables = this.read_variable_list(flags, attrs);
154
+ attrs = [];
155
+ result = result.concat(variables);
156
+ } else {
157
+ // raise an error
158
+ this.error([
159
+ this.tok.T_CONST,
160
+ ...(allow_variables ? [this.tok.T_VARIABLE] : []),
161
+ ...(allow_enum_cases ? [this.tok.T_CASE] : []),
162
+ this.tok.T_FUNCTION,
163
+ ]);
164
+ // ignore token
165
+ this.next();
166
+ }
167
+ }
168
+ this.expect("}");
169
+ this.next();
170
+ return result;
171
+ },
172
+ /*
173
+ * Reads variable list
174
+ * ```ebnf
175
+ * variable_list ::= (variable_declaration ',')* variable_declaration
176
+ * ```
177
+ */
178
+ read_variable_list(flags, attrs) {
179
+ let property_statement = this.node("propertystatement");
180
+
181
+ const properties = this.read_list(
182
+ /*
183
+ * Reads a variable declaration
184
+ *
185
+ * ```ebnf
186
+ * variable_declaration ::= T_VARIABLE '=' scalar
187
+ * ```
188
+ */
189
+ function read_variable_declaration() {
190
+ const result = this.node("property");
191
+ let readonly = false;
192
+ if (this.token === this.tok.T_READ_ONLY) {
193
+ readonly = true;
194
+ this.next();
195
+ }
196
+ const [nullable, type] = this.read_optional_type();
197
+ this.expect(this.tok.T_VARIABLE);
198
+ let propName = this.node("identifier");
199
+ const name = this.text().substring(1); // ignore $
200
+ this.next();
201
+ propName = propName(name);
202
+
203
+ let value = null;
204
+ let property_hooks = [];
205
+
206
+ this.expect([",", ";", "=", "{"]);
207
+
208
+ // Property has a value
209
+ if (this.token === "=") {
210
+ // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L815
211
+ value = this.next().read_expr();
212
+ }
213
+
214
+ // Property is using a hook to define getter/setters
215
+ if (this.token === "{") {
216
+ property_hooks = this.read_property_hooks();
217
+ } else {
218
+ this.expect([";", ","]);
219
+ }
220
+
221
+ return result(
222
+ propName,
223
+ value,
224
+ readonly,
225
+ nullable,
226
+ type,
227
+ property_hooks,
228
+ attrs || [],
229
+ );
230
+ },
231
+ ",",
232
+ );
233
+
234
+ property_statement = property_statement(null, properties, flags);
235
+
236
+ // semicolons are found only for regular properties definitions.
237
+ // Property hooks are terminated by a closing curly brace, }.
238
+ // property_statement is instanciated before this check to avoid including the semicolon in the AST end location of the property.
239
+ if (this.token === ";") {
240
+ this.next();
241
+ }
242
+ return property_statement;
243
+ },
244
+
245
+ /**
246
+ * Reads property hooks
247
+ *
248
+ * @returns {PropertyHook[]}
249
+ */
250
+ read_property_hooks() {
251
+ if (this.version < 804) {
252
+ this.raiseError("Parse Error: Typed Class Constants requires PHP 8.4+");
253
+ }
254
+ this.expect("{");
255
+ this.next();
256
+
257
+ const hooks = [];
258
+
259
+ while (this.token !== "}") {
260
+ hooks.push(this.read_property_hook());
261
+ }
262
+
263
+ if (this.token === "}") {
264
+ this.next();
265
+ return hooks;
266
+ }
267
+ return [];
268
+ },
269
+
270
+ read_property_hook() {
271
+ const property_hooks = this.node("propertyhook");
272
+
273
+ const is_final = this.token === this.tok.T_FINAL;
274
+ if (is_final) this.next();
275
+
276
+ const is_reference = this.token === "&";
277
+ if (is_reference) this.next();
278
+
279
+ const method_name = this.text();
280
+
281
+ if (method_name !== "get" && method_name !== "set") {
282
+ this.raiseError(
283
+ "Parse Error: Property hooks must be either 'get' or 'set'",
284
+ );
285
+ }
286
+ this.next();
287
+
288
+ let parameter = null;
289
+ let body = null;
290
+ this.expect([this.tok.T_DOUBLE_ARROW, "{", "(", ";"]);
291
+
292
+ // interface or abstract definition
293
+ if (this.token === ";") {
294
+ this.next();
295
+ }
296
+
297
+ if (this.token === "(") {
298
+ this.next();
299
+ parameter = this.read_parameter(false);
300
+ this.expect(")");
301
+ this.next();
302
+ }
303
+
304
+ if (this.token === this.tok.T_DOUBLE_ARROW) {
305
+ this.next();
306
+ body = this.read_expr();
307
+ this.next();
308
+ } else if (this.token === "{") {
309
+ body = this.read_code_block();
310
+ }
311
+
312
+ return property_hooks(method_name, is_final, is_reference, parameter, body);
313
+ },
314
+
315
+ /*
316
+ * Reads constant list
317
+ * ```ebnf
318
+ * constant_list ::= T_CONST [type] (constant_declaration ',')* constant_declaration
319
+ * ```
320
+ */
321
+ read_constant_list(flags, attrs) {
322
+ if (this.expect(this.tok.T_CONST)) {
323
+ this.next();
324
+ }
325
+
326
+ const [nullable, type] =
327
+ this.version >= 803 ? this.read_optional_type() : [false, null];
328
+
329
+ const result = this.node("classconstant");
330
+ const items = this.read_list(
331
+ /*
332
+ * Reads a constant declaration
333
+ *
334
+ * ```ebnf
335
+ * constant_declaration ::= (T_STRING | IDENTIFIER) '=' expr
336
+ * ```
337
+ * @return {Constant} [:link:](AST.md#constant)
338
+ */
339
+ function read_constant_declaration() {
340
+ const result = this.node("constant");
341
+ let constName = null;
342
+ let value = null;
343
+ if (
344
+ this.token === this.tok.T_STRING ||
345
+ (this.version >= 700 && this.is("IDENTIFIER"))
346
+ ) {
347
+ constName = this.node("identifier");
348
+ const name = this.text();
349
+ this.next();
350
+ constName = constName(name);
351
+ } else {
352
+ this.expect("IDENTIFIER");
353
+ }
354
+ if (this.expect("=")) {
355
+ value = this.next().read_expr();
356
+ }
357
+ return result(constName, value);
358
+ },
359
+ ",",
360
+ );
361
+
362
+ return result(null, items, flags, nullable, type, attrs || []);
363
+ },
364
+ /*
365
+ * Read member flags
366
+ * @return array
367
+ * 1st index : 0 => public, 1 => protected, 2 => private
368
+ * 2nd index : 0 => instance member, 1 => static member
369
+ * 3rd index : 0 => normal, 1 => abstract member, 2 => final member
370
+ */
371
+ read_member_flags(asInterface) {
372
+ const result = [-1, -1, -1];
373
+ if (this.is("T_MEMBER_FLAGS")) {
374
+ let idx = 0,
375
+ val = 0;
376
+ do {
377
+ switch (this.token) {
378
+ case this.tok.T_PUBLIC:
379
+ idx = 0;
380
+ val = 0;
381
+ break;
382
+ case this.tok.T_PROTECTED:
383
+ idx = 0;
384
+ val = 1;
385
+ break;
386
+ case this.tok.T_PRIVATE:
387
+ idx = 0;
388
+ val = 2;
389
+ break;
390
+ case this.tok.T_STATIC:
391
+ idx = 1;
392
+ val = 1;
393
+ break;
394
+ case this.tok.T_ABSTRACT:
395
+ idx = 2;
396
+ val = 1;
397
+ break;
398
+ case this.tok.T_FINAL:
399
+ idx = 2;
400
+ val = 2;
401
+ break;
402
+ }
403
+ if (asInterface) {
404
+ if (idx === 0 && val === 2) {
405
+ // an interface can't be private
406
+ this.expect([this.tok.T_PUBLIC, this.tok.T_PROTECTED]);
407
+ val = -1;
408
+ } else if (idx === 2 && val === 1) {
409
+ // an interface cant be abstract
410
+ this.error();
411
+ val = -1;
412
+ }
413
+ }
414
+ if (result[idx] !== -1) {
415
+ // already defined flag
416
+ this.error();
417
+ } else if (val !== -1) {
418
+ result[idx] = val;
419
+ }
420
+ } while (this.next().is("T_MEMBER_FLAGS"));
421
+ }
422
+
423
+ if (result[1] === -1) result[1] = 0;
424
+ if (result[2] === -1) result[2] = 0;
425
+ return result;
426
+ },
427
+
428
+ /*
429
+ * optional_type:
430
+ * /- empty -/ { $$ = NULL; }
431
+ * | type_expr { $$ = $1; }
432
+ * ;
433
+ *
434
+ * type_expr:
435
+ * type { $$ = $1; }
436
+ * | '?' type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; }
437
+ * | union_type { $$ = $1; }
438
+ * ;
439
+ *
440
+ * type:
441
+ * T_ARRAY { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY); }
442
+ * | T_CALLABLE { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_CALLABLE); }
443
+ * | name { $$ = $1; }
444
+ * ;
445
+ *
446
+ * union_type:
447
+ * type '|' type { $$ = zend_ast_create_list(2, ZEND_AST_TYPE_UNION, $1, $3); }
448
+ * | union_type '|' type { $$ = zend_ast_list_add($1, $3); }
449
+ * ;
450
+ */
451
+ read_optional_type() {
452
+ const nullable = this.token === "?";
453
+ if (nullable) {
454
+ this.next();
455
+ }
456
+
457
+ if (this.peekSkipComments() === "=") {
458
+ return [false, null];
459
+ }
460
+
461
+ let type = this.read_types();
462
+ if (nullable && !type) {
463
+ this.raiseError(
464
+ "Expecting a type definition combined with nullable operator",
465
+ );
466
+ }
467
+ if (!nullable && !type) {
468
+ return [false, null];
469
+ }
470
+ if (this.token === "|") {
471
+ type = [type];
472
+ do {
473
+ this.next();
474
+ const variant = this.read_type();
475
+ if (!variant) {
476
+ this.raiseError("Expecting a type definition");
477
+ break;
478
+ }
479
+ type.push(variant);
480
+ } while (this.token === "|");
481
+ }
482
+ return [nullable, type];
483
+ },
484
+
485
+ peekSkipComments() {
486
+ const lexerState = this.lexer.getState();
487
+ let nextToken;
488
+
489
+ do {
490
+ nextToken = this.lexer.lex();
491
+ } while (
492
+ nextToken === this.tok.T_COMMENT ||
493
+ nextToken === this.tok.T_WHITESPACE
494
+ );
495
+
496
+ this.lexer.setState(lexerState);
497
+ return nextToken;
498
+ },
499
+
500
+ /*
501
+ * reading an interface
502
+ * ```ebnf
503
+ * interface ::= T_INTERFACE T_STRING (T_EXTENDS (NAMESPACE_NAME ',')* NAMESPACE_NAME)? '{' INTERFACE_BODY '}'
504
+ * ```
505
+ */
506
+ read_interface_declaration_statement(attrs) {
507
+ const result = this.node("interface");
508
+ if (this.token !== this.tok.T_INTERFACE) {
509
+ this.error(this.tok.T_INTERFACE);
510
+ this.next();
511
+ return null;
512
+ }
513
+ this.next().expect(this.tok.T_STRING);
514
+ let propName = this.node("identifier");
515
+ const name = this.text();
516
+ this.next();
517
+ propName = propName(name);
518
+ const propExtends = this.read_interface_extends_list();
519
+ this.expect("{");
520
+ const body = this.next().read_interface_body();
521
+ return result(propName, propExtends, body, attrs || []);
522
+ },
523
+ /*
524
+ * Reads an interface body
525
+ * ```ebnf
526
+ * interface_body ::= (member_flags? (T_CONST | T_FUNCTION))*
527
+ * ```
528
+ */
529
+ read_interface_body() {
530
+ let result = [],
531
+ attrs = [];
532
+
533
+ while (this.token !== this.EOF && this.token !== "}") {
534
+ if (this.token === this.tok.T_COMMENT) {
535
+ result.push(this.read_comment());
536
+ continue;
537
+ }
538
+
539
+ if (this.token === this.tok.T_DOC_COMMENT) {
540
+ result.push(this.read_doc_comment());
541
+ continue;
542
+ }
543
+
544
+ const locStart = this.position();
545
+
546
+ attrs = this.read_attr_list();
547
+ // read member flags
548
+ const flags = this.read_member_flags(true);
549
+
550
+ // check constant
551
+ if (this.token === this.tok.T_CONST) {
552
+ const constants = this.read_constant_list(flags, attrs);
553
+ if (this.expect(";")) {
554
+ this.next();
555
+ }
556
+ result = result.concat(constants);
557
+ attrs = [];
558
+ } else if (this.token === this.tok.T_FUNCTION) {
559
+ // reads a function
560
+ const method = this.read_function_declaration(
561
+ 2,
562
+ flags,
563
+ attrs,
564
+ locStart,
565
+ );
566
+ method.parseFlags(flags);
567
+ result.push(method);
568
+ if (this.expect(";")) {
569
+ this.next();
570
+ }
571
+ attrs = [];
572
+ } else if (this.token === this.tok.T_STRING) {
573
+ result.push(this.read_variable_list(flags, attrs));
574
+ } else {
575
+ // raise an error
576
+ this.error([this.tok.T_CONST, this.tok.T_FUNCTION, this.tok.T_STRING]);
577
+ this.next();
578
+ }
579
+ }
580
+ if (this.expect("}")) {
581
+ this.next();
582
+ }
583
+ return result;
584
+ },
585
+ /*
586
+ * reading a trait
587
+ * ```ebnf
588
+ * trait ::= T_TRAIT T_STRING (T_EXTENDS (NAMESPACE_NAME ',')* NAMESPACE_NAME)? '{' FUNCTION* '}'
589
+ * ```
590
+ */
591
+ read_trait_declaration_statement() {
592
+ const result = this.node("trait");
593
+ // graceful mode : ignore token & go next
594
+ if (this.token !== this.tok.T_TRAIT) {
595
+ this.error(this.tok.T_TRAIT);
596
+ this.next();
597
+ return null;
598
+ }
599
+ this.next().expect(this.tok.T_STRING);
600
+ let propName = this.node("identifier");
601
+ const name = this.text();
602
+ this.next();
603
+ propName = propName(name);
604
+ this.expect("{");
605
+ const body = this.next().read_class_body(true, false);
606
+ return result(propName, body);
607
+ },
608
+ /*
609
+ * reading a use statement
610
+ * ```ebnf
611
+ * trait_use_statement ::= namespace_name (',' namespace_name)* ('{' trait_use_alias '}')?
612
+ * ```
613
+ */
614
+ read_trait_use_statement() {
615
+ // defines use statements
616
+ const node = this.node("traituse");
617
+ this.expect(this.tok.T_USE) && this.next();
618
+ const traits = [this.read_namespace_name()];
619
+ let adaptations = null;
620
+ while (this.token === ",") {
621
+ traits.push(this.next().read_namespace_name());
622
+ }
623
+ if (this.token === "{") {
624
+ adaptations = [];
625
+ // defines alias statements
626
+ while (this.next().token !== this.EOF) {
627
+ if (this.token === "}") break;
628
+ adaptations.push(this.read_trait_use_alias());
629
+ this.expect(";");
630
+ }
631
+ if (this.expect("}")) {
632
+ this.next();
633
+ }
634
+ } else {
635
+ if (this.expect(";")) {
636
+ this.next();
637
+ }
638
+ }
639
+ return node(traits, adaptations);
640
+ },
641
+ /*
642
+ * Reading trait alias
643
+ * ```ebnf
644
+ * trait_use_alias ::= namespace_name ( T_DOUBLE_COLON T_STRING )? (T_INSTEADOF namespace_name) | (T_AS member_flags? T_STRING)
645
+ * ```
646
+ * name list : https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L303
647
+ * trait adaptation : https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L742
648
+ */
649
+ read_trait_use_alias() {
650
+ const node = this.node();
651
+ let trait = null;
652
+ let method;
653
+
654
+ if (this.is("IDENTIFIER")) {
655
+ method = this.node("identifier");
656
+ const methodName = this.text();
657
+ this.next();
658
+ method = method(methodName);
659
+ } else {
660
+ method = this.read_namespace_name();
661
+
662
+ if (this.token === this.tok.T_DOUBLE_COLON) {
663
+ this.next();
664
+ if (
665
+ this.token === this.tok.T_STRING ||
666
+ (this.version >= 700 && this.is("IDENTIFIER"))
667
+ ) {
668
+ trait = method;
669
+ method = this.node("identifier");
670
+ const methodName = this.text();
671
+ this.next();
672
+ method = method(methodName);
673
+ } else {
674
+ this.expect(this.tok.T_STRING);
675
+ }
676
+ } else {
677
+ // convert identifier as string
678
+ method = method.name;
679
+ }
680
+ }
681
+
682
+ // handle trait precedence
683
+ if (this.token === this.tok.T_INSTEADOF) {
684
+ return node(
685
+ "traitprecedence",
686
+ trait,
687
+ method,
688
+ this.next().read_name_list(),
689
+ );
690
+ } else if (this.token === this.tok.T_AS) {
691
+ // handle trait alias
692
+ let flags = null;
693
+ let alias = null;
694
+ if (this.next().is("T_MEMBER_FLAGS")) {
695
+ flags = this.read_member_flags();
696
+ }
697
+
698
+ if (
699
+ this.token === this.tok.T_STRING ||
700
+ (this.version >= 700 && this.is("IDENTIFIER"))
701
+ ) {
702
+ alias = this.node("identifier");
703
+ const name = this.text();
704
+ this.next();
705
+ alias = alias(name);
706
+ } else if (flags === false) {
707
+ // no visibility flags and no name => too bad
708
+ this.expect(this.tok.T_STRING);
709
+ }
710
+
711
+ return node("traitalias", trait, method, alias, flags);
712
+ }
713
+
714
+ // handle errors
715
+ this.expect([this.tok.T_AS, this.tok.T_INSTEADOF]);
716
+ return node("traitalias", trait, method, null, null);
717
+ },
718
+ };