diff --git a/src/ast.js b/src/ast.js index 0a3b05b43..974898619 100644 --- a/src/ast.js +++ b/src/ast.js @@ -527,6 +527,7 @@ AST.prototype.checkNodes = function () { require("./ast/offsetlookup"), require("./ast/operation"), require("./ast/parameter"), + require("./ast/promotedparameter"), require("./ast/parentreference"), require("./ast/post"), require("./ast/pre"), diff --git a/src/ast/promotedparameter.js b/src/ast/promotedparameter.js new file mode 100644 index 000000000..e1a2ce1b9 --- /dev/null +++ b/src/ast/promotedparameter.js @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2018 Glayzzle (BSD3 License) + * @authors https://github.com/glayzzle/php-parser/graphs/contributors + * @url http://glayzzle.com + */ +"use strict"; + +const Declaration = require("./declaration"); +const KIND = "promotedparameter"; + +/** + * Defines a promoted function parameter (for class constructor) + * @constructor PromotedParameter + * @extends {Declaration} + * @property {Identifier|null} type + * @property {Node|null} value + * @property {boolean} byref + * @property {boolean} variadic + * @property {boolean} nullable + * @property {'public'|'private'|'protected'} promote + */ +module.exports = Declaration.extends(KIND, function PromotedParameter( + name, + type, + value, + isRef, + isVariadic, + nullable, + promote, + docs, + location +) { + Declaration.apply(this, [KIND, name, docs, location]); + this.value = value; + this.type = type; + this.byref = isRef; + this.variadic = isVariadic; + this.nullable = nullable; + this.promote = promote; +}); diff --git a/src/parser/function.js b/src/parser/function.js index f4ef967a4..6b49d0f78 100644 --- a/src/parser/function.js +++ b/src/parser/function.js @@ -204,6 +204,17 @@ module.exports = { let value = null; let types = null; let nullable = false; + let promote = null; + if (this.token === this.tok.T_PUBLIC) { + promote = "public"; + } else if (this.token === this.tok.T_PRIVATE) { + promote = "private"; + } else if (this.token === this.tok.T_PROTECTED) { + promote = "protected"; + } + if (promote !== null) { + this.next(); + } if (this.token === "?") { this.next(); nullable = true; @@ -225,6 +236,17 @@ module.exports = { if (this.token == "=") { value = this.next().read_expr(); } + if (promote !== null) { + return this.node("promotedparameter")( + parameterName, + types, + value, + isRef, + isVariadic, + nullable, + promote + ); + } return node(parameterName, types, value, isRef, isVariadic, nullable); }, read_types() { diff --git a/test/snapshot/__snapshots__/class.test.js.snap b/test/snapshot/__snapshots__/class.test.js.snap index e2c5657fa..d043ffc5c 100644 --- a/test/snapshot/__snapshots__/class.test.js.snap +++ b/test/snapshot/__snapshots__/class.test.js.snap @@ -866,6 +866,96 @@ Program { } `; +exports[`Test classes Test promoted class properties php 8 1`] = ` +Program { + "children": Array [ + Class { + "body": Array [ + Method { + "arguments": Array [ + PromotedParameter { + "byref": false, + "kind": "promotedparameter", + "name": Identifier { + "kind": "identifier", + "name": "id", + }, + "nullable": false, + "promote": "public", + "type": TypeReference { + "kind": "typereference", + "name": "int", + "raw": "int", + }, + "value": null, + "variadic": false, + }, + PromotedParameter { + "byref": false, + "kind": "promotedparameter", + "name": Identifier { + "kind": "identifier", + "name": "name", + }, + "nullable": false, + "promote": "private", + "type": null, + "value": null, + "variadic": false, + }, + PromotedParameter { + "byref": false, + "kind": "promotedparameter", + "name": Identifier { + "kind": "identifier", + "name": "req", + }, + "nullable": false, + "promote": "protected", + "type": Name { + "kind": "name", + "name": "ServerRequestInterface", + "resolution": "uqn", + }, + "value": null, + "variadic": false, + }, + ], + "body": Block { + "children": Array [], + "kind": "block", + }, + "byref": false, + "isAbstract": false, + "isFinal": false, + "isStatic": false, + "kind": "method", + "name": Identifier { + "kind": "identifier", + "name": "constructor", + }, + "nullable": false, + "type": null, + "visibility": "public", + }, + ], + "extends": null, + "implements": null, + "isAbstract": false, + "isAnonymous": false, + "isFinal": false, + "kind": "class", + "name": Identifier { + "kind": "identifier", + "name": "__proto__", + }, + }, + ], + "errors": Array [], + "kind": "program", +} +`; + exports[`Test classes Validate usual declarations 1`] = ` Program { "children": Array [ diff --git a/test/snapshot/class.test.js b/test/snapshot/class.test.js index a26a1975d..f65a3c8d4 100644 --- a/test/snapshot/class.test.js +++ b/test/snapshot/class.test.js @@ -129,6 +129,22 @@ describe("Test classes", function () { ).toMatchSnapshot(); }); + it("Test promoted class properties php 8", function () { + const ast = parser.parseEval( + ` + class __proto__ { + public function constructor(public int $id, private $name, protected ServerRequestInterface $req) {} + }`, + { + parser: { + version: "8.0", + suppressErrors: true, + }, + } + ); + expect(ast).toMatchSnapshot(); + }); + it("Test class union properties", function () { expect( parser.parseEval(`