Skip to content

Commit

Permalink
Support PHP 8.4 property hooks in AST version 110
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).

Add test of `new X()->propName`

Fixes #240
  • Loading branch information
TysonAndre committed Aug 8, 2024
1 parent 4c5efd5 commit 35ed2cc
Show file tree
Hide file tree
Showing 22 changed files with 575 additions and 85 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
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
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, returnType, 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
57 changes: 48 additions & 9 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 @@ -426,6 +431,7 @@ static inline zend_bool ast_kind_uses_attr(zend_ast_kind kind) {
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 +747,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 +785,20 @@ 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;
}
}
/* 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 +870,19 @@ 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);
/* For now, ast_kind_is_decl is false. */
/* TODO a future backwards incompatible release dropping support for older AST versions might have property elements as declarations? */
return;
}
}
#endif

if (ast_kind_is_decl(ast_kind)) {
zval id_zval;
#if PHP_VERSION_ID < 80000
Expand Down Expand Up @@ -1016,18 +1049,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,6 +1071,11 @@ 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 PHP_VERSION_ID >= 80400
if (kind == ZEND_AST_CLOSURE || kind == ZEND_AST_ARROW_FUNC) {
ZVAL_STR(&tmp_zv, AST_STR(str_bracketed_closure));
} else
#endif
if (decl->name) {
ZVAL_STR(&tmp_zv, decl->name);
Z_TRY_ADDREF(tmp_zv);
Expand Down Expand Up @@ -1091,7 +1130,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 +1441,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 +1583,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
98 changes: 50 additions & 48 deletions ast_str_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,55 @@
#define AST_STR_DEFS_H

#define AST_STR_DEFS \
X(kind) \
X(flags) \
X(lineno) \
X(children) \
X(name) \
X(docComment) \
X(endLineno) \
X(__declId) \
X(flagsCombinable) \
X(type) \
X(params) \
X(uses) \
X(stmts) \
X(returnType) \
X(attributes) \
X(extends) \
X(implements) \
X(expr) \
X(var) \
X(offset) \
X(label) \
X(depth) \
X(class) \
X(const) \
X(dim) \
X(prop) \
X(args) \
X(left) \
X(right) \
X(value) \
X(key) \
X(default) \
X(cond) \
X(declares) \
X(props) \
X(traits) \
X(adaptations) \
X(method) \
X(insteadof) \
X(alias) \
X(prefix) \
X(true) \
X(false) \
X(try) \
X(catches) \
X(finally) \
X(init) \
X(loop) \
X(kind, "kind") \
X(flags, "flags") \
X(lineno, "lineno") \
X(children, "children") \
X(name, "name") \
X(docComment, "docComment") \
X(endLineno, "endLineno") \
X(__declId, "__declId") \
X(flagsCombinable, "flagsCombinable") \
X(type, "type") \
X(params, "params") \
X(uses, "uses") \
X(stmts, "stmts") \
X(returnType, "returnType") \
X(attributes, "attributes") \
X(extends, "extends") \
X(implements, "implements") \
X(expr, "expr") \
X(var, "var") \
X(offset, "offset") \
X(label, "label") \
X(depth, "depth") \
X(class, "class") \
X(const, "const") \
X(dim, "dim") \
X(prop, "prop") \
X(args, "args") \
X(left, "left") \
X(right, "right") \
X(value, "value") \
X(key, "key") \
X(default, "default") \
X(cond, "cond") \
X(declares, "declares") \
X(hooks, "hooks") \
X(props, "props") \
X(traits, "traits") \
X(adaptations, "adaptations") \
X(method, "method") \
X(insteadof, "insteadof") \
X(alias, "alias") \
X(prefix, "prefix") \
X(true, "true") \
X(false, "false") \
X(try, "try") \
X(catches, "catches") \
X(finally, "finally") \
X(init, "init") \
X(loop, "loop") \
X(bracketed_closure, "{closure}") \

#endif
Loading

0 comments on commit 35ed2cc

Please sign in to comment.