@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,492 @@
|
|
|
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
|
+
const specialChar = {
|
|
9
|
+
"\\": "\\",
|
|
10
|
+
$: "$",
|
|
11
|
+
n: "\n",
|
|
12
|
+
r: "\r",
|
|
13
|
+
t: "\t",
|
|
14
|
+
f: String.fromCharCode(12),
|
|
15
|
+
v: String.fromCharCode(11),
|
|
16
|
+
e: String.fromCharCode(27),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
/*
|
|
21
|
+
* Unescape special chars
|
|
22
|
+
*/
|
|
23
|
+
resolve_special_chars(text, doubleQuote) {
|
|
24
|
+
if (!doubleQuote) {
|
|
25
|
+
// single quote fix
|
|
26
|
+
return text.replace(/\\\\/g, "\\").replace(/\\'/g, "'");
|
|
27
|
+
}
|
|
28
|
+
return text
|
|
29
|
+
.replace(/\\"/, '"')
|
|
30
|
+
.replace(
|
|
31
|
+
/\\([\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}|u{([0-9a-fA-F]+)})/g,
|
|
32
|
+
($match, p1, p2) => {
|
|
33
|
+
if (specialChar[p1]) {
|
|
34
|
+
return specialChar[p1];
|
|
35
|
+
} else if ("x" === p1[0] || "X" === p1[0]) {
|
|
36
|
+
return String.fromCodePoint(parseInt(p1.substr(1), 16));
|
|
37
|
+
} else if ("u" === p1[0]) {
|
|
38
|
+
return String.fromCodePoint(parseInt(p2, 16));
|
|
39
|
+
} else {
|
|
40
|
+
return String.fromCodePoint(parseInt(p1, 8));
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
);
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
/*
|
|
47
|
+
* Remove all leading spaces each line for heredoc text if there is a indentation
|
|
48
|
+
* @param {string} text
|
|
49
|
+
* @param {number} indentation
|
|
50
|
+
* @param {boolean} indentation_uses_spaces
|
|
51
|
+
* @param {boolean} first_encaps_node if it is behind a variable, the first N spaces should not be removed
|
|
52
|
+
*/
|
|
53
|
+
remove_heredoc_leading_whitespace_chars(
|
|
54
|
+
text,
|
|
55
|
+
indentation,
|
|
56
|
+
indentation_uses_spaces,
|
|
57
|
+
first_encaps_node,
|
|
58
|
+
) {
|
|
59
|
+
if (indentation === 0) {
|
|
60
|
+
return text;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.check_heredoc_indentation_level(
|
|
64
|
+
text,
|
|
65
|
+
indentation,
|
|
66
|
+
indentation_uses_spaces,
|
|
67
|
+
first_encaps_node,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const matchedChar = indentation_uses_spaces ? " " : "\t";
|
|
71
|
+
const removementRegExp = new RegExp(
|
|
72
|
+
`\\n${matchedChar}{${indentation}}`,
|
|
73
|
+
"g",
|
|
74
|
+
);
|
|
75
|
+
const removementFirstEncapsNodeRegExp = new RegExp(
|
|
76
|
+
`^${matchedChar}{${indentation}}`,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
// Rough replace, need more check
|
|
80
|
+
if (first_encaps_node) {
|
|
81
|
+
// Remove text leading whitespace
|
|
82
|
+
text = text.replace(removementFirstEncapsNodeRegExp, "");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Remove leading whitespace after \n
|
|
86
|
+
return text.replace(removementRegExp, "\n");
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
/*
|
|
90
|
+
* Check indentation level of heredoc in text, if mismatch, raiseError
|
|
91
|
+
* @param {string} text
|
|
92
|
+
* @param {number} indentation
|
|
93
|
+
* @param {boolean} indentation_uses_spaces
|
|
94
|
+
* @param {boolean} first_encaps_node if it is behind a variable, the first N spaces should not be removed
|
|
95
|
+
*/
|
|
96
|
+
check_heredoc_indentation_level(
|
|
97
|
+
text,
|
|
98
|
+
indentation,
|
|
99
|
+
indentation_uses_spaces,
|
|
100
|
+
first_encaps_node,
|
|
101
|
+
) {
|
|
102
|
+
const textSize = text.length;
|
|
103
|
+
let offset = 0;
|
|
104
|
+
let leadingWhitespaceCharCount = 0;
|
|
105
|
+
/*
|
|
106
|
+
* @var inCoutingState {boolean} reset to true after a new line
|
|
107
|
+
* @private
|
|
108
|
+
*/
|
|
109
|
+
let inCoutingState = true;
|
|
110
|
+
const chToCheck = indentation_uses_spaces ? " " : "\t";
|
|
111
|
+
let inCheckState = false;
|
|
112
|
+
if (!first_encaps_node) {
|
|
113
|
+
// start from first \n
|
|
114
|
+
offset = text.indexOf("\n");
|
|
115
|
+
// if no \n, just return
|
|
116
|
+
if (offset === -1) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
offset++;
|
|
120
|
+
}
|
|
121
|
+
while (offset < textSize) {
|
|
122
|
+
if (inCoutingState) {
|
|
123
|
+
if (text[offset] === chToCheck) {
|
|
124
|
+
leadingWhitespaceCharCount++;
|
|
125
|
+
} else {
|
|
126
|
+
inCheckState = true;
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
inCoutingState = false;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (
|
|
133
|
+
text[offset] !== "\n" &&
|
|
134
|
+
inCheckState &&
|
|
135
|
+
leadingWhitespaceCharCount < indentation
|
|
136
|
+
) {
|
|
137
|
+
this.raiseError(
|
|
138
|
+
`Invalid body indentation level (expecting an indentation at least ${indentation})`,
|
|
139
|
+
);
|
|
140
|
+
} else {
|
|
141
|
+
inCheckState = false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (text[offset] === "\n") {
|
|
145
|
+
// Reset counting state
|
|
146
|
+
inCoutingState = true;
|
|
147
|
+
leadingWhitespaceCharCount = 0;
|
|
148
|
+
}
|
|
149
|
+
offset++;
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
/*
|
|
154
|
+
* Reads dereferencable scalar
|
|
155
|
+
*/
|
|
156
|
+
read_dereferencable_scalar() {
|
|
157
|
+
let result = null;
|
|
158
|
+
|
|
159
|
+
switch (this.token) {
|
|
160
|
+
case this.tok.T_CONSTANT_ENCAPSED_STRING:
|
|
161
|
+
{
|
|
162
|
+
let value = this.node("string");
|
|
163
|
+
const text = this.text();
|
|
164
|
+
let offset = 0;
|
|
165
|
+
if (text[0] === "b" || text[0] === "B") {
|
|
166
|
+
offset = 1;
|
|
167
|
+
}
|
|
168
|
+
const isDoubleQuote = text[offset] === '"';
|
|
169
|
+
this.next();
|
|
170
|
+
const textValue = this.resolve_special_chars(
|
|
171
|
+
text.substring(offset + 1, text.length - 1),
|
|
172
|
+
isDoubleQuote,
|
|
173
|
+
);
|
|
174
|
+
value = value(
|
|
175
|
+
isDoubleQuote,
|
|
176
|
+
textValue,
|
|
177
|
+
offset === 1, // unicode flag
|
|
178
|
+
text,
|
|
179
|
+
);
|
|
180
|
+
if (this.token === this.tok.T_DOUBLE_COLON) {
|
|
181
|
+
// https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1151
|
|
182
|
+
result = this.read_static_getter(value);
|
|
183
|
+
} else {
|
|
184
|
+
// dirrect string
|
|
185
|
+
result = value;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
break;
|
|
189
|
+
case this.tok.T_ARRAY: // array parser
|
|
190
|
+
result = this.read_array();
|
|
191
|
+
break;
|
|
192
|
+
case "[": // short array format
|
|
193
|
+
result = this.read_array();
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return result;
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
/*
|
|
201
|
+
* ```ebnf
|
|
202
|
+
* scalar ::= T_MAGIC_CONST
|
|
203
|
+
* | T_LNUMBER | T_DNUMBER
|
|
204
|
+
* | T_START_HEREDOC T_ENCAPSED_AND_WHITESPACE? T_END_HEREDOC
|
|
205
|
+
* | '"' encaps_list '"'
|
|
206
|
+
* | T_START_HEREDOC encaps_list T_END_HEREDOC
|
|
207
|
+
* | namespace_name (T_DOUBLE_COLON T_STRING)?
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
read_scalar() {
|
|
211
|
+
if (this.is("T_MAGIC_CONST")) {
|
|
212
|
+
return this.get_magic_constant();
|
|
213
|
+
} else {
|
|
214
|
+
let value, node;
|
|
215
|
+
switch (this.token) {
|
|
216
|
+
// NUMERIC
|
|
217
|
+
case this.tok.T_LNUMBER: // long
|
|
218
|
+
case this.tok.T_DNUMBER: {
|
|
219
|
+
// double
|
|
220
|
+
const result = this.node("number");
|
|
221
|
+
value = this.text();
|
|
222
|
+
this.next();
|
|
223
|
+
return result(value, null);
|
|
224
|
+
}
|
|
225
|
+
case this.tok.T_START_HEREDOC:
|
|
226
|
+
if (this.lexer.curCondition === "ST_NOWDOC") {
|
|
227
|
+
const start = this.lexer.yylloc.first_offset;
|
|
228
|
+
node = this.node("nowdoc");
|
|
229
|
+
value = this.next().text();
|
|
230
|
+
// strip the last line return char
|
|
231
|
+
if (this.lexer.heredoc_label.indentation > 0) {
|
|
232
|
+
value = value.substring(
|
|
233
|
+
0,
|
|
234
|
+
value.length - this.lexer.heredoc_label.indentation,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
const lastCh = value[value.length - 1];
|
|
238
|
+
if (lastCh === "\n") {
|
|
239
|
+
if (value[value.length - 2] === "\r") {
|
|
240
|
+
// windows style
|
|
241
|
+
value = value.substring(0, value.length - 2);
|
|
242
|
+
} else {
|
|
243
|
+
// linux style
|
|
244
|
+
value = value.substring(0, value.length - 1);
|
|
245
|
+
}
|
|
246
|
+
} else if (lastCh === "\r") {
|
|
247
|
+
// mac style
|
|
248
|
+
value = value.substring(0, value.length - 1);
|
|
249
|
+
}
|
|
250
|
+
this.expect(this.tok.T_ENCAPSED_AND_WHITESPACE) && this.next();
|
|
251
|
+
this.expect(this.tok.T_END_HEREDOC) && this.next();
|
|
252
|
+
const raw = this.lexer._input.substring(
|
|
253
|
+
start,
|
|
254
|
+
this.lexer.yylloc.first_offset,
|
|
255
|
+
);
|
|
256
|
+
node = node(
|
|
257
|
+
this.remove_heredoc_leading_whitespace_chars(
|
|
258
|
+
value,
|
|
259
|
+
this.lexer.heredoc_label.indentation,
|
|
260
|
+
this.lexer.heredoc_label.indentation_uses_spaces,
|
|
261
|
+
this.lexer.heredoc_label.first_encaps_node,
|
|
262
|
+
),
|
|
263
|
+
raw,
|
|
264
|
+
this.lexer.heredoc_label.label,
|
|
265
|
+
);
|
|
266
|
+
this.lexer.heredoc_label.finished = true;
|
|
267
|
+
return node;
|
|
268
|
+
} else {
|
|
269
|
+
return this.read_encapsed_string(this.tok.T_END_HEREDOC);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
case '"':
|
|
273
|
+
return this.read_encapsed_string('"');
|
|
274
|
+
|
|
275
|
+
case 'b"':
|
|
276
|
+
case 'B"': {
|
|
277
|
+
return this.read_encapsed_string('"', true);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// TEXTS
|
|
281
|
+
case this.tok.T_CONSTANT_ENCAPSED_STRING:
|
|
282
|
+
case this.tok.T_ARRAY: // array parser
|
|
283
|
+
case "[": // short array format
|
|
284
|
+
return this.read_dereferencable_scalar();
|
|
285
|
+
default: {
|
|
286
|
+
const err = this.error("SCALAR");
|
|
287
|
+
// graceful mode : ignore token & return error node
|
|
288
|
+
this.next();
|
|
289
|
+
return err;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
/*
|
|
295
|
+
* Handles the dereferencing
|
|
296
|
+
*/
|
|
297
|
+
read_dereferencable(expr) {
|
|
298
|
+
let result, offset;
|
|
299
|
+
const node = this.node("offsetlookup");
|
|
300
|
+
if (this.token === "[") {
|
|
301
|
+
offset = this.next().read_expr();
|
|
302
|
+
if (this.expect("]")) this.next();
|
|
303
|
+
result = node(expr, offset);
|
|
304
|
+
} else if (this.token === this.tok.T_DOLLAR_OPEN_CURLY_BRACES) {
|
|
305
|
+
offset = this.read_encapsed_string_item(false);
|
|
306
|
+
result = node(expr, offset);
|
|
307
|
+
}
|
|
308
|
+
return result;
|
|
309
|
+
},
|
|
310
|
+
/*
|
|
311
|
+
* Reads and extracts an encapsed item
|
|
312
|
+
* ```ebnf
|
|
313
|
+
* encapsed_string_item ::= T_ENCAPSED_AND_WHITESPACE
|
|
314
|
+
* | T_DOLLAR_OPEN_CURLY_BRACES expr '}'
|
|
315
|
+
* | T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '}'
|
|
316
|
+
* | T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '[' expr ']' '}'
|
|
317
|
+
* | T_CURLY_OPEN variable '}'
|
|
318
|
+
* | variable
|
|
319
|
+
* | variable '[' expr ']'
|
|
320
|
+
* | variable T_OBJECT_OPERATOR T_STRING
|
|
321
|
+
* ```
|
|
322
|
+
* @return {String|Variable|Expr|Lookup}
|
|
323
|
+
* @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1219
|
|
324
|
+
*/
|
|
325
|
+
read_encapsed_string_item(isDoubleQuote) {
|
|
326
|
+
const encapsedPart = this.node("encapsedpart");
|
|
327
|
+
let syntax = null;
|
|
328
|
+
let curly = false;
|
|
329
|
+
let result = this.node(),
|
|
330
|
+
offset,
|
|
331
|
+
node,
|
|
332
|
+
name;
|
|
333
|
+
|
|
334
|
+
// plain text
|
|
335
|
+
// https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1222
|
|
336
|
+
if (this.token === this.tok.T_ENCAPSED_AND_WHITESPACE) {
|
|
337
|
+
const text = this.text();
|
|
338
|
+
this.next();
|
|
339
|
+
|
|
340
|
+
// if this.lexer.heredoc_label.first_encaps_node -> remove first indents
|
|
341
|
+
result = result(
|
|
342
|
+
"string",
|
|
343
|
+
false,
|
|
344
|
+
this.version >= 703 && !this.lexer.heredoc_label.finished
|
|
345
|
+
? this.remove_heredoc_leading_whitespace_chars(
|
|
346
|
+
this.resolve_special_chars(text, isDoubleQuote),
|
|
347
|
+
this.lexer.heredoc_label.indentation,
|
|
348
|
+
this.lexer.heredoc_label.indentation_uses_spaces,
|
|
349
|
+
this.lexer.heredoc_label.first_encaps_node,
|
|
350
|
+
)
|
|
351
|
+
: text,
|
|
352
|
+
false,
|
|
353
|
+
text,
|
|
354
|
+
);
|
|
355
|
+
} else if (this.token === this.tok.T_DOLLAR_OPEN_CURLY_BRACES) {
|
|
356
|
+
syntax = "simple";
|
|
357
|
+
curly = true;
|
|
358
|
+
// dynamic variable name
|
|
359
|
+
// https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1239
|
|
360
|
+
name = null;
|
|
361
|
+
if (this.next().token === this.tok.T_STRING_VARNAME) {
|
|
362
|
+
name = this.node("variable");
|
|
363
|
+
const varName = this.text();
|
|
364
|
+
this.next();
|
|
365
|
+
// check if lookup an offset
|
|
366
|
+
// https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1243
|
|
367
|
+
result.destroy();
|
|
368
|
+
if (this.token === "[") {
|
|
369
|
+
name = name(varName, false);
|
|
370
|
+
node = this.node("offsetlookup");
|
|
371
|
+
offset = this.next().read_expr();
|
|
372
|
+
this.expect("]") && this.next();
|
|
373
|
+
result = node(name, offset);
|
|
374
|
+
} else {
|
|
375
|
+
result = name(varName, false);
|
|
376
|
+
}
|
|
377
|
+
} else {
|
|
378
|
+
result = result("variable", this.read_expr(), false);
|
|
379
|
+
}
|
|
380
|
+
this.expect("}") && this.next();
|
|
381
|
+
} else if (this.token === this.tok.T_CURLY_OPEN) {
|
|
382
|
+
// expression
|
|
383
|
+
// https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1246
|
|
384
|
+
syntax = "complex";
|
|
385
|
+
result.destroy();
|
|
386
|
+
result = this.next().read_variable(false, false);
|
|
387
|
+
this.expect("}") && this.next();
|
|
388
|
+
} else if (this.token === this.tok.T_VARIABLE) {
|
|
389
|
+
syntax = "simple";
|
|
390
|
+
// plain variable
|
|
391
|
+
// https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1231
|
|
392
|
+
result.destroy();
|
|
393
|
+
result = this.read_simple_variable();
|
|
394
|
+
|
|
395
|
+
// https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1233
|
|
396
|
+
if (this.token === "[") {
|
|
397
|
+
node = this.node("offsetlookup");
|
|
398
|
+
offset = this.next().read_encaps_var_offset();
|
|
399
|
+
this.expect("]") && this.next();
|
|
400
|
+
result = node(result, offset);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1236
|
|
404
|
+
if (this.token === this.tok.T_OBJECT_OPERATOR) {
|
|
405
|
+
node = this.node("propertylookup");
|
|
406
|
+
this.next().expect(this.tok.T_STRING);
|
|
407
|
+
const what = this.node("identifier");
|
|
408
|
+
name = this.text();
|
|
409
|
+
this.next();
|
|
410
|
+
result = node(result, what(name));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// error / fallback
|
|
414
|
+
} else {
|
|
415
|
+
this.expect(this.tok.T_ENCAPSED_AND_WHITESPACE);
|
|
416
|
+
const value = this.text();
|
|
417
|
+
this.next();
|
|
418
|
+
// consider it as string
|
|
419
|
+
result.destroy();
|
|
420
|
+
result = result("string", false, value, false, value);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// reset first_encaps_node to false after access any node
|
|
424
|
+
this.lexer.heredoc_label.first_encaps_node = false;
|
|
425
|
+
return encapsedPart(result, syntax, curly);
|
|
426
|
+
},
|
|
427
|
+
/*
|
|
428
|
+
* Reads an encapsed string
|
|
429
|
+
*/
|
|
430
|
+
read_encapsed_string(expect, isBinary = false) {
|
|
431
|
+
const labelStart = this.lexer.yylloc.first_offset;
|
|
432
|
+
let node = this.node("encapsed");
|
|
433
|
+
this.next();
|
|
434
|
+
const start = this.lexer.yylloc.prev_offset - (isBinary ? 1 : 0);
|
|
435
|
+
const value = [];
|
|
436
|
+
let type = null;
|
|
437
|
+
|
|
438
|
+
if (expect === "`") {
|
|
439
|
+
type = this.ast.encapsed.TYPE_SHELL;
|
|
440
|
+
} else if (expect === '"') {
|
|
441
|
+
type = this.ast.encapsed.TYPE_STRING;
|
|
442
|
+
} else {
|
|
443
|
+
type = this.ast.encapsed.TYPE_HEREDOC;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// reading encapsed parts
|
|
447
|
+
while (this.token !== expect && this.token !== this.EOF) {
|
|
448
|
+
value.push(this.read_encapsed_string_item(true));
|
|
449
|
+
}
|
|
450
|
+
if (
|
|
451
|
+
value.length > 0 &&
|
|
452
|
+
value[value.length - 1].kind === "encapsedpart" &&
|
|
453
|
+
value[value.length - 1].expression.kind === "string"
|
|
454
|
+
) {
|
|
455
|
+
const node = value[value.length - 1].expression;
|
|
456
|
+
const lastCh = node.value[node.value.length - 1];
|
|
457
|
+
if (lastCh === "\n") {
|
|
458
|
+
if (node.value[node.value.length - 2] === "\r") {
|
|
459
|
+
// windows style
|
|
460
|
+
node.value = node.value.substring(0, node.value.length - 2);
|
|
461
|
+
} else {
|
|
462
|
+
// linux style
|
|
463
|
+
node.value = node.value.substring(0, node.value.length - 1);
|
|
464
|
+
}
|
|
465
|
+
} else if (lastCh === "\r") {
|
|
466
|
+
// mac style
|
|
467
|
+
node.value = node.value.substring(0, node.value.length - 1);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
this.expect(expect) && this.next();
|
|
471
|
+
const raw = this.lexer._input.substring(
|
|
472
|
+
type === "heredoc" ? labelStart : start - 1,
|
|
473
|
+
this.lexer.yylloc.first_offset,
|
|
474
|
+
);
|
|
475
|
+
node = node(value, raw, type);
|
|
476
|
+
|
|
477
|
+
if (expect === this.tok.T_END_HEREDOC) {
|
|
478
|
+
node.label = this.lexer.heredoc_label.label;
|
|
479
|
+
this.lexer.heredoc_label.finished = true;
|
|
480
|
+
}
|
|
481
|
+
return node;
|
|
482
|
+
},
|
|
483
|
+
/*
|
|
484
|
+
* Constant token
|
|
485
|
+
*/
|
|
486
|
+
get_magic_constant() {
|
|
487
|
+
const result = this.node("magic");
|
|
488
|
+
const name = this.text();
|
|
489
|
+
this.next();
|
|
490
|
+
return result(name.toUpperCase(), name);
|
|
491
|
+
},
|
|
492
|
+
};
|