Skip to content

Commit 7a82704

Browse files
feat: add support for PHP 8.3 typed class constants (glayzzle#1136)
[Related RFC](https://wiki.php.net/rfc/typed_class_constants). Closes glayzzle#1133
1 parent 80eb946 commit 7a82704

11 files changed

+126
-4
lines changed

src/ast/classconstant.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,27 @@ const IS_PRIVATE = "private";
1919
* @memberOf module:php-parser
2020
* @extends {ConstantStatement}
2121
* @property {string} visibility
22-
* @property {bool} final
22+
* @property {boolean} final
23+
* @property {boolean} nullable
24+
* @property {TypeReference|IntersectionType|UnionType|null} type
2325
* @property {AttrGroup[]} attrGroups
2426
*/
2527
const ClassConstant = ConstantStatement.extends(
2628
KIND,
27-
function ClassConstant(kind, constants, flags, attrGroups, docs, location) {
29+
function ClassConstant(
30+
kind,
31+
constants,
32+
flags,
33+
nullable,
34+
type,
35+
attrGroups,
36+
docs,
37+
location
38+
) {
2839
ConstantStatement.apply(this, [kind || KIND, constants, docs, location]);
2940
this.parseFlags(flags);
41+
this.nullable = nullable;
42+
this.type = type;
3043
this.attrGroups = attrGroups;
3144
}
3245
);

src/parser/class.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,13 +226,17 @@ module.exports = {
226226
/*
227227
* Reads constant list
228228
* ```ebnf
229-
* constant_list ::= T_CONST (constant_declaration ',')* constant_declaration
229+
* constant_list ::= T_CONST [type] (constant_declaration ',')* constant_declaration
230230
* ```
231231
*/
232232
read_constant_list: function (flags, attrs) {
233233
if (this.expect(this.tok.T_CONST)) {
234234
this.next();
235235
}
236+
237+
const [nullable, type] =
238+
this.version >= 830 ? this.read_optional_type() : [false, null];
239+
236240
const result = this.node("classconstant");
237241
const items = this.read_list(
238242
/*
@@ -266,7 +270,7 @@ module.exports = {
266270
","
267271
);
268272

269-
return result(null, items, flags, attrs || []);
273+
return result(null, items, flags, nullable, type, attrs || []);
270274
},
271275
/*
272276
* Read member flags

test/snapshot/__snapshots__/acid.test.js.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,8 @@ Program {
826826
"offset": 544,
827827
},
828828
},
829+
"nullable": false,
830+
"type": null,
829831
"visibility": "",
830832
},
831833
PropertyStatement {

test/snapshot/__snapshots__/attributes.test.js.snap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,8 @@ Program {
335335
],
336336
"final": false,
337337
"kind": "classconstant",
338+
"nullable": false,
339+
"type": null,
338340
"visibility": "",
339341
},
340342
],
@@ -524,6 +526,8 @@ Program {
524526
],
525527
"final": false,
526528
"kind": "classconstant",
529+
"nullable": false,
530+
"type": null,
527531
"visibility": "",
528532
},
529533
Method {

test/snapshot/__snapshots__/class.test.js.snap

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ Program {
172172
",
173173
},
174174
],
175+
"nullable": false,
176+
"type": null,
175177
"visibility": "",
176178
},
177179
Method {
@@ -240,6 +242,8 @@ Program {
240242
",
241243
},
242244
],
245+
"nullable": false,
246+
"type": null,
243247
"visibility": "",
244248
},
245249
Method {
@@ -1338,6 +1342,8 @@ Program {
13381342
],
13391343
"final": false,
13401344
"kind": "classconstant",
1345+
"nullable": false,
1346+
"type": null,
13411347
"visibility": "",
13421348
},
13431349
PropertyStatement {
@@ -1448,6 +1454,8 @@ Program {
14481454
],
14491455
"final": false,
14501456
"kind": "classconstant",
1457+
"nullable": false,
1458+
"type": null,
14511459
"visibility": "",
14521460
},
14531461
Method {

test/snapshot/__snapshots__/classconstant.test.js.snap

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ Program {
2626
],
2727
"final": true,
2828
"kind": "classconstant",
29+
"nullable": false,
30+
"type": null,
2931
"visibility": "public",
3032
},
3133
],
@@ -87,6 +89,8 @@ Program {
8789
],
8890
"final": false,
8991
"kind": "classconstant",
92+
"nullable": false,
93+
"type": null,
9094
"visibility": "",
9195
},
9296
],
@@ -134,6 +138,8 @@ Program {
134138
],
135139
"final": false,
136140
"kind": "classconstant",
141+
"nullable": false,
142+
"type": null,
137143
"visibility": "private",
138144
},
139145
],
@@ -181,6 +187,8 @@ Program {
181187
],
182188
"final": false,
183189
"kind": "classconstant",
190+
"nullable": false,
191+
"type": null,
184192
"visibility": "protected",
185193
},
186194
],
@@ -228,6 +236,8 @@ Program {
228236
],
229237
"final": false,
230238
"kind": "classconstant",
239+
"nullable": false,
240+
"type": null,
231241
"visibility": "public",
232242
},
233243
],
@@ -275,6 +285,8 @@ Program {
275285
],
276286
"final": false,
277287
"kind": "classconstant",
288+
"nullable": false,
289+
"type": null,
278290
"visibility": "",
279291
},
280292
],
@@ -295,3 +307,58 @@ Program {
295307
"kind": "program",
296308
}
297309
`;
310+
311+
exports[`classconstant type hinted (supported) 1`] = `
312+
Program {
313+
"children": [
314+
Class {
315+
"attrGroups": [],
316+
"body": [
317+
ClassConstant {
318+
"attrGroups": [],
319+
"constants": [
320+
Constant {
321+
"kind": "constant",
322+
"name": Identifier {
323+
"kind": "identifier",
324+
"name": "CONSTANT",
325+
},
326+
"value": String {
327+
"isDoubleQuote": true,
328+
"kind": "string",
329+
"raw": ""Hello world!"",
330+
"unicode": false,
331+
"value": "Hello world!",
332+
},
333+
},
334+
],
335+
"final": false,
336+
"kind": "classconstant",
337+
"nullable": false,
338+
"type": TypeReference {
339+
"kind": "typereference",
340+
"name": "string",
341+
"raw": "string",
342+
},
343+
"visibility": "public",
344+
},
345+
],
346+
"extends": null,
347+
"implements": null,
348+
"isAbstract": false,
349+
"isAnonymous": false,
350+
"isFinal": false,
351+
"isReadonly": false,
352+
"kind": "class",
353+
"name": Identifier {
354+
"kind": "identifier",
355+
"name": "Foo",
356+
},
357+
},
358+
],
359+
"errors": [],
360+
"kind": "program",
361+
}
362+
`;
363+
364+
exports[`classconstant type hinted (unsupported) 1`] = `"Parse Error : syntax error, unexpected 'CONSTANT' (T_STRING), expecting '=' on line 1"`;

test/snapshot/__snapshots__/enum.test.js.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ Program {
3838
],
3939
"final": false,
4040
"kind": "classconstant",
41+
"nullable": false,
42+
"type": null,
4143
"visibility": "public",
4244
},
4345
],

test/snapshot/__snapshots__/heredoc.test.js.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1711,6 +1711,8 @@ FOOBAR",
17111711
],
17121712
"final": false,
17131713
"kind": "classconstant",
1714+
"nullable": false,
1715+
"type": null,
17141716
"visibility": "",
17151717
},
17161718
PropertyStatement {

test/snapshot/__snapshots__/interface.test.js.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ Program {
6767
],
6868
"final": false,
6969
"kind": "classconstant",
70+
"nullable": false,
71+
"type": null,
7072
"visibility": "",
7173
},
7274
],

test/snapshot/__snapshots__/nowdoc.test.js.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ FOOBAR",
218218
],
219219
"final": false,
220220
"kind": "classconstant",
221+
"nullable": false,
222+
"type": null,
221223
"visibility": "",
222224
},
223225
PropertyStatement {

test/snapshot/classconstant.test.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,20 @@ describe("classconstant", () => {
3737
)
3838
).toMatchSnapshot();
3939
});
40+
it("type hinted (supported)", () => {
41+
expect(
42+
parser.parseEval(
43+
'class Foo { public const string CONSTANT = "Hello world!"; }',
44+
{ parser: { version: 830 } }
45+
)
46+
).toMatchSnapshot();
47+
});
48+
it("type hinted (unsupported)", () => {
49+
expect(() =>
50+
parser.parseEval(
51+
'class Foo { public const string CONSTANT = "Hello world!"; }',
52+
{ parser: { version: 820 } }
53+
)
54+
).toThrowErrorMatchingSnapshot();
55+
});
4056
});

0 commit comments

Comments
 (0)