@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,203 @@
|
|
|
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 a short form of tokens
|
|
11
|
+
* @param {Number} token - The ending token
|
|
12
|
+
* @return {Block}
|
|
13
|
+
*/
|
|
14
|
+
read_short_form(token) {
|
|
15
|
+
const body = this.node("block");
|
|
16
|
+
const items = [];
|
|
17
|
+
/* istanbul ignore next */
|
|
18
|
+
if (this.expect(":")) this.next();
|
|
19
|
+
while (this.token != this.EOF && this.token !== token) {
|
|
20
|
+
items.push(this.read_inner_statement());
|
|
21
|
+
}
|
|
22
|
+
if (
|
|
23
|
+
items.length === 0 &&
|
|
24
|
+
this.extractDoc &&
|
|
25
|
+
this._docs.length > this._docIndex
|
|
26
|
+
) {
|
|
27
|
+
items.push(this.node("noop")());
|
|
28
|
+
}
|
|
29
|
+
/* istanbul ignore next */
|
|
30
|
+
if (this.expect(token)) this.next();
|
|
31
|
+
this.expectEndOfStatement();
|
|
32
|
+
return body(null, items);
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
/*
|
|
36
|
+
* https://wiki.php.net/rfc/trailing-comma-function-calls
|
|
37
|
+
* @param {*} item
|
|
38
|
+
* @param {*} separator
|
|
39
|
+
*/
|
|
40
|
+
read_function_list(item, separator) {
|
|
41
|
+
const result = [];
|
|
42
|
+
do {
|
|
43
|
+
if (this.token == separator && this.version >= 703 && result.length > 0) {
|
|
44
|
+
result.push(this.node("noop")());
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
result.push(item.apply(this, []));
|
|
48
|
+
if (this.token != separator) {
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
if (this.next().token == ")" && this.version >= 703) {
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
} while (this.token != this.EOF);
|
|
55
|
+
return result;
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
/*
|
|
59
|
+
* Helper : reads a list of tokens / sample : T_STRING ',' T_STRING ...
|
|
60
|
+
* ```ebnf
|
|
61
|
+
* list ::= separator? ( item separator )* item
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
read_list(item, separator, preserveFirstSeparator) {
|
|
65
|
+
const result = [];
|
|
66
|
+
|
|
67
|
+
if (this.token == separator) {
|
|
68
|
+
if (preserveFirstSeparator) {
|
|
69
|
+
result.push(typeof item === "function" ? this.node("noop")() : null);
|
|
70
|
+
}
|
|
71
|
+
this.next();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (typeof item === "function") {
|
|
75
|
+
do {
|
|
76
|
+
const itemResult = item.apply(this, []);
|
|
77
|
+
if (itemResult) {
|
|
78
|
+
result.push(itemResult);
|
|
79
|
+
}
|
|
80
|
+
if (this.token != separator) {
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
} while (this.next().token != this.EOF);
|
|
84
|
+
} else {
|
|
85
|
+
if (this.expect(item)) {
|
|
86
|
+
result.push(this.text());
|
|
87
|
+
} else {
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
while (this.next().token != this.EOF) {
|
|
91
|
+
if (this.token != separator) break;
|
|
92
|
+
// trim current separator & check item
|
|
93
|
+
if (this.next().token != item) break;
|
|
94
|
+
result.push(this.text());
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
/*
|
|
101
|
+
* Reads a list of names separated by a comma
|
|
102
|
+
*
|
|
103
|
+
* ```ebnf
|
|
104
|
+
* name_list ::= namespace (',' namespace)*
|
|
105
|
+
* ```
|
|
106
|
+
*
|
|
107
|
+
* Sample code :
|
|
108
|
+
* ```php
|
|
109
|
+
* <?php class foo extends bar, baz { }
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L726
|
|
113
|
+
* @return {Reference[]}
|
|
114
|
+
*/
|
|
115
|
+
read_name_list() {
|
|
116
|
+
return this.read_list(this.read_namespace_name, ",", false);
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
/*
|
|
120
|
+
* Reads the byref token and assign it to the specified node
|
|
121
|
+
* @param {*} cb
|
|
122
|
+
*/
|
|
123
|
+
read_byref(cb) {
|
|
124
|
+
let byref = this.node("byref");
|
|
125
|
+
this.next();
|
|
126
|
+
byref = byref(null);
|
|
127
|
+
const result = cb();
|
|
128
|
+
if (result) {
|
|
129
|
+
this.ast.swapLocations(result, byref, result, this);
|
|
130
|
+
result.byref = true;
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
/*
|
|
136
|
+
* Reads a list of variables declarations
|
|
137
|
+
*
|
|
138
|
+
* ```ebnf
|
|
139
|
+
* variable_declaration ::= T_VARIABLE ('=' expr)?*
|
|
140
|
+
* variable_declarations ::= variable_declaration (',' variable_declaration)*
|
|
141
|
+
* ```
|
|
142
|
+
*
|
|
143
|
+
* Sample code :
|
|
144
|
+
* ```php
|
|
145
|
+
* <?php static $a = 'hello', $b = 'world';
|
|
146
|
+
* ```
|
|
147
|
+
* @return {StaticVariable[]} Returns an array composed by a list of variables, or
|
|
148
|
+
* assign values
|
|
149
|
+
*/
|
|
150
|
+
read_variable_declarations() {
|
|
151
|
+
return this.read_list(function () {
|
|
152
|
+
const node = this.node("staticvariable");
|
|
153
|
+
let variable = this.node("variable");
|
|
154
|
+
// plain variable name
|
|
155
|
+
/* istanbul ignore else */
|
|
156
|
+
if (this.expect(this.tok.T_VARIABLE)) {
|
|
157
|
+
const name = this.text().substring(1);
|
|
158
|
+
this.next();
|
|
159
|
+
variable = variable(name, false);
|
|
160
|
+
} else {
|
|
161
|
+
variable = variable("#ERR", false);
|
|
162
|
+
}
|
|
163
|
+
if (this.token === "=") {
|
|
164
|
+
return node(variable, this.next().read_expr());
|
|
165
|
+
} else {
|
|
166
|
+
return variable;
|
|
167
|
+
}
|
|
168
|
+
}, ",");
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
/*
|
|
172
|
+
* Reads class extends
|
|
173
|
+
*/
|
|
174
|
+
read_extends_from() {
|
|
175
|
+
if (this.token === this.tok.T_EXTENDS) {
|
|
176
|
+
return this.next().read_namespace_name();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return null;
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
/*
|
|
183
|
+
* Reads interface extends list
|
|
184
|
+
*/
|
|
185
|
+
read_interface_extends_list() {
|
|
186
|
+
if (this.token === this.tok.T_EXTENDS) {
|
|
187
|
+
return this.next().read_name_list();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return null;
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
/*
|
|
194
|
+
* Reads implements list
|
|
195
|
+
*/
|
|
196
|
+
read_implements_list() {
|
|
197
|
+
if (this.token === this.tok.T_IMPLEMENTS) {
|
|
198
|
+
return this.next().read_name_list();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return null;
|
|
202
|
+
},
|
|
203
|
+
};
|
|
@@ -0,0 +1,363 @@
|
|
|
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 a variable
|
|
11
|
+
*
|
|
12
|
+
* ```ebnf
|
|
13
|
+
* variable ::= &? ...complex @todo
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* Some samples of parsed code :
|
|
17
|
+
* ```php
|
|
18
|
+
* &$var // simple var
|
|
19
|
+
* $var // simple var
|
|
20
|
+
* classname::CONST_NAME // dynamic class name with const retrieval
|
|
21
|
+
* foo() // function call
|
|
22
|
+
* $var->func()->property // chained calls
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
read_variable(read_only, encapsed) {
|
|
26
|
+
let result;
|
|
27
|
+
// check the byref flag
|
|
28
|
+
if (this.token === "&") {
|
|
29
|
+
return this.read_byref(
|
|
30
|
+
this.read_variable.bind(this, read_only, encapsed),
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// reads the entry point
|
|
35
|
+
if (this.is([this.tok.T_VARIABLE, "$"])) {
|
|
36
|
+
result = this.read_reference_variable(encapsed);
|
|
37
|
+
} else if (
|
|
38
|
+
this.is([
|
|
39
|
+
this.tok.T_NS_SEPARATOR,
|
|
40
|
+
this.tok.T_STRING,
|
|
41
|
+
this.tok.T_NAME_RELATIVE,
|
|
42
|
+
this.tok.T_NAME_QUALIFIED,
|
|
43
|
+
this.tok.T_NAME_FULLY_QUALIFIED,
|
|
44
|
+
this.tok.T_NAMESPACE,
|
|
45
|
+
])
|
|
46
|
+
) {
|
|
47
|
+
result = this.node();
|
|
48
|
+
const name = this.read_namespace_name();
|
|
49
|
+
if (
|
|
50
|
+
this.token != this.tok.T_DOUBLE_COLON &&
|
|
51
|
+
this.token != "(" &&
|
|
52
|
+
["parentreference", "selfreference"].indexOf(name.kind) === -1
|
|
53
|
+
) {
|
|
54
|
+
// @see parser.js line 130 : resolves a conflict with scalar
|
|
55
|
+
const literal = name.name.toLowerCase();
|
|
56
|
+
if (literal === "true") {
|
|
57
|
+
result = name.destroy(result("boolean", true, name.name));
|
|
58
|
+
} else if (literal === "false") {
|
|
59
|
+
result = name.destroy(result("boolean", false, name.name));
|
|
60
|
+
} else if (literal === "null") {
|
|
61
|
+
result = name.destroy(result("nullkeyword", name.name));
|
|
62
|
+
} else {
|
|
63
|
+
result.destroy(name);
|
|
64
|
+
result = name;
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
// @fixme possible #193 bug
|
|
68
|
+
result.destroy(name);
|
|
69
|
+
result = name;
|
|
70
|
+
}
|
|
71
|
+
} else if (this.token === this.tok.T_STATIC) {
|
|
72
|
+
result = this.node("staticreference");
|
|
73
|
+
const raw = this.text();
|
|
74
|
+
this.next();
|
|
75
|
+
result = result(raw);
|
|
76
|
+
} else {
|
|
77
|
+
this.expect("VARIABLE");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// static mode
|
|
81
|
+
if (this.token === this.tok.T_DOUBLE_COLON) {
|
|
82
|
+
result = this.read_static_getter(result, encapsed);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return this.recursive_variable_chain_scan(result, read_only, encapsed);
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
// resolves a static call
|
|
89
|
+
read_static_getter(what, encapsed) {
|
|
90
|
+
const result = this.node("staticlookup");
|
|
91
|
+
let offset, name;
|
|
92
|
+
if (this.next().is([this.tok.T_VARIABLE, "$"])) {
|
|
93
|
+
offset = this.read_reference_variable(encapsed);
|
|
94
|
+
} else if (
|
|
95
|
+
this.token === this.tok.T_STRING ||
|
|
96
|
+
this.token === this.tok.T_CLASS ||
|
|
97
|
+
(this.version >= 700 && this.is("IDENTIFIER"))
|
|
98
|
+
) {
|
|
99
|
+
offset = this.node("identifier");
|
|
100
|
+
name = this.text();
|
|
101
|
+
this.next();
|
|
102
|
+
offset = offset(name);
|
|
103
|
+
} else if (this.token === "{") {
|
|
104
|
+
offset = this.node("literal");
|
|
105
|
+
name = this.next().read_expr();
|
|
106
|
+
this.expect("}") && this.next();
|
|
107
|
+
offset = offset("literal", name, null);
|
|
108
|
+
} else {
|
|
109
|
+
this.error([this.tok.T_VARIABLE, this.tok.T_STRING]);
|
|
110
|
+
// graceful mode : set getter as error node and continue
|
|
111
|
+
offset = this.node("identifier");
|
|
112
|
+
name = this.text();
|
|
113
|
+
this.next();
|
|
114
|
+
offset = offset(name);
|
|
115
|
+
}
|
|
116
|
+
return result(what, offset);
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
read_what(is_static_lookup = false) {
|
|
120
|
+
let what = null;
|
|
121
|
+
let name = null;
|
|
122
|
+
switch (this.next().token) {
|
|
123
|
+
case this.tok.T_STRING:
|
|
124
|
+
what = this.node("identifier");
|
|
125
|
+
name = this.text();
|
|
126
|
+
this.next();
|
|
127
|
+
what = what(name);
|
|
128
|
+
|
|
129
|
+
if (is_static_lookup && this.token === this.tok.T_OBJECT_OPERATOR) {
|
|
130
|
+
this.error();
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
case this.tok.T_VARIABLE:
|
|
134
|
+
what = this.node("variable");
|
|
135
|
+
name = this.text().substring(1);
|
|
136
|
+
this.next();
|
|
137
|
+
what = what(name, false);
|
|
138
|
+
break;
|
|
139
|
+
case this.tok.T_CLASS:
|
|
140
|
+
if (!is_static_lookup) {
|
|
141
|
+
this.error();
|
|
142
|
+
}
|
|
143
|
+
what = this.node("identifier");
|
|
144
|
+
name = this.text();
|
|
145
|
+
this.next();
|
|
146
|
+
what = what(name, false);
|
|
147
|
+
break;
|
|
148
|
+
case "$":
|
|
149
|
+
what = this.node();
|
|
150
|
+
this.next().expect(["$", "{", this.tok.T_VARIABLE]);
|
|
151
|
+
if (this.token === "{") {
|
|
152
|
+
// $obj->${$varname}
|
|
153
|
+
name = this.next().read_expr();
|
|
154
|
+
this.expect("}") && this.next();
|
|
155
|
+
what = what("variable", name, true);
|
|
156
|
+
} else {
|
|
157
|
+
// $obj->$$varname
|
|
158
|
+
name = this.read_expr();
|
|
159
|
+
what = what("variable", name, false);
|
|
160
|
+
}
|
|
161
|
+
break;
|
|
162
|
+
case "{":
|
|
163
|
+
what = this.node("encapsedpart");
|
|
164
|
+
name = this.next().read_expr();
|
|
165
|
+
this.expect("}") && this.next();
|
|
166
|
+
what = what(name, "complex", false);
|
|
167
|
+
break;
|
|
168
|
+
default:
|
|
169
|
+
this.error([this.tok.T_STRING, this.tok.T_VARIABLE, "$", "{"]);
|
|
170
|
+
// graceful mode : set what as error mode & continue
|
|
171
|
+
what = this.node("identifier");
|
|
172
|
+
name = this.text();
|
|
173
|
+
this.next();
|
|
174
|
+
what = what(name);
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return what;
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
recursive_variable_chain_scan(result, read_only, encapsed) {
|
|
182
|
+
let node, offset;
|
|
183
|
+
recursive_scan_loop: while (this.token != this.EOF) {
|
|
184
|
+
switch (this.token) {
|
|
185
|
+
case "(":
|
|
186
|
+
if (read_only) {
|
|
187
|
+
// @fixme : add more informations & test
|
|
188
|
+
return result;
|
|
189
|
+
} else {
|
|
190
|
+
result = this.node("call")(result, this.read_argument_list());
|
|
191
|
+
}
|
|
192
|
+
break;
|
|
193
|
+
case "[":
|
|
194
|
+
case "{": {
|
|
195
|
+
const backet = this.token;
|
|
196
|
+
const isSquareBracket = backet === "[";
|
|
197
|
+
node = this.node("offsetlookup");
|
|
198
|
+
this.next();
|
|
199
|
+
offset = false;
|
|
200
|
+
if (encapsed) {
|
|
201
|
+
offset = this.read_encaps_var_offset();
|
|
202
|
+
this.expect(isSquareBracket ? "]" : "}") && this.next();
|
|
203
|
+
} else {
|
|
204
|
+
const isCallableVariable = isSquareBracket
|
|
205
|
+
? this.token !== "]"
|
|
206
|
+
: this.token !== "}";
|
|
207
|
+
// callable_variable : https://github.com/php/php-src/blob/493524454d66adde84e00d249d607ecd540de99f/Zend/zend_language_parser.y#L1122
|
|
208
|
+
if (isCallableVariable) {
|
|
209
|
+
offset = this.read_expr();
|
|
210
|
+
this.expect(isSquareBracket ? "]" : "}") && this.next();
|
|
211
|
+
} else {
|
|
212
|
+
this.next();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
result = node(result, offset);
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
case this.tok.T_DOUBLE_COLON:
|
|
219
|
+
// @see https://github.com/glayzzle/php-parser/issues/107#issuecomment-354104574
|
|
220
|
+
if (
|
|
221
|
+
result.kind === "staticlookup" &&
|
|
222
|
+
result.offset.kind === "identifier"
|
|
223
|
+
) {
|
|
224
|
+
this.error();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
node = this.node("staticlookup");
|
|
228
|
+
result = node(result, this.read_what(true));
|
|
229
|
+
|
|
230
|
+
// fix 185
|
|
231
|
+
// static lookup dereferencables are limited to staticlookup over functions
|
|
232
|
+
/*if (dereferencable && this.token !== "(") {
|
|
233
|
+
this.error("(");
|
|
234
|
+
}*/
|
|
235
|
+
break;
|
|
236
|
+
case this.tok.T_OBJECT_OPERATOR: {
|
|
237
|
+
node = this.node("propertylookup");
|
|
238
|
+
result = node(result, this.read_what());
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
case this.tok.T_NULLSAFE_OBJECT_OPERATOR: {
|
|
242
|
+
node = this.node("nullsafepropertylookup");
|
|
243
|
+
result = node(result, this.read_what());
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
default:
|
|
247
|
+
break recursive_scan_loop;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return result;
|
|
251
|
+
},
|
|
252
|
+
/*
|
|
253
|
+
* https://github.com/php/php-src/blob/493524454d66adde84e00d249d607ecd540de99f/Zend/zend_language_parser.y#L1231
|
|
254
|
+
*/
|
|
255
|
+
read_encaps_var_offset() {
|
|
256
|
+
let offset = this.node();
|
|
257
|
+
if (this.token === this.tok.T_STRING) {
|
|
258
|
+
const text = this.text();
|
|
259
|
+
this.next();
|
|
260
|
+
offset = offset("identifier", text);
|
|
261
|
+
} else if (this.token === this.tok.T_NUM_STRING) {
|
|
262
|
+
const num = this.text();
|
|
263
|
+
this.next();
|
|
264
|
+
offset = offset("number", num, null);
|
|
265
|
+
} else if (this.token === "-") {
|
|
266
|
+
this.next();
|
|
267
|
+
const num = -1 * this.text();
|
|
268
|
+
this.expect(this.tok.T_NUM_STRING) && this.next();
|
|
269
|
+
offset = offset("number", num, null);
|
|
270
|
+
} else if (this.token === this.tok.T_VARIABLE) {
|
|
271
|
+
const name = this.text().substring(1);
|
|
272
|
+
this.next();
|
|
273
|
+
offset = offset("variable", name, false);
|
|
274
|
+
} else {
|
|
275
|
+
this.expect([
|
|
276
|
+
this.tok.T_STRING,
|
|
277
|
+
this.tok.T_NUM_STRING,
|
|
278
|
+
"-",
|
|
279
|
+
this.tok.T_VARIABLE,
|
|
280
|
+
]);
|
|
281
|
+
// fallback : consider as identifier
|
|
282
|
+
const text = this.text();
|
|
283
|
+
this.next();
|
|
284
|
+
offset = offset("identifier", text);
|
|
285
|
+
}
|
|
286
|
+
return offset;
|
|
287
|
+
},
|
|
288
|
+
/*
|
|
289
|
+
* ```ebnf
|
|
290
|
+
* reference_variable ::= simple_variable ('[' OFFSET ']')* | '{' EXPR '}'
|
|
291
|
+
* ```
|
|
292
|
+
* <code>
|
|
293
|
+
* $foo[123]; // foo is an array ==> gets its entry
|
|
294
|
+
* $foo{1}; // foo is a string ==> get the 2nd char offset
|
|
295
|
+
* ${'foo'}[123]; // get the dynamic var $foo
|
|
296
|
+
* $foo[123]{1}; // gets the 2nd char from the 123 array entry
|
|
297
|
+
* </code>
|
|
298
|
+
*/
|
|
299
|
+
read_reference_variable(encapsed) {
|
|
300
|
+
let result = this.read_simple_variable();
|
|
301
|
+
let offset;
|
|
302
|
+
while (this.token != this.EOF) {
|
|
303
|
+
const node = this.node();
|
|
304
|
+
if (this.token == "{" && !encapsed) {
|
|
305
|
+
// @fixme check coverage, not sure thats working
|
|
306
|
+
offset = this.next().read_expr();
|
|
307
|
+
this.expect("}") && this.next();
|
|
308
|
+
result = node("offsetlookup", result, offset);
|
|
309
|
+
} else {
|
|
310
|
+
node.destroy();
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return result;
|
|
315
|
+
},
|
|
316
|
+
/*
|
|
317
|
+
* ```ebnf
|
|
318
|
+
* simple_variable ::= T_VARIABLE | '$' '{' expr '}' | '$' simple_variable
|
|
319
|
+
* ```
|
|
320
|
+
*/
|
|
321
|
+
read_simple_variable() {
|
|
322
|
+
let result = this.node("variable");
|
|
323
|
+
let name;
|
|
324
|
+
if (
|
|
325
|
+
this.expect([this.tok.T_VARIABLE, "$"]) &&
|
|
326
|
+
this.token === this.tok.T_VARIABLE
|
|
327
|
+
) {
|
|
328
|
+
// plain variable name
|
|
329
|
+
name = this.text().substring(1);
|
|
330
|
+
this.next();
|
|
331
|
+
result = result(name, false);
|
|
332
|
+
} else {
|
|
333
|
+
if (this.token === "$") this.next();
|
|
334
|
+
// dynamic variable name
|
|
335
|
+
switch (this.token) {
|
|
336
|
+
case "{": {
|
|
337
|
+
const expr = this.next().read_expr();
|
|
338
|
+
this.expect("}") && this.next();
|
|
339
|
+
result = result(expr, true);
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
case "$": // $$$var
|
|
343
|
+
result = result(this.read_simple_variable(), false);
|
|
344
|
+
break;
|
|
345
|
+
case this.tok.T_VARIABLE: {
|
|
346
|
+
// $$var
|
|
347
|
+
name = this.text().substring(1);
|
|
348
|
+
const node = this.node("variable");
|
|
349
|
+
this.next();
|
|
350
|
+
result = result(node(name, false), false);
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
default:
|
|
354
|
+
this.error(["{", "$", this.tok.T_VARIABLE]);
|
|
355
|
+
// graceful mode
|
|
356
|
+
name = this.text();
|
|
357
|
+
this.next();
|
|
358
|
+
result = result(name, false);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return result;
|
|
362
|
+
},
|
|
363
|
+
};
|