Skip to content

Commit

Permalink
Support PHP 8.4 property hooks in AST version 110 (#241)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
TysonAndre authored Aug 10, 2024
1 parent 4c5efd5 commit 1fc2c3a
Show file tree
Hide file tree
Showing 22 changed files with 577 additions and 90 deletions.
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ tests/*.out
tests/*.exp
tests/*.log
tests/*.sh
*.dep
*~
tags
configure.ac
3 changes: 2 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ jobs:
- PHP_VERSION: '8.0'
- PHP_VERSION: '8.1'
- PHP_VERSION: '8.2'
- PHP_VERSION: '8.3.0RC5'
- PHP_VERSION: '8.3'
- PHP_VERSION: '8.4.0alpha4'

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@ tests/*.php
tests/*.exp
tests/*.log
tests/*.sh
*.dep
*~
tags
configure.ac
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ This section lists the AST node kinds that are supported and the names of their

```
AST_ARRAY_ELEM: value, key
AST_ARROW_FUNC: name, docComment, params, stmts, returnType, attributes
AST_ARROW_FUNC: name, docComment, params, stmts, returnType, attributes // name removed in version 110
AST_ASSIGN: var, expr
AST_ASSIGN_OP: var, expr
AST_ASSIGN_REF: var, expr
Expand All @@ -390,7 +390,7 @@ AST_CLASS_CONST: class, const
AST_CLASS_CONST_GROUP class, attributes, type // version 80+
AST_CLASS_NAME: class // version 70+
AST_CLONE: expr
AST_CLOSURE: name, docComment, params, uses, stmts, returnType, attributes
AST_CLOSURE: name, docComment, params, uses, stmts, returnType, attributes // name removed in version 110
AST_CLOSURE_VAR: name
AST_CONDITIONAL: cond, true, false
AST_CONST: name
Expand Down Expand Up @@ -430,15 +430,17 @@ AST_NEW: class, args
AST_NULLABLE_TYPE: type // Used only since PHP 7.1
AST_NULLSAFE_METHOD_CALL: expr, method, args // php 8.0 null safe operator
AST_NULLSAFE_PROP: expr, prop // php 8.0 null safe operator
AST_PARAM: type, name, default, attributes, docComment
AST_PARAM: type, name, default, attributes, docComment, hooks // 'hooks' field added in version 110
AST_POST_DEC: var
AST_POST_INC: var
AST_PRE_DEC: var
AST_PRE_INC: var
AST_PRINT: expr
AST_PROP: expr, prop
AST_PROP_ELEM: name, default, docComment
AST_PROP_ELEM: name, default, docComment, hooks // 'hooks' field added in version 110
AST_PROP_GROUP: type, props, attributes // version 70+
AST_PROPERTY_HOOK: name, docComment, params, stmts, attributes // version 110+
AST_PROPERTY_HOOK_SHORT_BODY: expr
AST_REF: var // only used in foreach ($a as &$v)
AST_RETURN: expr
AST_SHELL_EXEC: expr
Expand Down Expand Up @@ -504,6 +506,13 @@ function accepts a boolean argument that determines whether deprecated versions
In the following the changes in the respective AST versions, as well as their current support state,
are listed.

### 110 (current)

Supported since 1.1.2 (2024-08-08).

* 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+)
* Add new node kinds `AST_PROPERTY_HOOK` and `AST_PROPERTY_HOOK_SHORT_BODY`.

### 100 (current)

Supported since 1.1.1 (2023-11-12).
Expand Down
69 changes: 57 additions & 12 deletions ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
#define AST_METADATA_PROP_FLAGS(object) OBJ_PROP_NUM((object), 2)
#define AST_METADATA_PROP_FLAGS_COMBINABLE(object) OBJ_PROP_NUM((object), 3)

#define AST_CURRENT_VERSION 90
#define AST_CURRENT_VERSION 110

/* Additional flags for BINARY_OP */
#define AST_BINARY_IS_GREATER 256
Expand Down Expand Up @@ -117,6 +117,10 @@
# define ZEND_ENCAPS_VAR_DOLLAR_CURLY_VAR_VAR (1<<1)
#endif

#if PHP_VERSION_ID >= 80400
# define ZEND_DIM_ALTERNATIVE_SYNTAX (1<<1)
#endif

/* This contains state of the ast Node creator. */
typedef struct ast_state_info {
zend_long version;
Expand Down Expand Up @@ -336,6 +340,7 @@ static const ast_flag_info flag_info[] = {
{ ZEND_AST_FUNC_DECL, 1, func_flags },
{ ZEND_AST_CLOSURE, 1, func_flags },
{ ZEND_AST_ARROW_FUNC, 1, func_flags },
{ ZEND_AST_PROPERTY_HOOK, 1, func_flags },
{ ZEND_AST_PROP_DECL, 1, modifier_flags },
{ ZEND_AST_PROP_GROUP, 1, modifier_flags },
{ ZEND_AST_CLASS_CONST_DECL, 1, modifier_flags },
Expand Down Expand Up @@ -423,9 +428,11 @@ static inline zend_bool ast_kind_uses_attr(zend_ast_kind kind) {
|| kind == ZEND_AST_VAR;
}

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

Expand Down Expand Up @@ -741,7 +748,7 @@ static void ast_fill_children_ht(HashTable *ht, zend_ast *ast, ast_state_info_t
switch (ast_kind) {
case ZEND_AST_PARAM:
if (i >= 3) {
/* Skip attributes and doc comment */
/* Skip attributes and doc comment and hooks. */
continue;
}
break;
Expand Down Expand Up @@ -779,6 +786,24 @@ static void ast_fill_children_ht(HashTable *ht, zend_ast *ast, ast_state_info_t
}
}
#endif
#if PHP_VERSION_ID >= 80400
if (ast_kind == ZEND_AST_PROP_ELEM && i == 3) {
if (state->version < 110) {
continue;
}
}
if (ast_kind == ZEND_AST_PROPERTY_HOOK && (i == 1 || i == 3)) {
/* Property hooks don't have uses/returnType but they do have params/stmts/attributes. */
continue;
}
/* Constructor property promotion shorthand can have property hooks. */
if (ast_kind == ZEND_AST_PARAM && i == 5) {
if (state->version < 110) {
continue;
}
}
#endif

#endif
if (ast_is_name(child, ast, i)) {
ast_name_to_zval(child, ast, &child_zv, i, state);
Expand Down Expand Up @@ -850,6 +875,17 @@ static void ast_fill_children_ht(HashTable *ht, zend_ast *ast, ast_state_info_t
}
#endif

#if PHP_VERSION_ID < 80400
if (state->version >= 110) {
if (ast_kind == ZEND_AST_PARAM || ast_kind == ZEND_AST_PROP_ELEM) {
zval tmp;
ZVAL_NULL(&tmp);
zend_hash_add_new(ht, AST_STR(str_hooks), &tmp);
return;
}
}
#endif

if (ast_kind_is_decl(ast_kind)) {
zval id_zval;
#if PHP_VERSION_ID < 80000
Expand Down Expand Up @@ -1016,18 +1052,19 @@ static void ast_to_zval(zval *zv, zend_ast *ast, ast_state_info_t *state) {

zend_object *obj = Z_OBJ_P(zv);

AST_NODE_SET_PROP_KIND(obj, ast->kind);
zend_ast_kind kind = ast->kind;
AST_NODE_SET_PROP_KIND(obj, kind);

AST_NODE_SET_PROP_LINENO(obj, zend_ast_get_lineno(ast));

array_init(AST_NODE_PROP_CHILDREN(obj));
HashTable *children = Z_ARRVAL_P(AST_NODE_PROP_CHILDREN(obj));

if (ast_kind_is_decl(ast->kind)) {
if (ast_kind_is_decl(kind)) {
zend_ast_decl *decl = (zend_ast_decl *) ast;
uint32_t flags = decl->flags;
#if PHP_VERSION_ID >= 80200
if (ast->kind == ZEND_AST_CLASS) {
if (kind == ZEND_AST_CLASS) {
flags &= ~ZEND_ACC_NO_DYNAMIC_PROPERTIES;
}
#endif
Expand All @@ -1037,14 +1074,22 @@ static void ast_to_zval(zval *zv, zend_ast *ast, ast_state_info_t *state) {
// This is an undeclared dynamic property and has no cache slot.
ast_update_property_long(zv, AST_STR(str_endLineno), decl->end_lineno);

if (decl->name) {
if (kind == ZEND_AST_CLOSURE || kind == ZEND_AST_ARROW_FUNC) {
if (state->version < 110) {
ZVAL_STR(&tmp_zv, AST_STR(str_bracketed_closure));
} else {
/* These never have names. */
ZVAL_UNDEF(&tmp_zv);
}
} else if (decl->name) {
ZVAL_STR(&tmp_zv, decl->name);
Z_TRY_ADDREF(tmp_zv);
} else {
ZVAL_NULL(&tmp_zv);
}

zend_hash_add_new(children, AST_STR(str_name), &tmp_zv);
if (!Z_ISUNDEF(tmp_zv)) {
zend_hash_add_new(children, AST_STR(str_name), &tmp_zv);
}

if (decl->doc_comment) {
ZVAL_STR(&tmp_zv, decl->doc_comment);
Expand Down Expand Up @@ -1091,7 +1136,7 @@ static void ast_to_zval(zval *zv, zend_ast *ast, ast_state_info_t *state) {
#endif
}

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

static inline zend_bool ast_version_deprecated(zend_long version) {
Expand Down Expand Up @@ -1402,9 +1447,9 @@ PHP_MINIT_FUNCTION(ast) {
zval zv_null;
ZVAL_NULL(&zv_null);

#define X(str) \
#define X(str, value) \
AST_STR(str_ ## str) = zend_new_interned_string( \
zend_string_init(#str, sizeof(#str) - 1, 1));
zend_string_init(value, sizeof(value) - 1, 1));
AST_STR_DEFS
#undef X

Expand Down Expand Up @@ -1544,7 +1589,7 @@ PHP_MINIT_FUNCTION(ast) {
}

PHP_MSHUTDOWN_FUNCTION(ast) {
#define X(str) zend_string_release(AST_STR(str_ ## str));
#define X(str, value) zend_string_release(AST_STR(str_ ## str));
AST_STR_DEFS
#undef X

Expand Down
10 changes: 10 additions & 0 deletions ast_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const zend_ast_kind ast_kinds[] = {
ZEND_AST_METHOD,
ZEND_AST_ARROW_FUNC,
ZEND_AST_CLASS,
ZEND_AST_PROPERTY_HOOK,
ZEND_AST_MAGIC_CONST,
ZEND_AST_TYPE,
ZEND_AST_CALLABLE_CONVERT,
Expand Down Expand Up @@ -63,6 +64,7 @@ const zend_ast_kind ast_kinds[] = {
ZEND_AST_BREAK,
ZEND_AST_CONTINUE,
ZEND_AST_CLASS_NAME,
ZEND_AST_PROPERTY_HOOK_SHORT_BODY,
ZEND_AST_CLASS_CONST_GROUP,
ZEND_AST_DIM,
ZEND_AST_PROP,
Expand Down Expand Up @@ -145,6 +147,7 @@ const char *ast_kind_to_name(zend_ast_kind kind) {
case ZEND_AST_METHOD: return "AST_METHOD";
case ZEND_AST_ARROW_FUNC: return "AST_ARROW_FUNC";
case ZEND_AST_CLASS: return "AST_CLASS";
case ZEND_AST_PROPERTY_HOOK: return "AST_PROPERTY_HOOK";
case ZEND_AST_MAGIC_CONST: return "AST_MAGIC_CONST";
case ZEND_AST_TYPE: return "AST_TYPE";
case ZEND_AST_CALLABLE_CONVERT: return "AST_CALLABLE_CONVERT";
Expand Down Expand Up @@ -177,6 +180,7 @@ const char *ast_kind_to_name(zend_ast_kind kind) {
case ZEND_AST_BREAK: return "AST_BREAK";
case ZEND_AST_CONTINUE: return "AST_CONTINUE";
case ZEND_AST_CLASS_NAME: return "AST_CLASS_NAME";
case ZEND_AST_PROPERTY_HOOK_SHORT_BODY: return "AST_PROPERTY_HOOK_SHORT_BODY";
case ZEND_AST_CLASS_CONST_GROUP: return "AST_CLASS_CONST_GROUP";
case ZEND_AST_DIM: return "AST_DIM";
case ZEND_AST_PROP: return "AST_PROP";
Expand Down Expand Up @@ -248,6 +252,7 @@ zend_string *ast_kind_child_name(zend_ast_kind kind, uint32_t child) {
case ZEND_AST_CLOSURE:
case ZEND_AST_METHOD:
case ZEND_AST_ARROW_FUNC:
case ZEND_AST_PROPERTY_HOOK:
switch (child) {
case 0: return AST_STR(str_params);
case 1: return AST_STR(str_uses);
Expand Down Expand Up @@ -282,6 +287,7 @@ zend_string *ast_kind_child_name(zend_ast_kind kind, uint32_t child) {
case ZEND_AST_RETURN:
case ZEND_AST_ECHO:
case ZEND_AST_THROW:
case ZEND_AST_PROPERTY_HOOK_SHORT_BODY:
switch (child) {
case 0: return AST_STR(str_expr);
}
Expand Down Expand Up @@ -424,6 +430,7 @@ zend_string *ast_kind_child_name(zend_ast_kind kind, uint32_t child) {
case 0: return AST_STR(str_name);
case 1: return AST_STR(str_default);
case 2: return AST_STR(str_docComment);
case 3: return AST_STR(str_hooks);
}
return NULL;
case ZEND_AST_PROP_GROUP:
Expand Down Expand Up @@ -561,6 +568,7 @@ zend_string *ast_kind_child_name(zend_ast_kind kind, uint32_t child) {
case 2: return AST_STR(str_default);
case 3: return AST_STR(str_attributes);
case 4: return AST_STR(str_docComment);
case 5: return AST_STR(str_hooks);
}
return NULL;
}
Expand Down Expand Up @@ -599,6 +607,7 @@ void ast_register_kind_constants(INIT_FUNC_ARGS) {
REGISTER_NS_LONG_CONSTANT("ast", "AST_METHOD", ZEND_AST_METHOD, CONST_CS | CONST_PERSISTENT);
REGISTER_NS_LONG_CONSTANT("ast", "AST_ARROW_FUNC", ZEND_AST_ARROW_FUNC, CONST_CS | CONST_PERSISTENT);
REGISTER_NS_LONG_CONSTANT("ast", "AST_CLASS", ZEND_AST_CLASS, CONST_CS | CONST_PERSISTENT);
REGISTER_NS_LONG_CONSTANT("ast", "AST_PROPERTY_HOOK", ZEND_AST_PROPERTY_HOOK, CONST_CS | CONST_PERSISTENT);
REGISTER_NS_LONG_CONSTANT("ast", "AST_MAGIC_CONST", ZEND_AST_MAGIC_CONST, CONST_CS | CONST_PERSISTENT);
REGISTER_NS_LONG_CONSTANT("ast", "AST_TYPE", ZEND_AST_TYPE, CONST_CS | CONST_PERSISTENT);
REGISTER_NS_LONG_CONSTANT("ast", "AST_CALLABLE_CONVERT", ZEND_AST_CALLABLE_CONVERT, CONST_CS | CONST_PERSISTENT);
Expand Down Expand Up @@ -631,6 +640,7 @@ void ast_register_kind_constants(INIT_FUNC_ARGS) {
REGISTER_NS_LONG_CONSTANT("ast", "AST_BREAK", ZEND_AST_BREAK, CONST_CS | CONST_PERSISTENT);
REGISTER_NS_LONG_CONSTANT("ast", "AST_CONTINUE", ZEND_AST_CONTINUE, CONST_CS | CONST_PERSISTENT);
REGISTER_NS_LONG_CONSTANT("ast", "AST_CLASS_NAME", ZEND_AST_CLASS_NAME, CONST_CS | CONST_PERSISTENT);
REGISTER_NS_LONG_CONSTANT("ast", "AST_PROPERTY_HOOK_SHORT_BODY", ZEND_AST_PROPERTY_HOOK_SHORT_BODY, CONST_CS | CONST_PERSISTENT);
REGISTER_NS_LONG_CONSTANT("ast", "AST_CLASS_CONST_GROUP", ZEND_AST_CLASS_CONST_GROUP, CONST_CS | CONST_PERSISTENT);
REGISTER_NS_LONG_CONSTANT("ast", "AST_DIM", ZEND_AST_DIM, CONST_CS | CONST_PERSISTENT);
REGISTER_NS_LONG_CONSTANT("ast", "AST_PROP", ZEND_AST_PROP, CONST_CS | CONST_PERSISTENT);
Expand Down
Loading

0 comments on commit 1fc2c3a

Please sign in to comment.