Skip to content

Commit 1fc2c3a

Browse files
authored
Support PHP 8.4 property hooks in AST version 110 (#241)
Add 'hooks' child of type `AST_PROPERTY_HOOK` to `AST_PROP_ELEM` and `AST_PARAM` (constructor property promotion) in AST version 110. In version 110, change `AST_CLOSURE` and `AST_ARROW_FUNC` nodes to have no `name`. Fixes #240
1 parent 4c5efd5 commit 1fc2c3a

22 files changed

+577
-90
lines changed

.dockerignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,7 @@ tests/*.out
3838
tests/*.exp
3939
tests/*.log
4040
tests/*.sh
41+
*.dep
42+
*~
43+
tags
44+
configure.ac

.github/workflows/main.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ jobs:
3333
- PHP_VERSION: '8.0'
3434
- PHP_VERSION: '8.1'
3535
- PHP_VERSION: '8.2'
36-
- PHP_VERSION: '8.3.0RC5'
36+
- PHP_VERSION: '8.3'
37+
- PHP_VERSION: '8.4.0alpha4'
3738

3839
# Steps represent a sequence of tasks that will be executed as part of the job
3940
steps:

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,7 @@ tests/*.php
3535
tests/*.exp
3636
tests/*.log
3737
tests/*.sh
38+
*.dep
39+
*~
40+
tags
41+
configure.ac

README.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ This section lists the AST node kinds that are supported and the names of their
374374

375375
```
376376
AST_ARRAY_ELEM: value, key
377-
AST_ARROW_FUNC: name, docComment, params, stmts, returnType, attributes
377+
AST_ARROW_FUNC: name, docComment, params, stmts, returnType, attributes // name removed in version 110
378378
AST_ASSIGN: var, expr
379379
AST_ASSIGN_OP: var, expr
380380
AST_ASSIGN_REF: var, expr
@@ -390,7 +390,7 @@ AST_CLASS_CONST: class, const
390390
AST_CLASS_CONST_GROUP class, attributes, type // version 80+
391391
AST_CLASS_NAME: class // version 70+
392392
AST_CLONE: expr
393-
AST_CLOSURE: name, docComment, params, uses, stmts, returnType, attributes
393+
AST_CLOSURE: name, docComment, params, uses, stmts, returnType, attributes // name removed in version 110
394394
AST_CLOSURE_VAR: name
395395
AST_CONDITIONAL: cond, true, false
396396
AST_CONST: name
@@ -430,15 +430,17 @@ AST_NEW: class, args
430430
AST_NULLABLE_TYPE: type // Used only since PHP 7.1
431431
AST_NULLSAFE_METHOD_CALL: expr, method, args // php 8.0 null safe operator
432432
AST_NULLSAFE_PROP: expr, prop // php 8.0 null safe operator
433-
AST_PARAM: type, name, default, attributes, docComment
433+
AST_PARAM: type, name, default, attributes, docComment, hooks // 'hooks' field added in version 110
434434
AST_POST_DEC: var
435435
AST_POST_INC: var
436436
AST_PRE_DEC: var
437437
AST_PRE_INC: var
438438
AST_PRINT: expr
439439
AST_PROP: expr, prop
440-
AST_PROP_ELEM: name, default, docComment
440+
AST_PROP_ELEM: name, default, docComment, hooks // 'hooks' field added in version 110
441441
AST_PROP_GROUP: type, props, attributes // version 70+
442+
AST_PROPERTY_HOOK: name, docComment, params, stmts, attributes // version 110+
443+
AST_PROPERTY_HOOK_SHORT_BODY: expr
442444
AST_REF: var // only used in foreach ($a as &$v)
443445
AST_RETURN: expr
444446
AST_SHELL_EXEC: expr
@@ -504,6 +506,13 @@ function accepts a boolean argument that determines whether deprecated versions
504506
In the following the changes in the respective AST versions, as well as their current support state,
505507
are listed.
506508

509+
### 110 (current)
510+
511+
Supported since 1.1.2 (2024-08-08).
512+
513+
* Add a `hooks` child node for `AST_PROP_ELEM` (PHP 8.4 property hooks) and `AST_PARAM` (constructor property promotion can have property hooks) (AST version 110+)
514+
* Add new node kinds `AST_PROPERTY_HOOK` and `AST_PROPERTY_HOOK_SHORT_BODY`.
515+
507516
### 100 (current)
508517

509518
Supported since 1.1.1 (2023-11-12).

ast.c

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
#define AST_METADATA_PROP_FLAGS(object) OBJ_PROP_NUM((object), 2)
7272
#define AST_METADATA_PROP_FLAGS_COMBINABLE(object) OBJ_PROP_NUM((object), 3)
7373

74-
#define AST_CURRENT_VERSION 90
74+
#define AST_CURRENT_VERSION 110
7575

7676
/* Additional flags for BINARY_OP */
7777
#define AST_BINARY_IS_GREATER 256
@@ -117,6 +117,10 @@
117117
# define ZEND_ENCAPS_VAR_DOLLAR_CURLY_VAR_VAR (1<<1)
118118
#endif
119119

120+
#if PHP_VERSION_ID >= 80400
121+
# define ZEND_DIM_ALTERNATIVE_SYNTAX (1<<1)
122+
#endif
123+
120124
/* This contains state of the ast Node creator. */
121125
typedef struct ast_state_info {
122126
zend_long version;
@@ -336,6 +340,7 @@ static const ast_flag_info flag_info[] = {
336340
{ ZEND_AST_FUNC_DECL, 1, func_flags },
337341
{ ZEND_AST_CLOSURE, 1, func_flags },
338342
{ ZEND_AST_ARROW_FUNC, 1, func_flags },
343+
{ ZEND_AST_PROPERTY_HOOK, 1, func_flags },
339344
{ ZEND_AST_PROP_DECL, 1, modifier_flags },
340345
{ ZEND_AST_PROP_GROUP, 1, modifier_flags },
341346
{ ZEND_AST_CLASS_CONST_DECL, 1, modifier_flags },
@@ -423,9 +428,11 @@ static inline zend_bool ast_kind_uses_attr(zend_ast_kind kind) {
423428
|| kind == ZEND_AST_VAR;
424429
}
425430

431+
/* Returns true if nodes of this kind are represented with the C struct zend_ast_decl. */
426432
static inline zend_bool ast_kind_is_decl(zend_ast_kind kind) {
427433
return kind == ZEND_AST_FUNC_DECL || kind == ZEND_AST_CLOSURE
428434
|| kind == ZEND_AST_ARROW_FUNC
435+
|| kind == ZEND_AST_PROPERTY_HOOK
429436
|| kind == ZEND_AST_METHOD || kind == ZEND_AST_CLASS;
430437
}
431438

@@ -741,7 +748,7 @@ static void ast_fill_children_ht(HashTable *ht, zend_ast *ast, ast_state_info_t
741748
switch (ast_kind) {
742749
case ZEND_AST_PARAM:
743750
if (i >= 3) {
744-
/* Skip attributes and doc comment */
751+
/* Skip attributes and doc comment and hooks. */
745752
continue;
746753
}
747754
break;
@@ -779,6 +786,24 @@ static void ast_fill_children_ht(HashTable *ht, zend_ast *ast, ast_state_info_t
779786
}
780787
}
781788
#endif
789+
#if PHP_VERSION_ID >= 80400
790+
if (ast_kind == ZEND_AST_PROP_ELEM && i == 3) {
791+
if (state->version < 110) {
792+
continue;
793+
}
794+
}
795+
if (ast_kind == ZEND_AST_PROPERTY_HOOK && (i == 1 || i == 3)) {
796+
/* Property hooks don't have uses/returnType but they do have params/stmts/attributes. */
797+
continue;
798+
}
799+
/* Constructor property promotion shorthand can have property hooks. */
800+
if (ast_kind == ZEND_AST_PARAM && i == 5) {
801+
if (state->version < 110) {
802+
continue;
803+
}
804+
}
805+
#endif
806+
782807
#endif
783808
if (ast_is_name(child, ast, i)) {
784809
ast_name_to_zval(child, ast, &child_zv, i, state);
@@ -850,6 +875,17 @@ static void ast_fill_children_ht(HashTable *ht, zend_ast *ast, ast_state_info_t
850875
}
851876
#endif
852877

878+
#if PHP_VERSION_ID < 80400
879+
if (state->version >= 110) {
880+
if (ast_kind == ZEND_AST_PARAM || ast_kind == ZEND_AST_PROP_ELEM) {
881+
zval tmp;
882+
ZVAL_NULL(&tmp);
883+
zend_hash_add_new(ht, AST_STR(str_hooks), &tmp);
884+
return;
885+
}
886+
}
887+
#endif
888+
853889
if (ast_kind_is_decl(ast_kind)) {
854890
zval id_zval;
855891
#if PHP_VERSION_ID < 80000
@@ -1016,18 +1052,19 @@ static void ast_to_zval(zval *zv, zend_ast *ast, ast_state_info_t *state) {
10161052

10171053
zend_object *obj = Z_OBJ_P(zv);
10181054

1019-
AST_NODE_SET_PROP_KIND(obj, ast->kind);
1055+
zend_ast_kind kind = ast->kind;
1056+
AST_NODE_SET_PROP_KIND(obj, kind);
10201057

10211058
AST_NODE_SET_PROP_LINENO(obj, zend_ast_get_lineno(ast));
10221059

10231060
array_init(AST_NODE_PROP_CHILDREN(obj));
10241061
HashTable *children = Z_ARRVAL_P(AST_NODE_PROP_CHILDREN(obj));
10251062

1026-
if (ast_kind_is_decl(ast->kind)) {
1063+
if (ast_kind_is_decl(kind)) {
10271064
zend_ast_decl *decl = (zend_ast_decl *) ast;
10281065
uint32_t flags = decl->flags;
10291066
#if PHP_VERSION_ID >= 80200
1030-
if (ast->kind == ZEND_AST_CLASS) {
1067+
if (kind == ZEND_AST_CLASS) {
10311068
flags &= ~ZEND_ACC_NO_DYNAMIC_PROPERTIES;
10321069
}
10331070
#endif
@@ -1037,14 +1074,22 @@ static void ast_to_zval(zval *zv, zend_ast *ast, ast_state_info_t *state) {
10371074
// This is an undeclared dynamic property and has no cache slot.
10381075
ast_update_property_long(zv, AST_STR(str_endLineno), decl->end_lineno);
10391076

1040-
if (decl->name) {
1077+
if (kind == ZEND_AST_CLOSURE || kind == ZEND_AST_ARROW_FUNC) {
1078+
if (state->version < 110) {
1079+
ZVAL_STR(&tmp_zv, AST_STR(str_bracketed_closure));
1080+
} else {
1081+
/* These never have names. */
1082+
ZVAL_UNDEF(&tmp_zv);
1083+
}
1084+
} else if (decl->name) {
10411085
ZVAL_STR(&tmp_zv, decl->name);
10421086
Z_TRY_ADDREF(tmp_zv);
10431087
} else {
10441088
ZVAL_NULL(&tmp_zv);
10451089
}
1046-
1047-
zend_hash_add_new(children, AST_STR(str_name), &tmp_zv);
1090+
if (!Z_ISUNDEF(tmp_zv)) {
1091+
zend_hash_add_new(children, AST_STR(str_name), &tmp_zv);
1092+
}
10481093

10491094
if (decl->doc_comment) {
10501095
ZVAL_STR(&tmp_zv, decl->doc_comment);
@@ -1091,7 +1136,7 @@ static void ast_to_zval(zval *zv, zend_ast *ast, ast_state_info_t *state) {
10911136
#endif
10921137
}
10931138

1094-
static const zend_long versions[] = {50, 60, 70, 80, 85, 90, 100};
1139+
static const zend_long versions[] = {50, 60, 70, 80, 85, 90, 100, 110};
10951140
static const size_t versions_count = sizeof(versions)/sizeof(versions[0]);
10961141

10971142
static inline zend_bool ast_version_deprecated(zend_long version) {
@@ -1402,9 +1447,9 @@ PHP_MINIT_FUNCTION(ast) {
14021447
zval zv_null;
14031448
ZVAL_NULL(&zv_null);
14041449

1405-
#define X(str) \
1450+
#define X(str, value) \
14061451
AST_STR(str_ ## str) = zend_new_interned_string( \
1407-
zend_string_init(#str, sizeof(#str) - 1, 1));
1452+
zend_string_init(value, sizeof(value) - 1, 1));
14081453
AST_STR_DEFS
14091454
#undef X
14101455

@@ -1544,7 +1589,7 @@ PHP_MINIT_FUNCTION(ast) {
15441589
}
15451590

15461591
PHP_MSHUTDOWN_FUNCTION(ast) {
1547-
#define X(str) zend_string_release(AST_STR(str_ ## str));
1592+
#define X(str, value) zend_string_release(AST_STR(str_ ## str));
15481593
AST_STR_DEFS
15491594
#undef X
15501595

ast_data.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const zend_ast_kind ast_kinds[] = {
3131
ZEND_AST_METHOD,
3232
ZEND_AST_ARROW_FUNC,
3333
ZEND_AST_CLASS,
34+
ZEND_AST_PROPERTY_HOOK,
3435
ZEND_AST_MAGIC_CONST,
3536
ZEND_AST_TYPE,
3637
ZEND_AST_CALLABLE_CONVERT,
@@ -63,6 +64,7 @@ const zend_ast_kind ast_kinds[] = {
6364
ZEND_AST_BREAK,
6465
ZEND_AST_CONTINUE,
6566
ZEND_AST_CLASS_NAME,
67+
ZEND_AST_PROPERTY_HOOK_SHORT_BODY,
6668
ZEND_AST_CLASS_CONST_GROUP,
6769
ZEND_AST_DIM,
6870
ZEND_AST_PROP,
@@ -145,6 +147,7 @@ const char *ast_kind_to_name(zend_ast_kind kind) {
145147
case ZEND_AST_METHOD: return "AST_METHOD";
146148
case ZEND_AST_ARROW_FUNC: return "AST_ARROW_FUNC";
147149
case ZEND_AST_CLASS: return "AST_CLASS";
150+
case ZEND_AST_PROPERTY_HOOK: return "AST_PROPERTY_HOOK";
148151
case ZEND_AST_MAGIC_CONST: return "AST_MAGIC_CONST";
149152
case ZEND_AST_TYPE: return "AST_TYPE";
150153
case ZEND_AST_CALLABLE_CONVERT: return "AST_CALLABLE_CONVERT";
@@ -177,6 +180,7 @@ const char *ast_kind_to_name(zend_ast_kind kind) {
177180
case ZEND_AST_BREAK: return "AST_BREAK";
178181
case ZEND_AST_CONTINUE: return "AST_CONTINUE";
179182
case ZEND_AST_CLASS_NAME: return "AST_CLASS_NAME";
183+
case ZEND_AST_PROPERTY_HOOK_SHORT_BODY: return "AST_PROPERTY_HOOK_SHORT_BODY";
180184
case ZEND_AST_CLASS_CONST_GROUP: return "AST_CLASS_CONST_GROUP";
181185
case ZEND_AST_DIM: return "AST_DIM";
182186
case ZEND_AST_PROP: return "AST_PROP";
@@ -248,6 +252,7 @@ zend_string *ast_kind_child_name(zend_ast_kind kind, uint32_t child) {
248252
case ZEND_AST_CLOSURE:
249253
case ZEND_AST_METHOD:
250254
case ZEND_AST_ARROW_FUNC:
255+
case ZEND_AST_PROPERTY_HOOK:
251256
switch (child) {
252257
case 0: return AST_STR(str_params);
253258
case 1: return AST_STR(str_uses);
@@ -282,6 +287,7 @@ zend_string *ast_kind_child_name(zend_ast_kind kind, uint32_t child) {
282287
case ZEND_AST_RETURN:
283288
case ZEND_AST_ECHO:
284289
case ZEND_AST_THROW:
290+
case ZEND_AST_PROPERTY_HOOK_SHORT_BODY:
285291
switch (child) {
286292
case 0: return AST_STR(str_expr);
287293
}
@@ -424,6 +430,7 @@ zend_string *ast_kind_child_name(zend_ast_kind kind, uint32_t child) {
424430
case 0: return AST_STR(str_name);
425431
case 1: return AST_STR(str_default);
426432
case 2: return AST_STR(str_docComment);
433+
case 3: return AST_STR(str_hooks);
427434
}
428435
return NULL;
429436
case ZEND_AST_PROP_GROUP:
@@ -561,6 +568,7 @@ zend_string *ast_kind_child_name(zend_ast_kind kind, uint32_t child) {
561568
case 2: return AST_STR(str_default);
562569
case 3: return AST_STR(str_attributes);
563570
case 4: return AST_STR(str_docComment);
571+
case 5: return AST_STR(str_hooks);
564572
}
565573
return NULL;
566574
}
@@ -599,6 +607,7 @@ void ast_register_kind_constants(INIT_FUNC_ARGS) {
599607
REGISTER_NS_LONG_CONSTANT("ast", "AST_METHOD", ZEND_AST_METHOD, CONST_CS | CONST_PERSISTENT);
600608
REGISTER_NS_LONG_CONSTANT("ast", "AST_ARROW_FUNC", ZEND_AST_ARROW_FUNC, CONST_CS | CONST_PERSISTENT);
601609
REGISTER_NS_LONG_CONSTANT("ast", "AST_CLASS", ZEND_AST_CLASS, CONST_CS | CONST_PERSISTENT);
610+
REGISTER_NS_LONG_CONSTANT("ast", "AST_PROPERTY_HOOK", ZEND_AST_PROPERTY_HOOK, CONST_CS | CONST_PERSISTENT);
602611
REGISTER_NS_LONG_CONSTANT("ast", "AST_MAGIC_CONST", ZEND_AST_MAGIC_CONST, CONST_CS | CONST_PERSISTENT);
603612
REGISTER_NS_LONG_CONSTANT("ast", "AST_TYPE", ZEND_AST_TYPE, CONST_CS | CONST_PERSISTENT);
604613
REGISTER_NS_LONG_CONSTANT("ast", "AST_CALLABLE_CONVERT", ZEND_AST_CALLABLE_CONVERT, CONST_CS | CONST_PERSISTENT);
@@ -631,6 +640,7 @@ void ast_register_kind_constants(INIT_FUNC_ARGS) {
631640
REGISTER_NS_LONG_CONSTANT("ast", "AST_BREAK", ZEND_AST_BREAK, CONST_CS | CONST_PERSISTENT);
632641
REGISTER_NS_LONG_CONSTANT("ast", "AST_CONTINUE", ZEND_AST_CONTINUE, CONST_CS | CONST_PERSISTENT);
633642
REGISTER_NS_LONG_CONSTANT("ast", "AST_CLASS_NAME", ZEND_AST_CLASS_NAME, CONST_CS | CONST_PERSISTENT);
643+
REGISTER_NS_LONG_CONSTANT("ast", "AST_PROPERTY_HOOK_SHORT_BODY", ZEND_AST_PROPERTY_HOOK_SHORT_BODY, CONST_CS | CONST_PERSISTENT);
634644
REGISTER_NS_LONG_CONSTANT("ast", "AST_CLASS_CONST_GROUP", ZEND_AST_CLASS_CONST_GROUP, CONST_CS | CONST_PERSISTENT);
635645
REGISTER_NS_LONG_CONSTANT("ast", "AST_DIM", ZEND_AST_DIM, CONST_CS | CONST_PERSISTENT);
636646
REGISTER_NS_LONG_CONSTANT("ast", "AST_PROP", ZEND_AST_PROP, CONST_CS | CONST_PERSISTENT);

0 commit comments

Comments
 (0)