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