@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.
- package/LICENSE +27 -0
- package/README.md +108 -0
- package/dist/@jorgsowa/php-parser.js +11239 -0
- package/dist/@jorgsowa/php-parser.min.js +2 -0
- package/dist/@jorgsowa/php-parser.min.js.LICENSE.txt +10 -0
- package/package.json +86 -0
- package/src/ast/array.js +44 -0
- package/src/ast/arrowfunc.js +43 -0
- package/src/ast/assign.js +28 -0
- package/src/ast/assignref.js +27 -0
- package/src/ast/attrgroup.js +21 -0
- package/src/ast/attribute.js +26 -0
- package/src/ast/bin.js +27 -0
- package/src/ast/block.js +24 -0
- package/src/ast/boolean.js +23 -0
- package/src/ast/break.js +21 -0
- package/src/ast/byref.js +21 -0
- package/src/ast/call.js +26 -0
- package/src/ast/case.js +26 -0
- package/src/ast/cast.js +28 -0
- package/src/ast/catch.js +29 -0
- package/src/ast/class.js +36 -0
- package/src/ast/classconstant.js +71 -0
- package/src/ast/clone.js +21 -0
- package/src/ast/closure.js +47 -0
- package/src/ast/comment.js +23 -0
- package/src/ast/commentblock.js +22 -0
- package/src/ast/commentline.js +22 -0
- package/src/ast/constant.js +26 -0
- package/src/ast/constantstatement.js +24 -0
- package/src/ast/continue.js +24 -0
- package/src/ast/declaration.js +60 -0
- package/src/ast/declare.js +71 -0
- package/src/ast/declaredirective.js +26 -0
- package/src/ast/do.js +26 -0
- package/src/ast/echo.js +26 -0
- package/src/ast/empty.js +23 -0
- package/src/ast/encapsed.js +75 -0
- package/src/ast/encapsedpart.js +28 -0
- package/src/ast/entry.js +30 -0
- package/src/ast/enum.js +30 -0
- package/src/ast/enumcase.js +26 -0
- package/src/ast/error.js +30 -0
- package/src/ast/eval.js +24 -0
- package/src/ast/exit.js +26 -0
- package/src/ast/expression.js +20 -0
- package/src/ast/expressionstatement.js +24 -0
- package/src/ast/for.js +33 -0
- package/src/ast/foreach.js +33 -0
- package/src/ast/function.js +34 -0
- package/src/ast/global.js +24 -0
- package/src/ast/goto.js +22 -0
- package/src/ast/halt.js +22 -0
- package/src/ast/identifier.js +26 -0
- package/src/ast/if.js +30 -0
- package/src/ast/include.js +28 -0
- package/src/ast/inline.js +23 -0
- package/src/ast/interface.js +28 -0
- package/src/ast/intersectiontype.js +24 -0
- package/src/ast/isset.js +23 -0
- package/src/ast/label.js +21 -0
- package/src/ast/list.js +26 -0
- package/src/ast/literal.js +28 -0
- package/src/ast/location.js +22 -0
- package/src/ast/lookup.js +26 -0
- package/src/ast/magic.js +22 -0
- package/src/ast/match.js +26 -0
- package/src/ast/matcharm.js +26 -0
- package/src/ast/method.js +24 -0
- package/src/ast/name.js +55 -0
- package/src/ast/namedargument.js +27 -0
- package/src/ast/namespace.js +26 -0
- package/src/ast/new.js +26 -0
- package/src/ast/node.js +111 -0
- package/src/ast/noop.js +20 -0
- package/src/ast/nowdoc.js +26 -0
- package/src/ast/nullkeyword.js +20 -0
- package/src/ast/nullsafepropertylookup.js +22 -0
- package/src/ast/number.js +23 -0
- package/src/ast/offsetlookup.js +22 -0
- package/src/ast/operation.js +19 -0
- package/src/ast/parameter.js +61 -0
- package/src/ast/parentreference.js +24 -0
- package/src/ast/position.js +22 -0
- package/src/ast/post.js +26 -0
- package/src/ast/pre.js +26 -0
- package/src/ast/print.js +23 -0
- package/src/ast/program.js +32 -0
- package/src/ast/property.js +46 -0
- package/src/ast/propertyhook.js +33 -0
- package/src/ast/propertylookup.js +22 -0
- package/src/ast/propertystatement.js +59 -0
- package/src/ast/reference.js +21 -0
- package/src/ast/retif.js +28 -0
- package/src/ast/return.js +21 -0
- package/src/ast/selfreference.js +24 -0
- package/src/ast/silent.js +24 -0
- package/src/ast/statement.js +19 -0
- package/src/ast/static.js +24 -0
- package/src/ast/staticlookup.js +22 -0
- package/src/ast/staticreference.js +24 -0
- package/src/ast/staticvariable.js +26 -0
- package/src/ast/string.js +28 -0
- package/src/ast/switch.js +28 -0
- package/src/ast/throw.js +21 -0
- package/src/ast/trait.js +24 -0
- package/src/ast/traitalias.js +44 -0
- package/src/ast/traitprecedence.js +28 -0
- package/src/ast/traituse.js +26 -0
- package/src/ast/try.js +28 -0
- package/src/ast/typereference.js +40 -0
- package/src/ast/unary.js +26 -0
- package/src/ast/uniontype.js +24 -0
- package/src/ast/unset.js +23 -0
- package/src/ast/usegroup.js +30 -0
- package/src/ast/useitem.js +45 -0
- package/src/ast/variable.js +36 -0
- package/src/ast/variadic.js +25 -0
- package/src/ast/variadicplaceholder.js +24 -0
- package/src/ast/while.js +28 -0
- package/src/ast/yield.js +27 -0
- package/src/ast/yieldfrom.js +25 -0
- package/src/ast.js +593 -0
- package/src/index.js +239 -0
- package/src/lexer/attribute.js +85 -0
- package/src/lexer/comments.js +63 -0
- package/src/lexer/initial.js +64 -0
- package/src/lexer/numbers.js +171 -0
- package/src/lexer/property.js +96 -0
- package/src/lexer/scripting.js +114 -0
- package/src/lexer/strings.js +524 -0
- package/src/lexer/tokens.js +356 -0
- package/src/lexer/utils.js +112 -0
- package/src/lexer.js +561 -0
- package/src/parser/array.js +113 -0
- package/src/parser/class.js +718 -0
- package/src/parser/comment.js +52 -0
- package/src/parser/enum.js +56 -0
- package/src/parser/expr.js +848 -0
- package/src/parser/function.js +507 -0
- package/src/parser/if.js +94 -0
- package/src/parser/loops.js +168 -0
- package/src/parser/main.js +21 -0
- package/src/parser/namespace.js +231 -0
- package/src/parser/scalar.js +492 -0
- package/src/parser/statement.js +444 -0
- package/src/parser/switch.js +99 -0
- package/src/parser/try.js +43 -0
- package/src/parser/utils.js +203 -0
- package/src/parser/variable.js +363 -0
- package/src/parser.js +748 -0
- package/src/tokens.js +177 -0
- package/types.d.ts +1457 -0
|
@@ -0,0 +1,507 @@
|
|
|
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
|
+
* checks if current token is a reference keyword
|
|
11
|
+
*/
|
|
12
|
+
is_reference() {
|
|
13
|
+
if (this.token === "&") {
|
|
14
|
+
this.next();
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
},
|
|
19
|
+
/*
|
|
20
|
+
* checks if current token is a variadic keyword
|
|
21
|
+
*/
|
|
22
|
+
is_variadic() {
|
|
23
|
+
if (this.token === this.tok.T_ELLIPSIS) {
|
|
24
|
+
this.next();
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
},
|
|
29
|
+
/*
|
|
30
|
+
* reading a function
|
|
31
|
+
* ```ebnf
|
|
32
|
+
* function ::= function_declaration code_block
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
read_function(closure, flag, attrs, locStart) {
|
|
36
|
+
const result = this.read_function_declaration(
|
|
37
|
+
closure ? 1 : flag ? 2 : 0,
|
|
38
|
+
flag && flag[1] === 1,
|
|
39
|
+
attrs || [],
|
|
40
|
+
locStart,
|
|
41
|
+
);
|
|
42
|
+
if (flag && flag[2] == 1) {
|
|
43
|
+
// abstract function :
|
|
44
|
+
result.parseFlags(flag);
|
|
45
|
+
if (this.expect(";")) {
|
|
46
|
+
this.next();
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
if (this.expect("{")) {
|
|
50
|
+
result.body = this.read_code_block(false);
|
|
51
|
+
if (result.loc && result.body.loc) {
|
|
52
|
+
result.loc.end = result.body.loc.end;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (!closure && flag) {
|
|
56
|
+
result.parseFlags(flag);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
},
|
|
61
|
+
/*
|
|
62
|
+
* reads a function declaration (without his body)
|
|
63
|
+
* ```ebnf
|
|
64
|
+
* function_declaration ::= T_FUNCTION '&'? T_STRING '(' parameter_list ')'
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
read_function_declaration(type, isStatic, attrs, locStart) {
|
|
68
|
+
let nodeName = "function";
|
|
69
|
+
if (type === 1) {
|
|
70
|
+
nodeName = "closure";
|
|
71
|
+
} else if (type === 2) {
|
|
72
|
+
nodeName = "method";
|
|
73
|
+
}
|
|
74
|
+
const result = this.node(nodeName);
|
|
75
|
+
|
|
76
|
+
if (this.expect(this.tok.T_FUNCTION)) {
|
|
77
|
+
this.next();
|
|
78
|
+
}
|
|
79
|
+
const isRef = this.is_reference();
|
|
80
|
+
let name = false,
|
|
81
|
+
use = [],
|
|
82
|
+
returnType = null,
|
|
83
|
+
nullable = false;
|
|
84
|
+
if (type !== 1) {
|
|
85
|
+
const nameNode = this.node("identifier");
|
|
86
|
+
if (type === 2) {
|
|
87
|
+
if (this.version >= 700) {
|
|
88
|
+
if (this.token === this.tok.T_STRING || this.is("IDENTIFIER")) {
|
|
89
|
+
name = this.text();
|
|
90
|
+
this.next();
|
|
91
|
+
} else if (this.version < 704) {
|
|
92
|
+
this.error("IDENTIFIER");
|
|
93
|
+
}
|
|
94
|
+
} else if (this.token === this.tok.T_STRING) {
|
|
95
|
+
name = this.text();
|
|
96
|
+
this.next();
|
|
97
|
+
} else {
|
|
98
|
+
this.error("IDENTIFIER");
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
if (this.version >= 700) {
|
|
102
|
+
if (this.token === this.tok.T_STRING) {
|
|
103
|
+
name = this.text();
|
|
104
|
+
this.next();
|
|
105
|
+
} else if (this.version >= 704) {
|
|
106
|
+
if (!this.expect("(")) {
|
|
107
|
+
this.next();
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
this.error(this.tok.T_STRING);
|
|
111
|
+
this.next();
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
if (this.expect(this.tok.T_STRING)) {
|
|
115
|
+
name = this.text();
|
|
116
|
+
}
|
|
117
|
+
this.next();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
name = nameNode(name);
|
|
121
|
+
}
|
|
122
|
+
if (this.expect("(")) this.next();
|
|
123
|
+
const params = this.read_parameter_list(name.name === "__construct");
|
|
124
|
+
if (this.expect(")")) this.next();
|
|
125
|
+
if (type === 1) {
|
|
126
|
+
use = this.read_lexical_vars();
|
|
127
|
+
}
|
|
128
|
+
if (this.token === ":") {
|
|
129
|
+
if (this.next().token === "?") {
|
|
130
|
+
nullable = true;
|
|
131
|
+
this.next();
|
|
132
|
+
}
|
|
133
|
+
returnType = this.read_types();
|
|
134
|
+
}
|
|
135
|
+
const apply_attrgroup_location = (node) => {
|
|
136
|
+
node.attrGroups = attrs || [];
|
|
137
|
+
|
|
138
|
+
if (locStart && node.loc) {
|
|
139
|
+
node.loc.start = locStart;
|
|
140
|
+
if (node.loc.source) {
|
|
141
|
+
node.loc.source = this.lexer._input.substr(
|
|
142
|
+
node.loc.start.offset,
|
|
143
|
+
node.loc.end.offset - node.loc.start.offset,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return node;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
if (type === 1) {
|
|
151
|
+
// closure
|
|
152
|
+
return apply_attrgroup_location(
|
|
153
|
+
result(params, isRef, use, returnType, nullable, isStatic),
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
return apply_attrgroup_location(
|
|
157
|
+
result(name, params, isRef, returnType, nullable),
|
|
158
|
+
);
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
read_lexical_vars() {
|
|
162
|
+
let result = [];
|
|
163
|
+
|
|
164
|
+
if (this.token === this.tok.T_USE) {
|
|
165
|
+
this.next();
|
|
166
|
+
this.expect("(") && this.next();
|
|
167
|
+
result = this.read_lexical_var_list();
|
|
168
|
+
this.expect(")") && this.next();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return result;
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
read_list_with_dangling_comma(item) {
|
|
175
|
+
const result = [];
|
|
176
|
+
|
|
177
|
+
while (this.token != this.EOF) {
|
|
178
|
+
result.push(item());
|
|
179
|
+
if (this.token == ",") {
|
|
180
|
+
this.next();
|
|
181
|
+
if (this.version >= 800 && this.token === ")") {
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
} else if (this.token == ")") {
|
|
185
|
+
break;
|
|
186
|
+
} else {
|
|
187
|
+
this.error([",", ")"]);
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return result;
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
read_lexical_var_list() {
|
|
195
|
+
return this.read_list_with_dangling_comma(this.read_lexical_var.bind(this));
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
/*
|
|
199
|
+
* ```ebnf
|
|
200
|
+
* lexical_var ::= '&'? T_VARIABLE
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
read_lexical_var() {
|
|
204
|
+
if (this.token === "&") {
|
|
205
|
+
return this.read_byref(this.read_lexical_var.bind(this));
|
|
206
|
+
}
|
|
207
|
+
const result = this.node("variable");
|
|
208
|
+
this.expect(this.tok.T_VARIABLE);
|
|
209
|
+
const name = this.text().substring(1);
|
|
210
|
+
this.next();
|
|
211
|
+
return result(name, false);
|
|
212
|
+
},
|
|
213
|
+
/*
|
|
214
|
+
* reads a list of parameters
|
|
215
|
+
* ```ebnf
|
|
216
|
+
* parameter_list ::= (parameter ',')* parameter?
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
219
|
+
read_parameter_list(is_class_constructor) {
|
|
220
|
+
if (this.token !== ")") {
|
|
221
|
+
let wasVariadic = false;
|
|
222
|
+
|
|
223
|
+
return this.read_list_with_dangling_comma(
|
|
224
|
+
function () {
|
|
225
|
+
const parameter = this.read_parameter(is_class_constructor);
|
|
226
|
+
if (parameter) {
|
|
227
|
+
// variadic parameters can only be defined at the end of the parameter list
|
|
228
|
+
if (wasVariadic) {
|
|
229
|
+
this.raiseError(
|
|
230
|
+
"Unexpected parameter after a variadic parameter",
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
if (parameter.variadic) {
|
|
234
|
+
wasVariadic = true;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return parameter;
|
|
238
|
+
}.bind(this),
|
|
239
|
+
",",
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return [];
|
|
244
|
+
},
|
|
245
|
+
/*
|
|
246
|
+
* ```ebnf
|
|
247
|
+
* parameter ::= type? '&'? T_ELLIPSIS? T_VARIABLE ('=' expr)?
|
|
248
|
+
* ```
|
|
249
|
+
* @see https://github.com/php/php-src/blob/493524454d66adde84e00d249d607ecd540de99f/Zend/zend_language_parser.y#L640
|
|
250
|
+
*/
|
|
251
|
+
read_parameter(is_class_constructor) {
|
|
252
|
+
const node = this.node("parameter");
|
|
253
|
+
let parameterName = null;
|
|
254
|
+
let value = null;
|
|
255
|
+
let types = null;
|
|
256
|
+
let nullable = false;
|
|
257
|
+
let readonly = false;
|
|
258
|
+
let attrs = [];
|
|
259
|
+
if (this.token === this.tok.T_ATTRIBUTE) attrs = this.read_attr_list();
|
|
260
|
+
|
|
261
|
+
if (this.version >= 801 && this.token === this.tok.T_READ_ONLY) {
|
|
262
|
+
if (is_class_constructor) {
|
|
263
|
+
this.next();
|
|
264
|
+
readonly = true;
|
|
265
|
+
} else {
|
|
266
|
+
this.raiseError(
|
|
267
|
+
"readonly properties can be used only on class constructor",
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const flags = this.read_promoted();
|
|
273
|
+
|
|
274
|
+
if (
|
|
275
|
+
!readonly &&
|
|
276
|
+
this.version >= 801 &&
|
|
277
|
+
this.token === this.tok.T_READ_ONLY
|
|
278
|
+
) {
|
|
279
|
+
if (is_class_constructor) {
|
|
280
|
+
this.next();
|
|
281
|
+
readonly = true;
|
|
282
|
+
} else {
|
|
283
|
+
this.raiseError(
|
|
284
|
+
"readonly properties can be used only on class constructor",
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (this.token === "?") {
|
|
290
|
+
this.next();
|
|
291
|
+
nullable = true;
|
|
292
|
+
}
|
|
293
|
+
types = this.read_types();
|
|
294
|
+
if (nullable && !types) {
|
|
295
|
+
this.raiseError(
|
|
296
|
+
"Expecting a type definition combined with nullable operator",
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
const isRef = this.is_reference();
|
|
300
|
+
const isVariadic = this.is_variadic();
|
|
301
|
+
if (this.expect(this.tok.T_VARIABLE)) {
|
|
302
|
+
parameterName = this.node("identifier");
|
|
303
|
+
const name = this.text().substring(1);
|
|
304
|
+
this.next();
|
|
305
|
+
parameterName = parameterName(name);
|
|
306
|
+
}
|
|
307
|
+
if (this.token == "=") {
|
|
308
|
+
value = this.next().read_expr();
|
|
309
|
+
}
|
|
310
|
+
const result = node(
|
|
311
|
+
parameterName,
|
|
312
|
+
types,
|
|
313
|
+
value,
|
|
314
|
+
isRef,
|
|
315
|
+
isVariadic,
|
|
316
|
+
readonly,
|
|
317
|
+
nullable,
|
|
318
|
+
flags,
|
|
319
|
+
);
|
|
320
|
+
if (attrs) result.attrGroups = attrs;
|
|
321
|
+
return result;
|
|
322
|
+
},
|
|
323
|
+
read_types() {
|
|
324
|
+
const MODE_UNSET = "unset";
|
|
325
|
+
const MODE_UNION = "union";
|
|
326
|
+
const MODE_INTERSECTION = "intersection";
|
|
327
|
+
|
|
328
|
+
const types = [];
|
|
329
|
+
let mode = MODE_UNSET;
|
|
330
|
+
const type = this.read_type();
|
|
331
|
+
if (!type) return null;
|
|
332
|
+
|
|
333
|
+
// we have matched a single type
|
|
334
|
+
types.push(type);
|
|
335
|
+
|
|
336
|
+
// is the current token a:
|
|
337
|
+
// - | for union type
|
|
338
|
+
// - & for intersection type (> php 8.1)
|
|
339
|
+
while (this.token === "|" || (this.version >= 801 && this.token === "&")) {
|
|
340
|
+
const nextToken = this.peek();
|
|
341
|
+
|
|
342
|
+
if (
|
|
343
|
+
nextToken === this.tok.T_ELLIPSIS ||
|
|
344
|
+
nextToken === this.tok.T_VARIABLE
|
|
345
|
+
) {
|
|
346
|
+
// the next token is part of the variable (or the variable itself),
|
|
347
|
+
// we're not gonna match anymore types
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (mode === MODE_UNSET) {
|
|
352
|
+
// are we in union or intersection "mode"
|
|
353
|
+
mode = this.token === "|" ? MODE_UNION : MODE_INTERSECTION;
|
|
354
|
+
} else {
|
|
355
|
+
// it is not possible to mix "modes"
|
|
356
|
+
if (
|
|
357
|
+
(mode === MODE_UNION && this.token !== "|") ||
|
|
358
|
+
(mode === MODE_INTERSECTION && this.token !== "&")
|
|
359
|
+
) {
|
|
360
|
+
this.raiseError(
|
|
361
|
+
'Unexpect token "' + this.token + '", "|" and "&" can not be mixed',
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
this.next();
|
|
367
|
+
types.push(this.read_type());
|
|
368
|
+
}
|
|
369
|
+
if (types.length === 1) {
|
|
370
|
+
return types[0];
|
|
371
|
+
} else {
|
|
372
|
+
return mode === MODE_INTERSECTION
|
|
373
|
+
? this.node("intersectiontype")(types)
|
|
374
|
+
: this.node("uniontype")(types);
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
read_promoted() {
|
|
378
|
+
const MODIFIER_PUBLIC = 1;
|
|
379
|
+
const MODIFIER_PROTECTED = 2;
|
|
380
|
+
const MODIFIER_PRIVATE = 4;
|
|
381
|
+
if (this.token === this.tok.T_PUBLIC) {
|
|
382
|
+
this.next();
|
|
383
|
+
return MODIFIER_PUBLIC;
|
|
384
|
+
} else if (this.token === this.tok.T_PROTECTED) {
|
|
385
|
+
this.next();
|
|
386
|
+
return MODIFIER_PROTECTED;
|
|
387
|
+
} else if (this.token === this.tok.T_PRIVATE) {
|
|
388
|
+
this.next();
|
|
389
|
+
return MODIFIER_PRIVATE;
|
|
390
|
+
}
|
|
391
|
+
return 0;
|
|
392
|
+
},
|
|
393
|
+
/*
|
|
394
|
+
* Reads a list of arguments
|
|
395
|
+
* ```ebnf
|
|
396
|
+
* function_argument_list ::= '(' (argument_list (',' argument_list)*)? ')'
|
|
397
|
+
* ```
|
|
398
|
+
*/
|
|
399
|
+
read_argument_list() {
|
|
400
|
+
let result = [];
|
|
401
|
+
this.expect("(") && this.next();
|
|
402
|
+
if (
|
|
403
|
+
this.version >= 801 &&
|
|
404
|
+
this.token === this.tok.T_ELLIPSIS &&
|
|
405
|
+
this.peek() === ")"
|
|
406
|
+
) {
|
|
407
|
+
result.push(this.node("variadicplaceholder")());
|
|
408
|
+
this.next();
|
|
409
|
+
} else if (this.token !== ")") {
|
|
410
|
+
result = this.read_non_empty_argument_list();
|
|
411
|
+
}
|
|
412
|
+
this.expect(")") && this.next();
|
|
413
|
+
return result;
|
|
414
|
+
},
|
|
415
|
+
/*
|
|
416
|
+
* Reads non empty argument list
|
|
417
|
+
*/
|
|
418
|
+
read_non_empty_argument_list() {
|
|
419
|
+
let wasVariadic = false;
|
|
420
|
+
|
|
421
|
+
return this.read_function_list(
|
|
422
|
+
function () {
|
|
423
|
+
const argument = this.read_argument();
|
|
424
|
+
if (argument) {
|
|
425
|
+
const isVariadic = argument.kind === "variadic";
|
|
426
|
+
// variadic arguments can only be followed by other variadic arguments
|
|
427
|
+
if (wasVariadic && !isVariadic) {
|
|
428
|
+
this.raiseError(
|
|
429
|
+
"Unexpected non-variadic argument after a variadic argument",
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
if (isVariadic) {
|
|
433
|
+
wasVariadic = true;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return argument;
|
|
437
|
+
}.bind(this),
|
|
438
|
+
",",
|
|
439
|
+
);
|
|
440
|
+
},
|
|
441
|
+
/*
|
|
442
|
+
* ```ebnf
|
|
443
|
+
* argument_list ::= T_STRING ':' expr | T_ELLIPSIS? expr
|
|
444
|
+
* ```
|
|
445
|
+
*/
|
|
446
|
+
read_argument() {
|
|
447
|
+
if (this.token === this.tok.T_ELLIPSIS) {
|
|
448
|
+
return this.node("variadic")(this.next().read_expr());
|
|
449
|
+
}
|
|
450
|
+
if (
|
|
451
|
+
this.token === this.tok.T_STRING ||
|
|
452
|
+
Object.values(this.lexer.keywords).includes(this.token)
|
|
453
|
+
) {
|
|
454
|
+
const nextToken = this.peek();
|
|
455
|
+
if (nextToken === ":") {
|
|
456
|
+
if (this.version < 800) {
|
|
457
|
+
this.raiseError("PHP 8+ is required to use named arguments");
|
|
458
|
+
}
|
|
459
|
+
return this.node("namedargument")(
|
|
460
|
+
this.text(),
|
|
461
|
+
this.next().next().read_expr(),
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return this.read_expr();
|
|
466
|
+
},
|
|
467
|
+
/*
|
|
468
|
+
* read type hinting
|
|
469
|
+
* ```ebnf
|
|
470
|
+
* type ::= T_ARRAY | T_CALLABLE | namespace_name
|
|
471
|
+
* ```
|
|
472
|
+
*/
|
|
473
|
+
read_type() {
|
|
474
|
+
const result = this.node();
|
|
475
|
+
if (this.token === this.tok.T_ARRAY || this.token === this.tok.T_CALLABLE) {
|
|
476
|
+
const type = this.text();
|
|
477
|
+
this.next();
|
|
478
|
+
return result("typereference", type.toLowerCase(), type);
|
|
479
|
+
} else if (
|
|
480
|
+
this.token === this.tok.T_NAME_RELATIVE ||
|
|
481
|
+
this.token === this.tok.T_NAME_QUALIFIED ||
|
|
482
|
+
this.token === this.tok.T_NAME_FULLY_QUALIFIED ||
|
|
483
|
+
this.token === this.tok.T_STRING ||
|
|
484
|
+
this.token === this.tok.T_STATIC
|
|
485
|
+
) {
|
|
486
|
+
const type = this.text();
|
|
487
|
+
const backup = [this.token, this.lexer.getState()];
|
|
488
|
+
this.next();
|
|
489
|
+
if (
|
|
490
|
+
this.token !== this.tok.T_NS_SEPARATOR &&
|
|
491
|
+
this.ast.typereference.types.indexOf(type.toLowerCase()) > -1
|
|
492
|
+
) {
|
|
493
|
+
return result("typereference", type.toLowerCase(), type);
|
|
494
|
+
} else {
|
|
495
|
+
// rollback a classic namespace
|
|
496
|
+
this.lexer.tokens.push(backup);
|
|
497
|
+
this.next();
|
|
498
|
+
// fix : destroy not consumed node (release comments)
|
|
499
|
+
result.destroy();
|
|
500
|
+
return this.read_namespace_name();
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
// fix : destroy not consumed node (release comments)
|
|
504
|
+
result.destroy();
|
|
505
|
+
return null;
|
|
506
|
+
},
|
|
507
|
+
};
|
package/src/parser/if.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
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
|
+
* Reads an IF statement
|
|
11
|
+
*
|
|
12
|
+
* ```ebnf
|
|
13
|
+
* if ::= T_IF '(' expr ')' ':' ...
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
read_if() {
|
|
17
|
+
const result = this.node("if");
|
|
18
|
+
const test = this.next().read_if_expr();
|
|
19
|
+
let body = null;
|
|
20
|
+
let alternate = null;
|
|
21
|
+
let shortForm = false;
|
|
22
|
+
|
|
23
|
+
if (this.token === ":") {
|
|
24
|
+
shortForm = true;
|
|
25
|
+
this.next();
|
|
26
|
+
body = this.node("block");
|
|
27
|
+
const items = [];
|
|
28
|
+
while (this.token !== this.EOF && this.token !== this.tok.T_ENDIF) {
|
|
29
|
+
if (this.token === this.tok.T_ELSEIF) {
|
|
30
|
+
alternate = this.read_elseif_short();
|
|
31
|
+
break;
|
|
32
|
+
} else if (this.token === this.tok.T_ELSE) {
|
|
33
|
+
alternate = this.read_else_short();
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
items.push(this.read_inner_statement());
|
|
37
|
+
}
|
|
38
|
+
body = body(null, items);
|
|
39
|
+
this.expect(this.tok.T_ENDIF) && this.next();
|
|
40
|
+
this.expectEndOfStatement();
|
|
41
|
+
} else {
|
|
42
|
+
body = this.read_statement();
|
|
43
|
+
if (this.token === this.tok.T_ELSEIF) {
|
|
44
|
+
alternate = this.read_if();
|
|
45
|
+
} else if (this.token === this.tok.T_ELSE) {
|
|
46
|
+
alternate = this.next().read_statement();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return result(test, body, alternate, shortForm);
|
|
50
|
+
},
|
|
51
|
+
/*
|
|
52
|
+
* reads an if expression : '(' expr ')'
|
|
53
|
+
*/
|
|
54
|
+
read_if_expr() {
|
|
55
|
+
this.expect("(") && this.next();
|
|
56
|
+
const result = this.read_expr();
|
|
57
|
+
this.expect(")") && this.next();
|
|
58
|
+
return result;
|
|
59
|
+
},
|
|
60
|
+
/*
|
|
61
|
+
* reads an elseif (expr): statements
|
|
62
|
+
*/
|
|
63
|
+
read_elseif_short() {
|
|
64
|
+
let alternate = null;
|
|
65
|
+
const result = this.node("if");
|
|
66
|
+
const test = this.next().read_if_expr();
|
|
67
|
+
if (this.expect(":")) this.next();
|
|
68
|
+
const body = this.node("block");
|
|
69
|
+
const items = [];
|
|
70
|
+
while (this.token != this.EOF && this.token !== this.tok.T_ENDIF) {
|
|
71
|
+
if (this.token === this.tok.T_ELSEIF) {
|
|
72
|
+
alternate = this.read_elseif_short();
|
|
73
|
+
break;
|
|
74
|
+
} else if (this.token === this.tok.T_ELSE) {
|
|
75
|
+
alternate = this.read_else_short();
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
items.push(this.read_inner_statement());
|
|
79
|
+
}
|
|
80
|
+
return result(test, body(null, items), alternate, true);
|
|
81
|
+
},
|
|
82
|
+
/*
|
|
83
|
+
*
|
|
84
|
+
*/
|
|
85
|
+
read_else_short() {
|
|
86
|
+
if (this.next().expect(":")) this.next();
|
|
87
|
+
const body = this.node("block");
|
|
88
|
+
const items = [];
|
|
89
|
+
while (this.token != this.EOF && this.token !== this.tok.T_ENDIF) {
|
|
90
|
+
items.push(this.read_inner_statement());
|
|
91
|
+
}
|
|
92
|
+
return body(null, items);
|
|
93
|
+
},
|
|
94
|
+
};
|