Skip to content

Commit 4676ac0

Browse files
committed
fix: refactor version handling, change default to 8.4
This consolidates `options.lexer.version` and `options.parser.version` to a central `options.version`. `options.parser.version` still works, but is deprecated. This also updates the default version to 8.4.
1 parent 7d134e8 commit 4676ac0

File tree

17 files changed

+105
-109
lines changed

17 files changed

+105
-109
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const engine = require("php-parser");
2525
// initialize a new parser instance
2626
const parser = new engine({
2727
// some options :
28+
version: "8.4", // specify the PHP version to parse
2829
parser: {
2930
extractDoc: true,
3031
php7: true,

src/index.js

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ const parser = require("./parser");
1010
const tokens = require("./tokens");
1111
const AST = require("./ast");
1212

13+
const DEFAULT_PHP_VERSION = "8.4";
14+
1315
/**
1416
* @private
1517
*/
@@ -43,10 +45,10 @@ function combine(src, to) {
4345
* @example
4446
* var parser = require('php-parser');
4547
* var instance = new parser({
48+
* version: 704 // or '7.4'
4649
* parser: {
4750
* extractDoc: true,
4851
* suppressErrors: true,
49-
* version: 704 // or '7.4'
5052
* },
5153
* ast: {
5254
* withPositions: true
@@ -81,30 +83,40 @@ const Engine = function (options) {
8183
if (!options.lexer) {
8284
options.lexer = {};
8385
}
84-
if (options.parser.version) {
85-
if (typeof options.parser.version === "string") {
86-
let version = options.parser.version.split(".");
87-
version = parseInt(version[0]) * 100 + parseInt(version[1]);
88-
if (isNaN(version)) {
89-
throw new Error("Bad version number : " + options.parser.version);
90-
} else {
91-
options.parser.version = version;
92-
}
93-
} else if (typeof options.parser.version !== "number") {
94-
throw new Error("Expecting a number for version");
95-
}
96-
if (options.parser.version < 500 || options.parser.version > 900) {
97-
throw new Error("Can only handle versions between 5.x to 8.x");
98-
}
99-
}
10086
}
10187
combine(options, this);
102-
103-
// same version flags based on parser options
104-
this.lexer.version = this.parser.version;
10588
}
89+
90+
// options.parser.version is deprecated, use options.version instead
91+
const versionString = options?.version ?? options?.parser?.version;
92+
this.version = normalizeVersion(versionString ?? DEFAULT_PHP_VERSION);
10693
};
10794

95+
/**
96+
* Validate and normalize a version (string or number) to a version number
97+
* @private
98+
* @param {String|Number} versionString - The version string or number to
99+
* validate and normalize, e.g., "7.4", or 704
100+
* @return {Number} - The normalized version number, e.g. 704
101+
* @throws {Error} - If the version is not a valid number or out of range
102+
*/
103+
function normalizeVersion(versionString) {
104+
let version = versionString;
105+
if (typeof version === "string") {
106+
const versionParts = version.split(".");
107+
version = parseInt(versionParts[0]) * 100 + parseInt(versionParts[1]);
108+
if (isNaN(version)) {
109+
throw new Error("Bad version number : " + versionString);
110+
}
111+
} else if (typeof version !== "number") {
112+
throw new Error("Expecting a string or number for version");
113+
}
114+
if (version < 500 || version > 900) {
115+
throw new Error("Can only handle versions between 5.x to 8.x");
116+
}
117+
return version;
118+
}
119+
108120
/**
109121
* Check if the inpyt is a buffer or a string
110122
* @private

src/lexer.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ const Lexer = function (engine) {
3030
this.mode_eval = false;
3131
this.asp_tags = false;
3232
this.short_tags = false;
33-
this.version = 803;
3433
this.yyprevcol = 0;
3534
this.keywords = {
3635
__class__: this.tok.T_CLASS_C,
@@ -150,7 +149,7 @@ Lexer.prototype.setInput = function (input) {
150149
last_column: 0,
151150
};
152151
this.tokens = [];
153-
if (this.version > 703) {
152+
if (this.engine.version > 703) {
154153
this.keywords.fn = this.tok.T_FN;
155154
} else {
156155
delete this.keywords.fn;

src/lexer/scripting.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ module.exports = {
1616
case "\r\n":
1717
return this.T_WHITESPACE();
1818
case "#":
19-
if (this.version >= 800 && this._input[this.offset] === "[") {
19+
if (this.engine.version >= 800 && this._input[this.offset] === "[") {
2020
this.input();
2121
this.attributeListDepth[++this.attributeIndex] = 0;
2222
this.begin("ST_ATTRIBUTE");

src/lexer/strings.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ module.exports = {
159159
let indentation = 0;
160160
let leading_ch = this._input[offset - 1];
161161

162-
if (this.version >= 703) {
162+
if (this.engine.version >= 703) {
163163
while (leading_ch === "\t" || leading_ch === " ") {
164164
if (leading_ch === " ") {
165165
indentation_uses_spaces = true;
@@ -188,7 +188,7 @@ module.exports = {
188188
) {
189189
const ch = this._input[offset - 1 + this.heredoc_label.length];
190190
if (
191-
(this.version >= 703
191+
(this.engine.version >= 703
192192
? valid_after_heredoc_73
193193
: valid_after_heredoc
194194
).includes(ch)

src/lexer/tokens.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ module.exports = {
1111
let id = this.keywords[token];
1212
if (typeof id !== "number") {
1313
if (token === "yield") {
14-
if (this.version >= 700 && this.tryMatch(" from")) {
14+
if (this.engine.version >= 700 && this.tryMatch(" from")) {
1515
this.consume(5);
1616
id = this.tok.T_YIELD_FROM;
1717
} else {
@@ -34,7 +34,7 @@ module.exports = {
3434

3535
// https://github.com/php/php-src/blob/master/Zend/zend_language_scanner.l#L1546
3636
if (id === this.tok.T_ENUM) {
37-
if (this.version < 801) {
37+
if (this.engine.version < 801) {
3838
return this.tok.T_STRING;
3939
}
4040
const initial = this.offset;
@@ -224,8 +224,11 @@ module.exports = {
224224
return "!";
225225
},
226226
"?"() {
227-
if (this.version >= 700 && this._input[this.offset] === "?") {
228-
if (this.version >= 704 && this._input[this.offset + 1] === "=") {
227+
if (this.engine.version >= 700 && this._input[this.offset] === "?") {
228+
if (
229+
this.engine.version >= 704 &&
230+
this._input[this.offset + 1] === "="
231+
) {
229232
this.consume(2);
230233
return this.tok.T_COALESCE_EQUAL;
231234
} else {
@@ -234,7 +237,7 @@ module.exports = {
234237
}
235238
}
236239
if (
237-
this.version >= 800 &&
240+
this.engine.version >= 800 &&
238241
this._input[this.offset] === "-" &&
239242
this._input[this.offset + 1] === ">"
240243
) {
@@ -260,7 +263,7 @@ module.exports = {
260263
return this.tok.T_SL;
261264
} else if (nchar === "=") {
262265
this.input();
263-
if (this.version >= 700 && this._input[this.offset] === ">") {
266+
if (this.engine.version >= 700 && this._input[this.offset] === ">") {
264267
this.input();
265268
return this.tok.T_SPACESHIP;
266269
} else {

src/parser.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@ function isNumber(n) {
3030
*/
3131
const Parser = function (lexer, ast) {
3232
this.lexer = lexer;
33+
this.engine = lexer.engine;
3334
this.ast = ast;
3435
this.tok = lexer.tok;
3536
this.EOF = lexer.EOF;
3637
this.token = null;
3738
this.prev = null;
3839
this.debug = false;
39-
this.version = 803;
4040
this.extractDoc = false;
4141
this.extractTokens = false;
4242
this.suppressErrors = false;

src/parser/array.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,10 @@ module.exports = {
8282
this.next();
8383
byRef = true;
8484
value = this.read_variable(true, false);
85-
} else if (this.token === this.tok.T_ELLIPSIS && this.version >= 704) {
85+
} else if (
86+
this.token === this.tok.T_ELLIPSIS &&
87+
this.engine.version >= 704
88+
) {
8689
this.next();
8790
if (this.token === "&") {
8891
this.error();

src/parser/class.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,9 @@ module.exports = {
136136
} else if (
137137
allow_variables &&
138138
(this.token === this.tok.T_VARIABLE ||
139-
(this.version >= 801 && this.token === this.tok.T_READ_ONLY) ||
139+
(this.engine.version >= 801 && this.token === this.tok.T_READ_ONLY) ||
140140
// support https://wiki.php.net/rfc/typed_properties_v2
141-
(this.version >= 704 &&
141+
(this.engine.version >= 704 &&
142142
(this.token === "?" ||
143143
this.token === this.tok.T_ARRAY ||
144144
this.token === this.tok.T_CALLABLE ||
@@ -228,7 +228,7 @@ module.exports = {
228228
}
229229

230230
const [nullable, type] =
231-
this.version >= 803 ? this.read_optional_type() : [false, null];
231+
this.engine.version >= 803 ? this.read_optional_type() : [false, null];
232232

233233
const result = this.node("classconstant");
234234
const items = this.read_list(
@@ -246,7 +246,7 @@ module.exports = {
246246
let value = null;
247247
if (
248248
this.token === this.tok.T_STRING ||
249-
(this.version >= 700 && this.is("IDENTIFIER"))
249+
(this.engine.version >= 700 && this.is("IDENTIFIER"))
250250
) {
251251
constName = this.node("identifier");
252252
const name = this.text();
@@ -565,7 +565,7 @@ module.exports = {
565565
this.next();
566566
if (
567567
this.token === this.tok.T_STRING ||
568-
(this.version >= 700 && this.is("IDENTIFIER"))
568+
(this.engine.version >= 700 && this.is("IDENTIFIER"))
569569
) {
570570
trait = method;
571571
method = this.node("identifier");
@@ -599,7 +599,7 @@ module.exports = {
599599

600600
if (
601601
this.token === this.tok.T_STRING ||
602-
(this.version >= 700 && this.is("IDENTIFIER"))
602+
(this.engine.version >= 700 && this.is("IDENTIFIER"))
603603
) {
604604
alias = this.node("identifier");
605605
const name = this.text();

src/parser/expr.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,10 @@ module.exports = {
352352

353353
case this.tok.T_NEW:
354354
expr = this.read_new_expr();
355-
if (this.token === this.tok.T_OBJECT_OPERATOR && this.version < 804) {
355+
if (
356+
this.token === this.tok.T_OBJECT_OPERATOR &&
357+
this.engine.version < 804
358+
) {
356359
this.raiseError(
357360
"New without parenthesis is not allowed before PHP 8.4",
358361
);
@@ -394,7 +397,7 @@ module.exports = {
394397
return this.read_expr_cast("unset");
395398

396399
case this.tok.T_THROW: {
397-
if (this.version < 800) {
400+
if (this.engine.version < 800) {
398401
this.raiseError("PHP 8+ is required to use throw as an expression");
399402
}
400403
const result = this.node("throw");
@@ -444,7 +447,7 @@ module.exports = {
444447
this.next();
445448
if (
446449
this.token === this.tok.T_FUNCTION ||
447-
(this.version >= 704 && this.token === this.tok.T_FN)
450+
(this.engine.version >= 704 && this.token === this.tok.T_FN)
448451
) {
449452
// handles static function
450453
return this.read_inline_function([0, 1, 0], attrs);
@@ -595,7 +598,7 @@ module.exports = {
595598
this.next();
596599
let right;
597600
if (this.token === this.tok.T_NEW) {
598-
if (this.version >= 700) {
601+
if (this.engine.version >= 700) {
599602
this.error();
600603
}
601604
right = this.read_new_expr();
@@ -628,7 +631,7 @@ module.exports = {
628631
return result;
629632
}
630633
// introduced in PHP 7.4
631-
if (!this.version >= 704) {
634+
if (!this.engine.version >= 704) {
632635
this.raiseError("Arrow Functions are not allowed");
633636
}
634637
// as an arrowfunc
@@ -667,7 +670,7 @@ module.exports = {
667670
read_match_expression() {
668671
const node = this.node("match");
669672
this.expect(this.tok.T_MATCH) && this.next();
670-
if (this.version < 800) {
673+
if (this.engine.version < 800) {
671674
this.raiseError("Match statements are not allowed before PHP 8");
672675
}
673676
let cond = null;

0 commit comments

Comments
 (0)