Skip to content

Support PHP 8.4 property hooks in AST version 110 #241

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading