diff --git a/.dockerignore b/.dockerignore
index 2640be1..cb9f075 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -38,3 +38,7 @@ tests/*.out
tests/*.exp
tests/*.log
tests/*.sh
+*.dep
+*~
+tags
+configure.ac
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index bfc4cd0..6b5371e 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -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:
diff --git a/.gitignore b/.gitignore
index f1f7125..9aea666 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,7 @@ tests/*.php
tests/*.exp
tests/*.log
tests/*.sh
+*.dep
+*~
+tags
+configure.ac
diff --git a/README.md b/README.md
index 06d6233..7ecc17a 100644
--- a/README.md
+++ b/README.md
@@ -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
@@ -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
@@ -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
@@ -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).
diff --git a/ast.c b/ast.c
index 9a89f0b..e372d75 100644
--- a/ast.c
+++ b/ast.c
@@ -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
@@ -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;
@@ -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 },
@@ -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;
}
@@ -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;
@@ -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);
@@ -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
@@ -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
@@ -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);
@@ -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) {
@@ -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
@@ -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
diff --git a/ast_data.c b/ast_data.c
index f2b7fa8..3d19b2f 100644
--- a/ast_data.c
+++ b/ast_data.c
@@ -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,
@@ -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,
@@ -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";
@@ -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";
@@ -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);
@@ -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);
}
@@ -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:
@@ -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;
}
@@ -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);
@@ -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);
diff --git a/ast_str_defs.h b/ast_str_defs.h
index dac5ff0..559dcb1 100644
--- a/ast_str_defs.h
+++ b/ast_str_defs.h
@@ -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
diff --git a/ast_stub.php b/ast_stub.php
index 095705b..3a6cfcd 100644
--- a/ast_stub.php
+++ b/ast_stub.php
@@ -69,7 +69,7 @@
const AST_BREAK = 286;
const AST_CONTINUE = 287;
const AST_CLASS_NAME = 276;
-const AST_CLASS_CONST_GROUP = 546;
+const AST_CLASS_CONST_GROUP = 777;
const AST_DIM = 512;
const AST_PROP = 513;
const AST_NULLSAFE_PROP = 514;
@@ -101,10 +101,10 @@
const AST_USE_ELEM = 543;
const AST_TRAIT_ALIAS = 544;
const AST_GROUP_USE = 545;
-const AST_ATTRIBUTE = 547;
-const AST_MATCH = 548;
-const AST_MATCH_ARM = 549;
-const AST_NAMED_ARG = 550;
+const AST_ATTRIBUTE = 546;
+const AST_MATCH = 547;
+const AST_MATCH_ARM = 548;
+const AST_NAMED_ARG = 549;
const AST_METHOD_CALL = 768;
const AST_NULLSAFE_METHOD_CALL = 769;
const AST_STATIC_CALL = 770;
diff --git a/package.xml b/package.xml
index 58cd8c1..537e52a 100644
--- a/package.xml
+++ b/package.xml
@@ -18,10 +18,10 @@
tandre@php.net
yes
- 2023-11-12
+ 2024-08-10
- 1.1.2dev
- 1.1.2dev
+ 1.1.2
+ 1.1.2
stable
@@ -29,7 +29,9 @@
BSD-3-Clause
-- TBD
+- Fix compilation errors in PHP 8.4.
+- In version 110, `AST_PARAM` and `AST_PROP_ELEM` nodes have a `hooks` node for property hooks (https://wiki.php.net/rfc/property-hooks)
+- In version 110, `AST_CLOSURE` and `AST_ARROW_FUNC` nodes have no `name`.
@@ -112,17 +114,23 @@
-
+
+
+
+
+
+
+
-
+
diff --git a/php_ast.h b/php_ast.h
index d326e1b..30d8af8 100644
--- a/php_ast.h
+++ b/php_ast.h
@@ -7,7 +7,7 @@
extern zend_module_entry ast_module_entry;
#define phpext_ast_ptr &ast_module_entry
-#define PHP_AST_VERSION "1.1.2dev"
+#define PHP_AST_VERSION "1.1.2"
#ifdef PHP_WIN32
# define PHP_AST_API __declspec(dllexport)
@@ -30,7 +30,7 @@ ZEND_EXTERN_MODULE_GLOBALS(ast)
#define AST_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(ast, v)
typedef struct _ast_str_globals {
-#define X(str) zend_string *str_ ## str;
+#define X(str, value) zend_string *str_ ## str;
AST_STR_DEFS
#undef X
} ast_str_globals;
@@ -49,6 +49,7 @@ extern ast_str_globals str_globals;
// NOTE: The first hex digit is the number of child nodes a given kind has
# define ZEND_AST_CLASS_NAME 0x1ff
# define ZEND_AST_PROP_GROUP 0x2ff
+// ZEND_AST_ARROW_FUNC technically should have been in the ZEND_AST_SPECIAL_SHIFT group, but keeping this value for compatibility with older releases. (e.g. serialized ast Nodes)
# define ZEND_AST_ARROW_FUNC 0x5ff
#endif
@@ -85,6 +86,13 @@ extern ast_str_globals str_globals;
# define ZEND_ACC_READONLY_CLASS (1 << 23)
#endif
+#if PHP_VERSION_ID < 80400
+# define ZEND_AST_PROPERTY_HOOK ((1 << (ZEND_AST_SPECIAL_SHIFT + 1)) - 1)
+// NOTE: The first hex digit is the number of child nodes a given kind has
+# define ZEND_AST_PROPERTY_HOOK_SHORT_BODY 0x1fe
+# define ZEND_AST_PARENT_PROPERTY_HOOK_CALL 0x2f8
+#endif
+
/* Pretend it still exists */
# define ZEND_AST_LIST ((1 << (ZEND_AST_IS_LIST_SHIFT + 1)) - 1)
diff --git a/scripts/generate_ast_data.php b/scripts/generate_ast_data.php
index 1629b3a..b68dfa2 100644
--- a/scripts/generate_ast_data.php
+++ b/scripts/generate_ast_data.php
@@ -59,6 +59,7 @@
'ZEND_AST_METHOD' => $funcNames,
'ZEND_AST_ARROW_FUNC' => $funcNames,
'ZEND_AST_CLASS' => ['extends', 'implements', 'stmts', 'attributes', 'type'],
+ 'ZEND_AST_PROPERTY_HOOK' => $funcNames, // only params/stmts are used.
/* 0 child nodes */
'ZEND_AST_MAGIC_CONST' => [],
@@ -96,6 +97,7 @@
'ZEND_AST_BREAK' => ['depth'],
'ZEND_AST_CONTINUE' => ['depth'],
'ZEND_AST_CLASS_NAME' => ['class'],
+ 'ZEND_AST_PROPERTY_HOOK_SHORT_BODY' => ['expr'],
/* 2 child nodes */
'ZEND_AST_CLASS_CONST_GROUP' => ['const', 'attributes', 'type'],
@@ -121,7 +123,7 @@
'ZEND_AST_SWITCH' => ['cond', 'stmts'],
'ZEND_AST_SWITCH_CASE' => ['cond', 'stmts'],
'ZEND_AST_DECLARE' => ['declares', 'stmts'],
- 'ZEND_AST_PROP_ELEM' => ['name', 'default', 'docComment'],
+ 'ZEND_AST_PROP_ELEM' => ['name', 'default', 'docComment', 'hooks'],
'ZEND_AST_PROP_GROUP' => ['type', 'props', 'attributes'],
'ZEND_AST_CONST_ELEM' => ['name', 'value', 'docComment'],
'ZEND_AST_USE_TRAIT' => ['traits', 'adaptations'],
@@ -150,8 +152,8 @@
'ZEND_AST_FOREACH' => ['expr', 'value', 'key', 'stmts'],
'ZEND_AST_ENUM_CASE' => ['name', 'expr', 'docComment', 'attributes'],
- /* 5 child nodes */
- 'ZEND_AST_PARAM' => ['type', 'name', 'default', 'attributes', 'docComment'],
+ /* 6 child nodes */
+ 'ZEND_AST_PARAM' => ['type', 'name', 'default', 'attributes', 'docComment', 'hooks'],
];
$listNodes = [
@@ -265,8 +267,9 @@
$strings = get_possible_strings($names);
$strDefs = [];
foreach ($strings as $name) {
- $strDefs[] .= "\tX($name) \\";
+ $strDefs[] = "\tX($name, \"$name\") \\";
}
+$strDefs[] = "\tX(bracketed_closure, \"{closure}\") \\";
$strDefsHeader = str_replace('{STR_DEFS}', implode("\n", $strDefs), $strDefsHeader);
file_put_contents($strDefsFile, $strDefsHeader);
diff --git a/tests/get_supported_versions.phpt b/tests/get_supported_versions.phpt
index 57e482a..0f1b787 100644
--- a/tests/get_supported_versions.phpt
+++ b/tests/get_supported_versions.phpt
@@ -8,7 +8,7 @@ var_dump(ast\get_supported_versions(true));
?>
--EXPECT--
-array(7) {
+array(8) {
[0]=>
int(50)
[1]=>
@@ -23,8 +23,10 @@ array(7) {
int(90)
[6]=>
int(100)
+ [7]=>
+ int(110)
}
-array(5) {
+array(6) {
[0]=>
int(70)
[1]=>
@@ -35,4 +37,6 @@ array(5) {
int(90)
[4]=>
int(100)
+ [5]=>
+ int(110)
}
diff --git a/tests/metadata.phpt b/tests/metadata.phpt
index a3363da..615e151 100644
--- a/tests/metadata.phpt
+++ b/tests/metadata.phpt
@@ -56,6 +56,7 @@ AST_CLOSURE: (combinable) [MODIFIER_PUBLIC, MODIFIER_PROTECTED, MODIFIER_PRIVATE
AST_METHOD: (combinable) [MODIFIER_PUBLIC, MODIFIER_PROTECTED, MODIFIER_PRIVATE, MODIFIER_STATIC, MODIFIER_ABSTRACT, MODIFIER_FINAL, MODIFIER_READONLY, FUNC_RETURNS_REF, FUNC_GENERATOR]
AST_ARROW_FUNC: (combinable) [MODIFIER_PUBLIC, MODIFIER_PROTECTED, MODIFIER_PRIVATE, MODIFIER_STATIC, MODIFIER_ABSTRACT, MODIFIER_FINAL, MODIFIER_READONLY, FUNC_RETURNS_REF, FUNC_GENERATOR]
AST_CLASS: (combinable) [CLASS_ABSTRACT, CLASS_FINAL, CLASS_TRAIT, CLASS_INTERFACE, CLASS_ANONYMOUS, CLASS_ENUM, CLASS_READONLY]
+AST_PROPERTY_HOOK: (combinable) [MODIFIER_PUBLIC, MODIFIER_PROTECTED, MODIFIER_PRIVATE, MODIFIER_STATIC, MODIFIER_ABSTRACT, MODIFIER_FINAL, MODIFIER_READONLY, FUNC_RETURNS_REF, FUNC_GENERATOR]
AST_MAGIC_CONST: [MAGIC_LINE, MAGIC_FILE, MAGIC_DIR, MAGIC_NAMESPACE, MAGIC_FUNCTION, MAGIC_METHOD, MAGIC_CLASS, MAGIC_TRAIT]
AST_TYPE: [TYPE_NULL, TYPE_FALSE, TYPE_TRUE, TYPE_BOOL, TYPE_LONG, TYPE_DOUBLE, TYPE_STRING, TYPE_ARRAY, TYPE_OBJECT, TYPE_CALLABLE, TYPE_VOID, TYPE_ITERABLE, TYPE_STATIC, TYPE_MIXED, TYPE_NEVER]
AST_CALLABLE_CONVERT: []
@@ -88,6 +89,7 @@ AST_GOTO: []
AST_BREAK: []
AST_CONTINUE: []
AST_CLASS_NAME: []
+AST_PROPERTY_HOOK_SHORT_BODY: []
AST_CLASS_CONST_GROUP: (combinable) [MODIFIER_PUBLIC, MODIFIER_PROTECTED, MODIFIER_PRIVATE, MODIFIER_STATIC, MODIFIER_ABSTRACT, MODIFIER_FINAL, MODIFIER_READONLY]
AST_DIM: (combinable) [DIM_ALTERNATIVE_SYNTAX, ENCAPS_VAR_DOLLAR_CURLY]
AST_PROP: []
@@ -133,4 +135,4 @@ AST_CATCH: []
AST_FOR: []
AST_FOREACH: []
AST_ENUM_CASE: []
-AST_PARAM: (combinable) [PARAM_REF, PARAM_VARIADIC, PARAM_MODIFIER_PUBLIC, PARAM_MODIFIER_PROTECTED, PARAM_MODIFIER_PRIVATE]
\ No newline at end of file
+AST_PARAM: (combinable) [PARAM_REF, PARAM_VARIADIC, PARAM_MODIFIER_PUBLIC, PARAM_MODIFIER_PROTECTED, PARAM_MODIFIER_PRIVATE]
diff --git a/tests/php74_dim_alternative_syntax.phpt b/tests/php74_dim_alternative_syntax.phpt
index 2d7bc56..b3c8c77 100644
--- a/tests/php74_dim_alternative_syntax.phpt
+++ b/tests/php74_dim_alternative_syntax.phpt
@@ -1,7 +1,7 @@
--TEST--
'$x{"offset"}' flag in PHP 7.4
--SKIPIF--
-= 7.4 only'); ?>
+= 80400) die('skip PHP 7.4-8.3 only'); ?>
--FILE--
= 8.2 only'); ?>
+= 80400) die('skip PHP >= 8.2 < 8.4 only'); ?>
--FILE--
= 8.2 only'); ?>
+--FILE--
+c}{$d}${$e["f"]}${g[\'h\']}{$i[\'j\']}";';
+$node = ast\parse_code($code, $version=85);
+echo ast_dump($node), "\n";
+--EXPECTF--
+AST_STMT_LIST
+ 0: AST_ENCAPS_LIST
+ 0: AST_VAR
+ flags: ENCAPS_VAR_DOLLAR_CURLY (%d)
+ name: "a"
+ 1: AST_VAR
+ flags: ENCAPS_VAR_DOLLAR_CURLY_VAR_VAR (%d)
+ name: AST_PROP
+ expr: AST_CONST
+ name: AST_NAME
+ flags: NAME_NOT_FQ (%d)
+ name: "b"
+ prop: "c"
+ 2: AST_VAR
+ name: "d"
+ 3: AST_VAR
+ flags: ENCAPS_VAR_DOLLAR_CURLY_VAR_VAR (%d)
+ name: AST_DIM
+ expr: AST_VAR
+ name: "e"
+ dim: "f"
+ 4: AST_DIM
+ flags: ENCAPS_VAR_DOLLAR_CURLY (%d)
+ expr: AST_VAR
+ name: "g"
+ dim: "h"
+ 5: AST_DIM
+ expr: AST_VAR
+ name: "i"
+ dim: "j"
diff --git a/tests/php84_dim_alternative_syntax.phpt b/tests/php84_dim_alternative_syntax.phpt
new file mode 100644
index 0000000..6409457
--- /dev/null
+++ b/tests/php84_dim_alternative_syntax.phpt
@@ -0,0 +1,23 @@
+--TEST--
+'$x{"offset"}' flag in PHP 8.4 is a Parse error
+--SKIPIF--
+= 8.4 only'); ?>
+--FILE--
+getMessage(), "\n";
+}
+?>
+--EXPECT--
+Caught: syntax error, unexpected token "{", expecting ")"
diff --git a/tests/php84_property_hook.phpt b/tests/php84_property_hook.phpt
new file mode 100644
index 0000000..cff36d8
--- /dev/null
+++ b/tests/php84_property_hook.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Property hooks in php 8.4
+--SKIPIF--
+=8.4 only'); ?>
+--FILE--
+=8.4 only'); ?>
+--FILE--
+name = $newName;
+ }
+ }
+}
+PHP;
+
+$node = ast\parse_code($code, $version=90);
+echo ast_dump($node), "\n";
+$node = ast\parse_code($code, $version=110);
+echo ast_dump($node), "\n";
+--EXPECTF--
+AST_STMT_LIST
+ 0: AST_CLASS
+ name: "A"
+ docComment: null
+ extends: null
+ implements: null
+ stmts: AST_STMT_LIST
+ 0: AST_PROP_GROUP
+ flags: MODIFIER_PUBLIC (1)
+ type: AST_TYPE
+ flags: TYPE_STRING (6)
+ props: AST_PROP_DECL
+ 0: AST_PROP_ELEM
+ name: "normal"
+ default: null
+ docComment: null
+ attributes: null
+ 1: AST_PROP_GROUP
+ flags: MODIFIER_PUBLIC (1)
+ type: AST_TYPE
+ flags: TYPE_STRING (6)
+ props: AST_PROP_DECL
+ 0: AST_PROP_ELEM
+ name: "name"
+ default: null
+ docComment: null
+ attributes: null
+ attributes: null
+ type: null
+ __declId: 0
+AST_STMT_LIST
+ 0: AST_CLASS
+ name: "A"
+ docComment: null
+ extends: null
+ implements: null
+ stmts: AST_STMT_LIST
+ 0: AST_PROP_GROUP
+ flags: MODIFIER_PUBLIC (1)
+ type: AST_TYPE
+ flags: TYPE_STRING (6)
+ props: AST_PROP_DECL
+ 0: AST_PROP_ELEM
+ name: "normal"
+ default: null
+ docComment: null
+ hooks: null
+ attributes: null
+ 1: AST_PROP_GROUP
+ flags: MODIFIER_PUBLIC (1)
+ type: AST_TYPE
+ flags: TYPE_STRING (6)
+ props: AST_PROP_DECL
+ 0: AST_PROP_ELEM
+ name: "name"
+ default: null
+ docComment: null
+ hooks: AST_STMT_LIST
+ 0: AST_PROPERTY_HOOK
+ name: "set"
+ docComment: null
+ params: AST_PARAM_LIST
+ 0: AST_PARAM
+ type: AST_TYPE
+ flags: TYPE_STRING (6)
+ name: "newName"
+ default: null
+ attributes: null
+ docComment: null
+ hooks: null
+ stmts: AST_STMT_LIST
+ 0: AST_ASSIGN
+ var: AST_PROP
+ expr: AST_VAR
+ name: "this"
+ prop: "name"
+ expr: AST_VAR
+ name: "newName"
+ attributes: null
+ __declId: 0
+ attributes: null
+ attributes: null
+ type: null
+ __declId: 1
diff --git a/tests/php84_property_hook3.phpt b/tests/php84_property_hook3.phpt
new file mode 100644
index 0000000..61b442f
--- /dev/null
+++ b/tests/php84_property_hook3.phpt
@@ -0,0 +1,60 @@
+--TEST--
+Property hooks in php 8.4 bodies flags
+--SKIPIF--
+=8.4 only'); ?>
+--FILE--
+=8.4 only'); ?>
+--FILE--
+ strtolower($value); }
+ ) {}
+}
+PHP;
+$node = ast\parse_code($code, $version=110);
+echo ast_dump($node), "\n";
+--EXPECTF--
+AST_STMT_LIST
+ 0: AST_CLASS
+ name: "User"
+ docComment: null
+ extends: null
+ implements: null
+ stmts: AST_STMT_LIST
+ 0: AST_METHOD
+ flags: MODIFIER_PUBLIC (%d)
+ name: "__construct"
+ docComment: null
+ params: AST_PARAM_LIST
+ 0: AST_PARAM
+ flags: PARAM_MODIFIER_PUBLIC (%d)
+ type: AST_TYPE
+ flags: TYPE_STRING (%d)
+ name: "username"
+ default: null
+ attributes: null
+ docComment: null
+ hooks: AST_STMT_LIST
+ 0: AST_PROPERTY_HOOK
+ name: "set"
+ docComment: null
+ params: null
+ stmts: AST_PROPERTY_HOOK_SHORT_BODY
+ expr: AST_CALL
+ expr: AST_NAME
+ flags: NAME_NOT_FQ (%d)
+ name: "strtolower"
+ args: AST_ARG_LIST
+ 0: AST_VAR
+ name: "value"
+ attributes: null
+ __declId: 0
+ stmts: AST_STMT_LIST
+ returnType: null
+ attributes: null
+ __declId: 1
+ attributes: null
+ type: null
+ __declId: 2
diff --git a/tests/prop_doc_comments.phpt b/tests/prop_doc_comments.phpt
index eff5ed1..6ec56f1 100644
--- a/tests/prop_doc_comments.phpt
+++ b/tests/prop_doc_comments.phpt
@@ -22,6 +22,7 @@ PHP;
echo ast_dump(ast\parse_code($code, $version=50)), "\n";
echo ast_dump(ast\parse_code($code, $version=80)), "\n";
+echo ast_dump(ast\parse_code($code, $version=110)), "\n";
?>
--EXPECTF--
@@ -80,4 +81,39 @@ AST_STMT_LIST
docComment: "/** docComment $c */"
attributes: null
attributes: null
- __declId: 0
\ No newline at end of file
+ __declId: 0
+AST_STMT_LIST
+ 0: AST_CLASS
+ name: "A"
+ docComment: null
+ extends: null
+ implements: null
+ stmts: AST_STMT_LIST
+ 0: AST_PROP_GROUP
+ flags: MODIFIER_PUBLIC (%d)
+ type: null
+ props: AST_PROP_DECL
+ 0: AST_PROP_ELEM
+ name: "a"
+ default: null
+ docComment: "/** docComment $a */"
+ hooks: null
+ attributes: null
+ 1: AST_PROP_GROUP
+ flags: MODIFIER_PUBLIC (%d)
+ type: null
+ props: AST_PROP_DECL
+ 0: AST_PROP_ELEM
+ name: "b"
+ default: null
+ docComment: "/** docComment $b */"
+ hooks: null
+ 1: AST_PROP_ELEM
+ name: "c"
+ default: null
+ docComment: "/** docComment $c */"
+ hooks: null
+ attributes: null
+ attributes: null
+ type: null
+ __declId: 0