From a7c7d9180edf664bf678df583bf69676e33ecd69 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 24 Mar 2020 13:18:28 +0100 Subject: [PATCH 1/8] Implement "Constructor Promotion" --- Zend/tests/ctor_promotion_abstract.phpt | 12 +++ .../ctor_promotion_additional_modifiers.phpt | 12 +++ Zend/tests/ctor_promotion_basic.phpt | 21 ++++++ Zend/tests/ctor_promotion_by_ref.phpt | 20 +++++ Zend/tests/ctor_promotion_callable_type.phpt | 12 +++ Zend/tests/ctor_promotion_defaults.phpt | 43 +++++++++++ Zend/tests/ctor_promotion_free_function.phpt | 10 +++ Zend/tests/ctor_promotion_interface.phpt | 12 +++ Zend/tests/ctor_promotion_mixing.phpt | 54 ++++++++++++++ Zend/tests/ctor_promotion_not_a_ctor.phpt | 12 +++ Zend/tests/ctor_promotion_null_default.phpt | 12 +++ Zend/tests/ctor_promotion_repeated_prop.phpt | 14 ++++ Zend/tests/ctor_promotion_trait.phpt | 21 ++++++ Zend/tests/ctor_promotion_variadic.phpt | 12 +++ Zend/zend_compile.c | 74 ++++++++++++++++++- Zend/zend_compile.h | 5 +- Zend/zend_language_parser.y | 19 +++-- 17 files changed, 355 insertions(+), 10 deletions(-) create mode 100644 Zend/tests/ctor_promotion_abstract.phpt create mode 100644 Zend/tests/ctor_promotion_additional_modifiers.phpt create mode 100644 Zend/tests/ctor_promotion_basic.phpt create mode 100644 Zend/tests/ctor_promotion_by_ref.phpt create mode 100644 Zend/tests/ctor_promotion_callable_type.phpt create mode 100644 Zend/tests/ctor_promotion_defaults.phpt create mode 100644 Zend/tests/ctor_promotion_free_function.phpt create mode 100644 Zend/tests/ctor_promotion_interface.phpt create mode 100644 Zend/tests/ctor_promotion_mixing.phpt create mode 100644 Zend/tests/ctor_promotion_not_a_ctor.phpt create mode 100644 Zend/tests/ctor_promotion_null_default.phpt create mode 100644 Zend/tests/ctor_promotion_repeated_prop.phpt create mode 100644 Zend/tests/ctor_promotion_trait.phpt create mode 100644 Zend/tests/ctor_promotion_variadic.phpt diff --git a/Zend/tests/ctor_promotion_abstract.phpt b/Zend/tests/ctor_promotion_abstract.phpt new file mode 100644 index 0000000000000..937e247acde2d --- /dev/null +++ b/Zend/tests/ctor_promotion_abstract.phpt @@ -0,0 +1,12 @@ +--TEST-- +Constructor promotion cannot be used inside an abstract constructor +--FILE-- + +--EXPECTF-- +Fatal error: Cannot declare promoted property in an abstract constructor in %s on line %d diff --git a/Zend/tests/ctor_promotion_additional_modifiers.phpt b/Zend/tests/ctor_promotion_additional_modifiers.phpt new file mode 100644 index 0000000000000..fb3b092d66f50 --- /dev/null +++ b/Zend/tests/ctor_promotion_additional_modifiers.phpt @@ -0,0 +1,12 @@ +--TEST-- +Constructor promotion only permits visibility modifiers +--FILE-- + +--EXPECTF-- +Parse error: syntax error, unexpected 'static' (T_STATIC), expecting variable (T_VARIABLE) in %s on line %d diff --git a/Zend/tests/ctor_promotion_basic.phpt b/Zend/tests/ctor_promotion_basic.phpt new file mode 100644 index 0000000000000..206f99fd40942 --- /dev/null +++ b/Zend/tests/ctor_promotion_basic.phpt @@ -0,0 +1,21 @@ +--TEST-- +Constructor promotion (basic example) +--FILE-- +x = "foo"; +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Cannot assign string to property Point::$x of type int diff --git a/Zend/tests/ctor_promotion_by_ref.phpt b/Zend/tests/ctor_promotion_by_ref.phpt new file mode 100644 index 0000000000000..4b07149c579ef --- /dev/null +++ b/Zend/tests/ctor_promotion_by_ref.phpt @@ -0,0 +1,20 @@ +--TEST-- +Constructor promotion of by-ref parameter +--FILE-- +array); + +?> +--EXPECT-- +array(1) { + [0]=> + int(42) +} diff --git a/Zend/tests/ctor_promotion_callable_type.phpt b/Zend/tests/ctor_promotion_callable_type.phpt new file mode 100644 index 0000000000000..ae10512ad54a3 --- /dev/null +++ b/Zend/tests/ctor_promotion_callable_type.phpt @@ -0,0 +1,12 @@ +--TEST-- +Type of promoted property may not be callable +--FILE-- + +--EXPECTF-- +Fatal error: Property Test::$callable cannot have type callable in %s on line %d diff --git a/Zend/tests/ctor_promotion_defaults.phpt b/Zend/tests/ctor_promotion_defaults.phpt new file mode 100644 index 0000000000000..9999e8a539ce8 --- /dev/null +++ b/Zend/tests/ctor_promotion_defaults.phpt @@ -0,0 +1,43 @@ +--TEST-- +Constructor promotion with default values +--FILE-- + +--EXPECT-- +object(Point)#1 (3) { + ["x"]=> + float(10) + ["y"]=> + float(1) + ["z"]=> + float(2) +} +object(Point)#1 (3) { + ["x"]=> + float(10) + ["y"]=> + float(11) + ["z"]=> + float(2) +} +object(Point)#1 (3) { + ["x"]=> + float(10) + ["y"]=> + float(11) + ["z"]=> + float(12) +} diff --git a/Zend/tests/ctor_promotion_free_function.phpt b/Zend/tests/ctor_promotion_free_function.phpt new file mode 100644 index 0000000000000..1eb84c5517797 --- /dev/null +++ b/Zend/tests/ctor_promotion_free_function.phpt @@ -0,0 +1,10 @@ +--TEST-- +Constructor promotion cannot be used in a free function +--FILE-- + +--EXPECTF-- +Fatal error: Cannot declare promoted property outside a constructor in %s on line %d diff --git a/Zend/tests/ctor_promotion_interface.phpt b/Zend/tests/ctor_promotion_interface.phpt new file mode 100644 index 0000000000000..7cc856b93a3af --- /dev/null +++ b/Zend/tests/ctor_promotion_interface.phpt @@ -0,0 +1,12 @@ +--TEST-- +Constructor promotion cannot be used inside an abstract constructor (interface variant) +--FILE-- + +--EXPECTF-- +Fatal error: Cannot declare promoted property in an abstract constructor in %s on line %d diff --git a/Zend/tests/ctor_promotion_mixing.phpt b/Zend/tests/ctor_promotion_mixing.phpt new file mode 100644 index 0000000000000..cdaa718394d10 --- /dev/null +++ b/Zend/tests/ctor_promotion_mixing.phpt @@ -0,0 +1,54 @@ +--TEST-- +Constructor promotiong mixed with other properties, parameters and code +--FILE-- +prop2 = $prop1 . $param2; + } +} + +var_dump(new Test("Foo", "Bar")); +echo "\n"; +echo new ReflectionClass(Test::class), "\n"; + +?> +--EXPECTF-- +object(Test)#1 (2) { + ["prop2"]=> + string(6) "FooBar" + ["prop1"]=> + string(3) "Foo" +} + +Class [ class Test ] { + @@ %s + + - Constants [0] { + } + + - Static properties [0] { + } + + - Static methods [0] { + } + + - Properties [2] { + Property [ public $prop2 ] + Property [ public $prop1 ] + } + + - Methods [1] { + Method [ public method __construct ] { + @@ %s + + - Parameters [2] { + Parameter #0 [ string $prop1 = '' ] + Parameter #1 [ $param2 = '' ] + } + } + } +} diff --git a/Zend/tests/ctor_promotion_not_a_ctor.phpt b/Zend/tests/ctor_promotion_not_a_ctor.phpt new file mode 100644 index 0000000000000..110ee8a5ce756 --- /dev/null +++ b/Zend/tests/ctor_promotion_not_a_ctor.phpt @@ -0,0 +1,12 @@ +--TEST-- +Constructor promotion can only be used in constructors ... duh +--FILE-- + +--EXPECTF-- +Fatal error: Cannot declare promoted property outside a constructor in %s on line %d diff --git a/Zend/tests/ctor_promotion_null_default.phpt b/Zend/tests/ctor_promotion_null_default.phpt new file mode 100644 index 0000000000000..61c7b2d10c04b --- /dev/null +++ b/Zend/tests/ctor_promotion_null_default.phpt @@ -0,0 +1,12 @@ +--TEST-- +Constructor promotion with null default, requires an explicitly nullable type +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use null as default value for parameter $x of type int in %s on line %d diff --git a/Zend/tests/ctor_promotion_repeated_prop.phpt b/Zend/tests/ctor_promotion_repeated_prop.phpt new file mode 100644 index 0000000000000..6f4a9ffd02d1e --- /dev/null +++ b/Zend/tests/ctor_promotion_repeated_prop.phpt @@ -0,0 +1,14 @@ +--TEST-- +Clash between promoted and explicit property +--FILE-- + +--EXPECTF-- +Fatal error: Cannot redeclare Test::$prop in %s on line %d diff --git a/Zend/tests/ctor_promotion_trait.phpt b/Zend/tests/ctor_promotion_trait.phpt new file mode 100644 index 0000000000000..4c109157ae7ee --- /dev/null +++ b/Zend/tests/ctor_promotion_trait.phpt @@ -0,0 +1,21 @@ +--TEST-- +Constructor promotion can be used inside a trait +--FILE-- + +--EXPECT-- +object(Test2)#1 (1) { + ["prop"]=> + int(42) +} diff --git a/Zend/tests/ctor_promotion_variadic.phpt b/Zend/tests/ctor_promotion_variadic.phpt new file mode 100644 index 0000000000000..7e1e81cad832d --- /dev/null +++ b/Zend/tests/ctor_promotion_variadic.phpt @@ -0,0 +1,12 @@ +--TEST-- +Cannot use constructor promotion with variadic parameter +--FILE-- + +--EXPECTF-- +Fatal error: Cannot declare variadic promoted property in %s on line %d diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 707d2ac9da319..0a760ede07f63 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5790,6 +5790,8 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall zend_string *name = zval_make_interned_string(zend_ast_get_zval(var_ast)); zend_bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0; zend_bool is_variadic = (param_ast->attr & ZEND_PARAM_VARIADIC) != 0; + uint32_t visibility = + param_ast->attr & (ZEND_ACC_PUBLIC|ZEND_ACC_PROTECTED|ZEND_ACC_PRIVATE); znode var_node, default_node; zend_uchar opcode; @@ -5862,16 +5864,16 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall if (type_ast) { uint32_t default_type = default_ast ? Z_TYPE(default_node.u.constant) : IS_UNDEF; + zend_bool force_nullable = default_type == IS_NULL && !visibility; op_array->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS; - arg_info->type = zend_compile_typename( - type_ast, default_type == IS_NULL, /* use_arena */ 0); + arg_info->type = zend_compile_typename(type_ast, force_nullable, /* use_arena */ 0); if (ZEND_TYPE_FULL_MASK(arg_info->type) & MAY_BE_VOID) { zend_error_noreturn(E_COMPILE_ERROR, "void cannot be used as a parameter type"); } - if (default_type > IS_NULL && default_type != IS_CONSTANT_AST + if (default_type != IS_UNDEF && default_type != IS_CONSTANT_AST && !force_nullable && !zend_is_valid_default_value(arg_info->type, &default_node.u.constant)) { zend_string *type_str = zend_type_to_string(arg_info->type); zend_error_noreturn(E_COMPILE_ERROR, @@ -5896,6 +5898,49 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall opline->op2.num = type_ast ? ZEND_TYPE_FULL_MASK(arg_info->type) : MAY_BE_ANY; } + + if (visibility) { + zend_op_array *op_array = CG(active_op_array); + zend_class_entry *scope = op_array->scope; + zend_bool is_ctor = + scope && zend_string_equals_literal_ci(op_array->function_name, "__construct"); + if (!is_ctor) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot declare promoted property outside a constructor"); + } + if ((op_array->fn_flags & ZEND_ACC_ABSTRACT) + || (scope->ce_flags & ZEND_ACC_INTERFACE)) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot declare promoted property in an abstract constructor"); + } + if (is_variadic) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot declare variadic promoted property"); + } + if (zend_hash_exists(&scope->properties_info, name)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot redeclare %s::$%s", + ZSTR_VAL(scope->name), ZSTR_VAL(name)); + } + if (ZEND_TYPE_FULL_MASK(arg_info->type) & MAY_BE_CALLABLE) { + zend_string *str = zend_type_to_string(arg_info->type); + zend_error_noreturn(E_COMPILE_ERROR, + "Property %s::$%s cannot have type %s", + ZSTR_VAL(scope->name), ZSTR_VAL(name), ZSTR_VAL(str)); + } + + /* Always use uninitialized as the default. */ + zval default_value; + ZVAL_UNDEF(&default_value); + + /* Recompile the type, as it has different memory management requirements. */ + zend_type type = ZEND_TYPE_INIT_NONE(0); + if (type_ast) { + type = zend_compile_typename(type_ast, /* force_allow_null */ 0, /* use_arena */ 1); + } + + zend_declare_typed_property( + scope, name, &default_value, visibility, /* doc_comment */ NULL, type); + } } /* These are assigned at the end to avoid uninitialized memory in case of an error */ @@ -5907,6 +5952,29 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall op_array->num_args--; } zend_set_function_arg_flags((zend_function*)op_array); + + for (i = 0; i < list->children; i++) { + zend_ast *param_ast = list->child[i]; + zend_bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0; + uint32_t visibility = + param_ast->attr & (ZEND_ACC_PUBLIC|ZEND_ACC_PROTECTED|ZEND_ACC_PRIVATE); + if (!visibility) { + continue; + } + + /* Emit $this->prop = $prop for promoted properties. */ + zend_string *name = zend_ast_get_str(param_ast->child[1]); + znode name_node, value_node; + name_node.op_type = IS_CONST; + ZVAL_STR_COPY(&name_node.u.constant, name); + value_node.op_type = IS_CV; + value_node.u.op.var = lookup_cv(name); + + zend_op *opline = zend_emit_op(NULL, + is_ref ? ZEND_ASSIGN_OBJ_REF : ZEND_ASSIGN_OBJ, NULL, &name_node); + opline->extended_value = zend_alloc_cache_slots(3); + zend_emit_op_data(&value_node); + } } /* }}} */ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 1a53f92e9e6eb..1b161b97572dc 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -873,8 +873,9 @@ ZEND_API zend_string *zend_type_to_string(zend_type type); #define ZEND_FETCH_CLASS_ALLOW_UNLINKED 0x0400 #define ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED 0x0800 -#define ZEND_PARAM_REF (1<<0) -#define ZEND_PARAM_VARIADIC (1<<1) +/* These should not clash with ZEND_ACC_(PUBLIC|PROTECTED|PRIVATE) */ +#define ZEND_PARAM_REF (1<<3) +#define ZEND_PARAM_VARIADIC (1<<4) #define ZEND_NAME_FQ 0 #define ZEND_NAME_NOT_FQ 1 diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index d8d66553c1335..cd6d9a87db59f 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -262,7 +262,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type attribute_arguments attribute_decl attribute attributes %type returns_ref function fn is_reference is_variadic variable_modifiers -%type method_modifiers non_empty_member_modifiers member_modifier +%type method_modifiers non_empty_member_modifiers member_modifier optional_visibility_modifier %type class_modifiers class_modifier use_type backup_fn_flags %type backup_lex_pos @@ -684,11 +684,20 @@ attributed_parameter: | parameter { $$ = $1; } ; +optional_visibility_modifier: + %empty { $$ = 0; } + | T_PUBLIC { $$ = ZEND_ACC_PUBLIC; } + | T_PROTECTED { $$ = ZEND_ACC_PROTECTED; } + | T_PRIVATE { $$ = ZEND_ACC_PRIVATE; } +; + parameter: - optional_type_without_static is_reference is_variadic T_VARIABLE - { $$ = zend_ast_create_ex(ZEND_AST_PARAM, $2 | $3, $1, $4, NULL, NULL); } - | optional_type_without_static is_reference is_variadic T_VARIABLE '=' expr - { $$ = zend_ast_create_ex(ZEND_AST_PARAM, $2 | $3, $1, $4, $6, NULL); } + optional_visibility_modifier optional_type_without_static + is_reference is_variadic T_VARIABLE + { $$ = zend_ast_create_ex(ZEND_AST_PARAM, $1 | $3 | $4, $2, $5, NULL); } + | optional_visibility_modifier optional_type_without_static + is_reference is_variadic T_VARIABLE '=' expr + { $$ = zend_ast_create_ex(ZEND_AST_PARAM, $1 | $3 | $4, $2, $5, $7); } ; From 72b1bcafdac2f7ee0423965043865c36b6adce5b Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 2 Apr 2020 10:59:12 +0200 Subject: [PATCH 2/8] Add reflection test The annotation is missing. --- .../tests/constructor_promotion.phpt | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 ext/reflection/tests/constructor_promotion.phpt diff --git a/ext/reflection/tests/constructor_promotion.phpt b/ext/reflection/tests/constructor_promotion.phpt new file mode 100644 index 0000000000000..05cf83177cd75 --- /dev/null +++ b/ext/reflection/tests/constructor_promotion.phpt @@ -0,0 +1,51 @@ +--TEST-- +Using Reflection on promoted properties +--FILE-- +getProperty('y'); +var_dump($y->getDocComment()); + +?> +--EXPECTF-- +Class [ class Test ] { + @@ %s 3-9 + + - Constants [0] { + } + + - Static properties [0] { + } + + - Static methods [0] { + } + + - Properties [2] { + Property [ public int $x ] + Property [ public string $y ] + } + + - Methods [1] { + Method [ public method __construct ] { + @@ %s 4 - 8 + + - Parameters [2] { + Parameter #0 [ int $x ] + Parameter #1 [ string $y = '123' ] + } + } + } +} + +bool(false) From abc2cdfdcb59017ed7e74ec444878658415c0ac0 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 2 Apr 2020 11:40:41 +0200 Subject: [PATCH 3/8] Preserve doc comment on promoted property --- Zend/zend_ast.h | 4 +++- Zend/zend_compile.c | 5 ++++- Zend/zend_language_parser.y | 10 ++++++---- ext/reflection/tests/constructor_promotion.phpt | 2 +- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 0ec922dcea47f..ac178c874d22e 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -156,7 +156,9 @@ enum _zend_ast_kind { /* 4 child nodes */ ZEND_AST_FOR = 4 << ZEND_AST_NUM_CHILDREN_SHIFT, ZEND_AST_FOREACH, - ZEND_AST_PARAM, + + /* 5 child nodes */ + ZEND_AST_PARAM = 5 << ZEND_AST_NUM_CHILDREN_SHIFT, }; typedef uint16_t zend_ast_kind; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 0a760ede07f63..540a7f71b875e 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5787,6 +5787,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall zend_ast *var_ast = param_ast->child[1]; zend_ast *default_ast = param_ast->child[2]; zend_ast *attributes_ast = param_ast->child[3]; + zend_ast *doc_comment_ast = param_ast->child[4]; zend_string *name = zval_make_interned_string(zend_ast_get_zval(var_ast)); zend_bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0; zend_bool is_variadic = (param_ast->attr & ZEND_PARAM_VARIADIC) != 0; @@ -5938,8 +5939,10 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall type = zend_compile_typename(type_ast, /* force_allow_null */ 0, /* use_arena */ 1); } + zend_string *doc_comment = + doc_comment_ast ? zend_string_copy(zend_ast_get_str(doc_comment_ast)) : NULL; zend_declare_typed_property( - scope, name, &default_value, visibility, /* doc_comment */ NULL, type); + scope, name, &default_value, visibility, doc_comment, type); } } diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index cd6d9a87db59f..bddde6d1be1b3 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -693,11 +693,13 @@ optional_visibility_modifier: parameter: optional_visibility_modifier optional_type_without_static - is_reference is_variadic T_VARIABLE - { $$ = zend_ast_create_ex(ZEND_AST_PARAM, $1 | $3 | $4, $2, $5, NULL); } + is_reference is_variadic T_VARIABLE backup_doc_comment + { $$ = zend_ast_create_ex(ZEND_AST_PARAM, $1 | $3 | $4, $2, $5, NULL, + NULL, $6 ? zend_ast_create_zval_from_str($6) : NULL); } | optional_visibility_modifier optional_type_without_static - is_reference is_variadic T_VARIABLE '=' expr - { $$ = zend_ast_create_ex(ZEND_AST_PARAM, $1 | $3 | $4, $2, $5, $7); } + is_reference is_variadic T_VARIABLE backup_doc_comment '=' expr + { $$ = zend_ast_create_ex(ZEND_AST_PARAM, $1 | $3 | $4, $2, $5, $8, + NULL, $6 ? zend_ast_create_zval_from_str($6) : NULL); } ; diff --git a/ext/reflection/tests/constructor_promotion.phpt b/ext/reflection/tests/constructor_promotion.phpt index 05cf83177cd75..9f6035c584d32 100644 --- a/ext/reflection/tests/constructor_promotion.phpt +++ b/ext/reflection/tests/constructor_promotion.phpt @@ -48,4 +48,4 @@ Class [ class Test ] { } } -bool(false) +string(24) "/** @SomeAnnotation() */" From 6470db71498e605d766755596fe7c9481302845d Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 2 Apr 2020 12:02:58 +0200 Subject: [PATCH 4/8] Update test case for master change --- Zend/tests/ctor_promotion_mixing.phpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zend/tests/ctor_promotion_mixing.phpt b/Zend/tests/ctor_promotion_mixing.phpt index cdaa718394d10..a527654e793f5 100644 --- a/Zend/tests/ctor_promotion_mixing.phpt +++ b/Zend/tests/ctor_promotion_mixing.phpt @@ -37,8 +37,8 @@ Class [ class Test ] { } - Properties [2] { - Property [ public $prop2 ] - Property [ public $prop1 ] + Property [ public string $prop2 ] + Property [ public string $prop1 ] } - Methods [1] { From 1a9dfaf0629907049e2352797c58fbbfdb1ab5c1 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 5 Jun 2020 11:59:26 +0200 Subject: [PATCH 5/8] Fix doc block assignment for arrow functions --- Zend/zend_ast.c | 31 ++++++++++++++++++++++++++++++ Zend/zend_ast.h | 14 ++++++++++---- Zend/zend_language_parser.y | 9 +++++---- ext/reflection/tests/bug71767.phpt | 9 +++++++++ 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index ba8af9bcdfc8b..b55f06604e723 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -244,6 +244,37 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_4(zend_ast_kind kind, zend_ast return ast; } +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_5(zend_ast_kind kind, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4, zend_ast *child5) { + zend_ast *ast; + uint32_t lineno; + + ZEND_ASSERT(kind >> ZEND_AST_NUM_CHILDREN_SHIFT == 5); + ast = zend_ast_alloc(zend_ast_size(5)); + ast->kind = kind; + ast->attr = 0; + ast->child[0] = child1; + ast->child[1] = child2; + ast->child[2] = child3; + ast->child[3] = child4; + ast->child[4] = child5; + if (child1) { + lineno = zend_ast_get_lineno(child1); + } else if (child2) { + lineno = zend_ast_get_lineno(child2); + } else if (child3) { + lineno = zend_ast_get_lineno(child3); + } else if (child4) { + lineno = zend_ast_get_lineno(child4); + } else if (child5) { + lineno = zend_ast_get_lineno(child5); + } else { + lineno = CG(zend_lineno); + } + ast->lineno = lineno; + + return ast; +} + ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_0(zend_ast_kind kind) { zend_ast *ast; zend_ast_list *list; diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index ac178c874d22e..825c392a18da4 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -214,12 +214,12 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast * #if ZEND_AST_SPEC # define ZEND_AST_SPEC_CALL(name, ...) \ - ZEND_EXPAND_VA(ZEND_AST_SPEC_CALL_(name, __VA_ARGS__, _4, _3, _2, _1, _0)(__VA_ARGS__)) -# define ZEND_AST_SPEC_CALL_(name, _, _4, _3, _2, _1, suffix, ...) \ + ZEND_EXPAND_VA(ZEND_AST_SPEC_CALL_(name, __VA_ARGS__, _5, _4, _3, _2, _1, _0)(__VA_ARGS__)) +# define ZEND_AST_SPEC_CALL_(name, _, _5, _4, _3, _2, _1, suffix, ...) \ name ## suffix # define ZEND_AST_SPEC_CALL_EX(name, ...) \ - ZEND_EXPAND_VA(ZEND_AST_SPEC_CALL_EX_(name, __VA_ARGS__, _4, _3, _2, _1, _0)(__VA_ARGS__)) -# define ZEND_AST_SPEC_CALL_EX_(name, _, _5, _4, _3, _2, _1, suffix, ...) \ + ZEND_EXPAND_VA(ZEND_AST_SPEC_CALL_EX_(name, __VA_ARGS__, _5, _4, _3, _2, _1, _0)(__VA_ARGS__)) +# define ZEND_AST_SPEC_CALL_EX_(name, _, _6, _5, _4, _3, _2, _1, suffix, ...) \ name ## suffix ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_0(zend_ast_kind kind); @@ -227,6 +227,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_1(zend_ast_kind kind, zend_ast ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_2(zend_ast_kind kind, zend_ast *child1, zend_ast *child2); ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_3(zend_ast_kind kind, zend_ast *child1, zend_ast *child2, zend_ast *child3); ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_4(zend_ast_kind kind, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4); +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_5(zend_ast_kind kind, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4, zend_ast *child5); static zend_always_inline zend_ast * zend_ast_create_ex_0(zend_ast_kind kind, zend_ast_attr attr) { zend_ast *ast = zend_ast_create_0(kind); @@ -253,6 +254,11 @@ static zend_always_inline zend_ast * zend_ast_create_ex_4(zend_ast_kind kind, ze ast->attr = attr; return ast; } +static zend_always_inline zend_ast * zend_ast_create_ex_5(zend_ast_kind kind, zend_ast_attr attr, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4, zend_ast *child5) { + zend_ast *ast = zend_ast_create_5(kind, child1, child2, child3, child4, child5); + ast->attr = attr; + return ast; +} ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_0(zend_ast_kind kind); ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_1(zend_ast_kind kind, zend_ast *child); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index bddde6d1be1b3..4fdc05909bf06 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -1089,10 +1089,11 @@ inline_function: { $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $2 | $13, $1, $3, zend_string_init("{closure}", sizeof("{closure}") - 1, 0), $5, $7, $11, $8, NULL); CG(extra_fn_flags) = $9; } - | fn returns_ref '(' parameter_list ')' return_type backup_doc_comment T_DOUBLE_ARROW backup_fn_flags backup_lex_pos expr backup_fn_flags - { $$ = zend_ast_create_decl(ZEND_AST_ARROW_FUNC, $2 | $12, $1, $7, - zend_string_init("{closure}", sizeof("{closure}") - 1, 0), $4, NULL, - zend_ast_create(ZEND_AST_RETURN, $11), $6, NULL); + | fn returns_ref backup_doc_comment '(' parameter_list ')' return_type + T_DOUBLE_ARROW backup_fn_flags backup_lex_pos expr backup_fn_flags + { $$ = zend_ast_create_decl(ZEND_AST_ARROW_FUNC, $2 | $12, $1, $3, + zend_string_init("{closure}", sizeof("{closure}") - 1, 0), $5, NULL, + zend_ast_create(ZEND_AST_RETURN, $11), $7, NULL); ((zend_ast_decl *) $$)->lex_pos = $10; CG(extra_fn_flags) = $9; } ; diff --git a/ext/reflection/tests/bug71767.phpt b/ext/reflection/tests/bug71767.phpt index c20073c67a9a2..95094290bf340 100644 --- a/ext/reflection/tests/bug71767.phpt +++ b/ext/reflection/tests/bug71767.phpt @@ -27,13 +27,21 @@ $func = function( ) { }; +/** Correct docblock */ +$func2 = fn( + /** wrong docblock */ + $arg +) => null; + $reflectionFunction = new ReflectionFunction('foo'); $reflectionClass = new ReflectionClass(Foo::class); $reflectionClosure = new ReflectionFunction($func); +$reflectionArrowFn = new ReflectionFunction($func2); echo $reflectionFunction->getDocComment() . PHP_EOL; echo $reflectionClass->getMethod('bar')->getDocComment() . PHP_EOL; echo $reflectionClosure->getDocComment() . PHP_EOL; +echo $reflectionArrowFn->getDocComment() . PHP_EOL; echo "Done\n"; ?> @@ -41,4 +49,5 @@ echo "Done\n"; /** Correct docblock */ /** Correct docblock */ /** Correct docblock */ +/** Correct docblock */ Done From a4a52edaad84ae4d978e2d38ff117800230c4781 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 5 Jun 2020 12:12:51 +0200 Subject: [PATCH 6/8] Handle attributes for promoted properties --- Zend/tests/ctor_promotion_attributes.phpt | 22 ++++++++++++++++++++++ Zend/zend_compile.c | 6 +++++- 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/ctor_promotion_attributes.phpt diff --git a/Zend/tests/ctor_promotion_attributes.phpt b/Zend/tests/ctor_promotion_attributes.phpt new file mode 100644 index 0000000000000..d85d8d3181d6f --- /dev/null +++ b/Zend/tests/ctor_promotion_attributes.phpt @@ -0,0 +1,22 @@ +--TEST-- +Attributes on promoted properties are assigned to both the property and parameter +--FILE-- +> + public int $num, + ) {} +} + +$prop = new ReflectionProperty(Test::class, 'num'); +var_dump($prop->getAttributes()[0]->getName()); + +$param = new ReflectionParameter([Test::class, '__construct'], 'num'); +var_dump($param->getAttributes()[0]->getName()); + +?> +--EXPECT-- +string(11) "NonNegative" +string(11) "NonNegative" diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 540a7f71b875e..c6d79dfcd5a60 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5941,8 +5941,12 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall zend_string *doc_comment = doc_comment_ast ? zend_string_copy(zend_ast_get_str(doc_comment_ast)) : NULL; - zend_declare_typed_property( + zend_property_info *prop = zend_declare_typed_property( scope, name, &default_value, visibility, doc_comment, type); + if (attributes_ast) { + zend_compile_attributes( + &prop->attributes, attributes_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY); + } } } From fe75c27d5aa0c931024d7da4a6259dc16be0c024 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 5 Jun 2020 12:23:34 +0200 Subject: [PATCH 7/8] Add ReflectionProperty::isPromoted() --- Zend/zend_compile.c | 2 +- Zend/zend_compile.h | 3 +++ ext/reflection/php_reflection.c | 8 ++++++++ ext/reflection/php_reflection.stub.php | 2 ++ ext/reflection/php_reflection_arginfo.h | 8 ++++++-- ext/reflection/tests/constructor_promotion.phpt | 2 ++ 6 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index c6d79dfcd5a60..a7f0f21327b2d 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5942,7 +5942,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall zend_string *doc_comment = doc_comment_ast ? zend_string_copy(zend_ast_get_str(doc_comment_ast)) : NULL; zend_property_info *prop = zend_declare_typed_property( - scope, name, &default_value, visibility, doc_comment, type); + scope, name, &default_value, visibility | ZEND_ACC_PROMOTED, doc_comment, type); if (attributes_ast) { zend_compile_attributes( &prop->attributes, attributes_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 1b161b97572dc..8d3f5b5a6d070 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -209,6 +209,9 @@ typedef struct _zend_oparray_context { /* Static method or property | | | */ #define ZEND_ACC_STATIC (1 << 4) /* | X | X | */ /* | | | */ +/* Promoted property / parameter | | | */ +#define ZEND_ACC_PROMOTED (1 << 5) /* | | X | X */ +/* | | | */ /* Final class or method | | | */ #define ZEND_ACC_FINAL (1 << 5) /* X | X | | */ /* | | | */ diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 6d4f257eb4a8c..93e6516b05c1f 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -5461,6 +5461,14 @@ ZEND_METHOD(ReflectionProperty, isDefault) } /* }}} */ +/* {{{ proto public bool ReflectionProperty::isPromoted() + Returns whether this property has been promoted from a constructor */ +ZEND_METHOD(ReflectionProperty, isPromoted) +{ + _property_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_PROMOTED); +} +/* }}} */ + /* {{{ proto public int ReflectionProperty::getModifiers() Returns a bitfield of the access modifiers for this property */ ZEND_METHOD(ReflectionProperty, getModifiers) diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index c27f971c070cb..6858475153a2e 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -413,6 +413,8 @@ public function isStatic() {} /** @return bool */ public function isDefault() {} + public function isPromoted(): bool {} + /** @return int */ public function getModifiers() {} diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 3dec73ec8b495..7f14db76d4c97 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -312,6 +312,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionProperty_isDefault arginfo_class_ReflectionFunctionAbstract___clone +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionProperty_isPromoted, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + #define arginfo_class_ReflectionProperty_getModifiers arginfo_class_ReflectionFunctionAbstract___clone #define arginfo_class_ReflectionProperty_getDeclaringClass arginfo_class_ReflectionFunctionAbstract___clone @@ -324,8 +327,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionProperty_hasType arginfo_class_ReflectionFunctionAbstract___clone -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionProperty_hasDefaultValue, 0, 0, _IS_BOOL, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_ReflectionProperty_hasDefaultValue arginfo_class_ReflectionProperty_isPromoted #define arginfo_class_ReflectionProperty_getDefaultValue arginfo_class_ReflectionFunctionAbstract___clone @@ -604,6 +606,7 @@ ZEND_METHOD(ReflectionProperty, isPrivate); ZEND_METHOD(ReflectionProperty, isProtected); ZEND_METHOD(ReflectionProperty, isStatic); ZEND_METHOD(ReflectionProperty, isDefault); +ZEND_METHOD(ReflectionProperty, isPromoted); ZEND_METHOD(ReflectionProperty, getModifiers); ZEND_METHOD(ReflectionProperty, getDeclaringClass); ZEND_METHOD(ReflectionProperty, getDocComment); @@ -851,6 +854,7 @@ static const zend_function_entry class_ReflectionProperty_methods[] = { ZEND_ME(ReflectionProperty, isProtected, arginfo_class_ReflectionProperty_isProtected, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionProperty, isStatic, arginfo_class_ReflectionProperty_isStatic, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionProperty, isDefault, arginfo_class_ReflectionProperty_isDefault, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionProperty, isPromoted, arginfo_class_ReflectionProperty_isPromoted, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionProperty, getModifiers, arginfo_class_ReflectionProperty_getModifiers, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionProperty, getDeclaringClass, arginfo_class_ReflectionProperty_getDeclaringClass, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionProperty, getDocComment, arginfo_class_ReflectionProperty_getDocComment, ZEND_ACC_PUBLIC) diff --git a/ext/reflection/tests/constructor_promotion.phpt b/ext/reflection/tests/constructor_promotion.phpt index 9f6035c584d32..f5d6d9b7a670c 100644 --- a/ext/reflection/tests/constructor_promotion.phpt +++ b/ext/reflection/tests/constructor_promotion.phpt @@ -15,6 +15,7 @@ $rc = new ReflectionClass(Test::class); echo $rc, "\n"; $y = $rc->getProperty('y'); +var_dump($y->isPromoted()); var_dump($y->getDocComment()); ?> @@ -48,4 +49,5 @@ Class [ class Test ] { } } +bool(true) string(24) "/** @SomeAnnotation() */" From 34c0b66c5815cc5a5de147a6b0afce0d9b8f8ab1 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 5 Jun 2020 12:29:22 +0200 Subject: [PATCH 8/8] Expose ReflectionParameter::isPromoted() --- Zend/zend_compile.c | 4 ++- Zend/zend_compile.h | 3 +++ ext/reflection/php_reflection.c | 16 +++++++++++ ext/reflection/php_reflection.stub.php | 2 ++ ext/reflection/php_reflection_arginfo.h | 4 +++ .../tests/constructor_promotion.phpt | 27 +++++++++++++++---- 6 files changed, 50 insertions(+), 6 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index a7f0f21327b2d..83c2573d791dc 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5894,7 +5894,9 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall zend_alloc_cache_slots(zend_type_get_num_classes(arg_info->type)); } - ZEND_TYPE_FULL_MASK(arg_info->type) |= _ZEND_ARG_INFO_FLAGS(is_ref, is_variadic); + uint32_t arg_info_flags = _ZEND_ARG_INFO_FLAGS(is_ref, is_variadic) + | (visibility ? _ZEND_IS_PROMOTED_BIT : 0); + ZEND_TYPE_FULL_MASK(arg_info->type) |= arg_info_flags; if (opcode == ZEND_RECV) { opline->op2.num = type_ast ? ZEND_TYPE_FULL_MASK(arg_info->type) : MAY_BE_ANY; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 8d3f5b5a6d070..9a1e5adaafe1c 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -941,10 +941,13 @@ ZEND_API zend_string *zend_type_to_string(zend_type type); /* The send mode and is_variadic flag are stored as part of zend_type */ #define _ZEND_SEND_MODE_SHIFT _ZEND_TYPE_EXTRA_FLAGS_SHIFT #define _ZEND_IS_VARIADIC_BIT (1 << (_ZEND_TYPE_EXTRA_FLAGS_SHIFT + 2)) +#define _ZEND_IS_PROMOTED_BIT (1 << (_ZEND_TYPE_EXTRA_FLAGS_SHIFT + 3)) #define ZEND_ARG_SEND_MODE(arg_info) \ ((ZEND_TYPE_FULL_MASK((arg_info)->type) >> _ZEND_SEND_MODE_SHIFT) & 3) #define ZEND_ARG_IS_VARIADIC(arg_info) \ ((ZEND_TYPE_FULL_MASK((arg_info)->type) & _ZEND_IS_VARIADIC_BIT) != 0) +#define ZEND_ARG_IS_PROMOTED(arg_info) \ + ((ZEND_TYPE_FULL_MASK((arg_info)->type) & _ZEND_IS_PROMOTED_BIT) != 0) #define ZEND_DIM_IS (1 << 0) /* isset fetch needed for null coalesce */ #define ZEND_DIM_ALTERNATIVE_SYNTAX (1 << 1) /* deprecated curly brace usage */ diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 93e6516b05c1f..d986f828f57b7 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -2870,6 +2870,22 @@ ZEND_METHOD(ReflectionParameter, isVariadic) } /* }}} */ +/* {{{ proto public bool ReflectionParameter::isPromoted() + Returns this constructor parameter has been promoted to a property */ +ZEND_METHOD(ReflectionParameter, isPromoted) +{ + reflection_object *intern; + parameter_reference *param; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + GET_REFLECTION_OBJECT_PTR(param); + + RETVAL_BOOL(ZEND_ARG_IS_PROMOTED(param->arg_info)); +} +/* }}} */ + /* {{{ proto public bool ReflectionType::allowsNull() Returns whether parameter MAY be null */ ZEND_METHOD(ReflectionType, allowsNull) diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index 6858475153a2e..e3d3fee52cf1f 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -555,6 +555,8 @@ public function getDefaultValueConstantName() {} /** @return bool */ public function isVariadic() {} + public function isPromoted(): bool {} + /** @return ReflectionAttribute[] */ public function getAttributes(?string $name = null, int $flags = 0): array {} } diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 7f14db76d4c97..e5ed4742ff9bf 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -402,6 +402,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionParameter_isVariadic arginfo_class_ReflectionFunctionAbstract___clone +#define arginfo_class_ReflectionParameter_isPromoted arginfo_class_ReflectionProperty_isPromoted + #define arginfo_class_ReflectionParameter_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes #define arginfo_class_ReflectionType___clone arginfo_class_ReflectionFunctionAbstract___clone @@ -647,6 +649,7 @@ ZEND_METHOD(ReflectionParameter, getDefaultValue); ZEND_METHOD(ReflectionParameter, isDefaultValueConstant); ZEND_METHOD(ReflectionParameter, getDefaultValueConstantName); ZEND_METHOD(ReflectionParameter, isVariadic); +ZEND_METHOD(ReflectionParameter, isPromoted); ZEND_METHOD(ReflectionParameter, getAttributes); ZEND_METHOD(ReflectionType, allowsNull); ZEND_METHOD(ReflectionType, __toString); @@ -907,6 +910,7 @@ static const zend_function_entry class_ReflectionParameter_methods[] = { ZEND_ME(ReflectionParameter, isDefaultValueConstant, arginfo_class_ReflectionParameter_isDefaultValueConstant, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionParameter, getDefaultValueConstantName, arginfo_class_ReflectionParameter_getDefaultValueConstantName, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionParameter, isVariadic, arginfo_class_ReflectionParameter_isVariadic, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionParameter, isPromoted, arginfo_class_ReflectionParameter_isPromoted, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionParameter, getAttributes, arginfo_class_ReflectionParameter_getAttributes, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/ext/reflection/tests/constructor_promotion.phpt b/ext/reflection/tests/constructor_promotion.phpt index f5d6d9b7a670c..d0a09aaee048f 100644 --- a/ext/reflection/tests/constructor_promotion.phpt +++ b/ext/reflection/tests/constructor_promotion.phpt @@ -4,10 +4,12 @@ Using Reflection on promoted properties getProperty('y'); var_dump($y->isPromoted()); var_dump($y->getDocComment()); +$z = $rc->getProperty('z'); +var_dump($z->isPromoted()); + +echo "\n"; + +$rp = new ReflectionParameter([Test::class, '__construct'], 'y'); +var_dump($rp->isPromoted()); +$rp = new ReflectionParameter([Test::class, '__construct'], 'z'); +var_dump($rp->isPromoted()); ?> --EXPECTF-- Class [ class Test ] { - @@ %s 3-9 + @@ %s 3-11 - Constants [0] { } @@ -32,18 +43,20 @@ Class [ class Test ] { - Static methods [0] { } - - Properties [2] { + - Properties [3] { + Property [ public $z = NULL ] Property [ public int $x ] Property [ public string $y ] } - Methods [1] { Method [ public method __construct ] { - @@ %s 4 - 8 + @@ %s 5 - 10 - - Parameters [2] { + - Parameters [3] { Parameter #0 [ int $x ] Parameter #1 [ string $y = '123' ] + Parameter #2 [ string $z = 'abc' ] } } } @@ -51,3 +64,7 @@ Class [ class Test ] { bool(true) string(24) "/** @SomeAnnotation() */" +bool(false) + +bool(true) +bool(false)