summaryrefslogtreecommitdiff
path: root/Zend
diff options
context:
space:
mode:
Diffstat (limited to 'Zend')
-rw-r--r--Zend/tests/ctor_promotion_abstract.phpt12
-rw-r--r--Zend/tests/ctor_promotion_additional_modifiers.phpt12
-rw-r--r--Zend/tests/ctor_promotion_attributes.phpt22
-rw-r--r--Zend/tests/ctor_promotion_basic.phpt21
-rw-r--r--Zend/tests/ctor_promotion_by_ref.phpt20
-rw-r--r--Zend/tests/ctor_promotion_callable_type.phpt12
-rw-r--r--Zend/tests/ctor_promotion_defaults.phpt43
-rw-r--r--Zend/tests/ctor_promotion_free_function.phpt10
-rw-r--r--Zend/tests/ctor_promotion_interface.phpt12
-rw-r--r--Zend/tests/ctor_promotion_mixing.phpt54
-rw-r--r--Zend/tests/ctor_promotion_not_a_ctor.phpt12
-rw-r--r--Zend/tests/ctor_promotion_null_default.phpt12
-rw-r--r--Zend/tests/ctor_promotion_repeated_prop.phpt14
-rw-r--r--Zend/tests/ctor_promotion_trait.phpt21
-rw-r--r--Zend/tests/ctor_promotion_variadic.phpt12
-rw-r--r--Zend/zend_ast.c31
-rw-r--r--Zend/zend_ast.h18
-rw-r--r--Zend/zend_compile.c85
-rw-r--r--Zend/zend_compile.h11
-rw-r--r--Zend/zend_language_parser.y30
20 files changed, 444 insertions, 20 deletions
diff --git a/Zend/tests/ctor_promotion_abstract.phpt b/Zend/tests/ctor_promotion_abstract.phpt
new file mode 100644
index 0000000000..937e247acd
--- /dev/null
+++ b/Zend/tests/ctor_promotion_abstract.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Constructor promotion cannot be used inside an abstract constructor
+--FILE--
+<?php
+
+abstract class Test {
+ abstract public function __construct(public int $x);
+}
+
+?>
+--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 0000000000..fb3b092d66
--- /dev/null
+++ b/Zend/tests/ctor_promotion_additional_modifiers.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Constructor promotion only permits visibility modifiers
+--FILE--
+<?php
+
+class Test {
+ public function __construct(public static $x) {}
+}
+
+?>
+--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_attributes.phpt b/Zend/tests/ctor_promotion_attributes.phpt
new file mode 100644
index 0000000000..d85d8d3181
--- /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--
+<?php
+
+class Test {
+ public function __construct(
+ <<NonNegative>>
+ 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/tests/ctor_promotion_basic.phpt b/Zend/tests/ctor_promotion_basic.phpt
new file mode 100644
index 0000000000..206f99fd40
--- /dev/null
+++ b/Zend/tests/ctor_promotion_basic.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Constructor promotion (basic example)
+--FILE--
+<?php
+
+class Point {
+ public function __construct(public int $x, public int $y, public int $z) {}
+}
+
+$point = new Point(1, 2, 3);
+
+// Check that properties really are typed.
+try {
+ $point->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 0000000000..4b07149c57
--- /dev/null
+++ b/Zend/tests/ctor_promotion_by_ref.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Constructor promotion of by-ref parameter
+--FILE--
+<?php
+
+class Ary {
+ public function __construct(public array &$array) {}
+}
+
+$array = [];
+$ary = new Ary($array);
+$array[] = 42;
+var_dump($ary->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 0000000000..ae10512ad5
--- /dev/null
+++ b/Zend/tests/ctor_promotion_callable_type.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Type of promoted property may not be callable
+--FILE--
+<?php
+
+class Test {
+ public function __construct(public callable $callable) {}
+}
+
+?>
+--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 0000000000..9999e8a539
--- /dev/null
+++ b/Zend/tests/ctor_promotion_defaults.phpt
@@ -0,0 +1,43 @@
+--TEST--
+Constructor promotion with default values
+--FILE--
+<?php
+
+class Point {
+ public function __construct(
+ public float $x = 0.0,
+ public float $y = 1.0,
+ public float $z = 2.0
+ ) {}
+}
+
+var_dump(new Point(10.0));
+var_dump(new Point(10.0, 11.0));
+var_dump(new Point(10.0, 11.0, 12.0));
+
+?>
+--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 0000000000..1eb84c5517
--- /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--
+<?php
+
+function __construct(public $prop) {}
+
+?>
+--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 0000000000..7cc856b93a
--- /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--
+<?php
+
+interface Test {
+ public function __construct(public int $x);
+}
+
+?>
+--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 0000000000..a527654e79
--- /dev/null
+++ b/Zend/tests/ctor_promotion_mixing.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Constructor promotiong mixed with other properties, parameters and code
+--FILE--
+<?php
+
+class Test {
+ public string $prop2;
+
+ public function __construct(public string $prop1 = "", $param2 = "") {
+ $this->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 [ <user> class Test ] {
+ @@ %s
+
+ - Constants [0] {
+ }
+
+ - Static properties [0] {
+ }
+
+ - Static methods [0] {
+ }
+
+ - Properties [2] {
+ Property [ public string $prop2 ]
+ Property [ public string $prop1 ]
+ }
+
+ - Methods [1] {
+ Method [ <user, ctor> public method __construct ] {
+ @@ %s
+
+ - Parameters [2] {
+ Parameter #0 [ <optional> string $prop1 = '' ]
+ Parameter #1 [ <optional> $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 0000000000..110ee8a5ce
--- /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--
+<?php
+
+class Test {
+ public function foobar(public int $x, public int $y) {}
+}
+
+?>
+--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 0000000000..61c7b2d10c
--- /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--
+<?php
+
+class Test {
+ public function __construct(public int $x = null) {}
+}
+
+?>
+--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 0000000000..6f4a9ffd02
--- /dev/null
+++ b/Zend/tests/ctor_promotion_repeated_prop.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Clash between promoted and explicit property
+--FILE--
+<?php
+
+class Test {
+ public $prop;
+
+ public function __construct(public $prop) {}
+}
+
+?>
+--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 0000000000..4c109157ae
--- /dev/null
+++ b/Zend/tests/ctor_promotion_trait.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Constructor promotion can be used inside a trait
+--FILE--
+<?php
+
+trait Test {
+ public function __construct(public $prop) {}
+}
+
+class Test2 {
+ use Test;
+}
+
+var_dump(new Test2(42));
+
+?>
+--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 0000000000..7e1e81cad8
--- /dev/null
+++ b/Zend/tests/ctor_promotion_variadic.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Cannot use constructor promotion with variadic parameter
+--FILE--
+<?php
+
+class Test {
+ public function __construct(public string ...$strings) {}
+}
+
+?>
+--EXPECTF--
+Fatal error: Cannot declare variadic promoted property in %s on line %d
diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c
index ba8af9bcdf..b55f06604e 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 0ec922dcea..825c392a18 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;
@@ -212,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);
@@ -225,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);
@@ -251,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_compile.c b/Zend/zend_compile.c
index 707d2ac9da..83c2573d79 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -5787,9 +5787,12 @@ 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;
+ 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 +5865,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,
@@ -5891,11 +5894,62 @@ 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;
}
+
+ 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_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 | ZEND_ACC_PROMOTED, doc_comment, type);
+ if (attributes_ast) {
+ zend_compile_attributes(
+ &prop->attributes, attributes_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY);
+ }
+ }
}
/* These are assigned at the end to avoid uninitialized memory in case of an error */
@@ -5907,6 +5961,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 1a53f92e9e..9a1e5adaaf 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 | | */
/* | | | */
@@ -873,8 +876,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
@@ -937,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/Zend/zend_language_parser.y b/Zend/zend_language_parser.y
index d8d66553c1..4fdc05909b 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 <ast> attribute_arguments attribute_decl attribute attributes
%type <num> returns_ref function fn is_reference is_variadic variable_modifiers
-%type <num> method_modifiers non_empty_member_modifiers member_modifier
+%type <num> method_modifiers non_empty_member_modifiers member_modifier optional_visibility_modifier
%type <num> class_modifiers class_modifier use_type backup_fn_flags
%type <ptr> backup_lex_pos
@@ -684,11 +684,22 @@ 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 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 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); }
;
@@ -1078,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; }
;