diff --git a/Zend/tests/accessors/automatic_setter_with_typehint.phpt b/Zend/tests/accessors/automatic_setter_with_typehint.phpt | |
index 4915c40..1ec4e1b 100644 | |
--- a/Zend/tests/accessors/automatic_setter_with_typehint.phpt | |
+++ b/Zend/tests/accessors/automatic_setter_with_typehint.phpt | |
@@ -4,8 +4,8 @@ Automatic setters can have typehints | |
<?php | |
class Test { | |
- public $test { | |
- get; set(stdClass $obj); | |
+ public stdClass $test { | |
+ get; set; | |
} | |
} | |
diff --git a/Zend/tests/accessors/std_set_typehint_error.phpt b/Zend/tests/accessors/std_set_typehint_error.phpt | |
index 5dd63b5..68954be 100644 | |
--- a/Zend/tests/accessors/std_set_typehint_error.phpt | |
+++ b/Zend/tests/accessors/std_set_typehint_error.phpt | |
@@ -10,8 +10,8 @@ class TestClass { | |
} | |
class TimePeriod { | |
- public $Object { | |
- set(TestClass2 $y) { $this->_Seconds = $y; } | |
+ public TestClass2 $Object { | |
+ set($y) { $this->_Seconds = $y; } | |
} | |
} | |
diff --git a/Zend/tests/accessors/std_set_with_paren_optional.phpt b/Zend/tests/accessors/std_set_with_paren_optional.phpt | |
index c59f3b5..71ef37f 100644 | |
--- a/Zend/tests/accessors/std_set_with_paren_optional.phpt | |
+++ b/Zend/tests/accessors/std_set_with_paren_optional.phpt | |
@@ -21,8 +21,8 @@ class TimePeriod { | |
public $Seconds3 { | |
set($x) { $this->_Seconds = $x; } | |
} | |
- public $Object { | |
- set(TestClass $y) { $this->_Seconds = $y; } | |
+ public TestClass $Object { | |
+ set($y) { $this->_Seconds = $y; } | |
} | |
} | |
@@ -47,4 +47,4 @@ echo "Done\n"; | |
4000 | |
5000 | |
Object [TestClass] | |
-Done | |
\ No newline at end of file | |
+Done | |
diff --git a/Zend/tests/accessors/typehinted_property_with_default_value.phpt b/Zend/tests/accessors/typehinted_property_with_default_value.phpt | |
new file mode 100644 | |
index 0000000..89efee7 | |
--- /dev/null | |
+++ b/Zend/tests/accessors/typehinted_property_with_default_value.phpt | |
@@ -0,0 +1,54 @@ | |
+--TEST-- | |
+Typehinted properties (without accessors) can have default values | |
+--FILE-- | |
+<?php | |
+ | |
+class Test { | |
+ public array $array = [1, 2, 3]; | |
+ public stdClass $object; | |
+ public stdClass $objectNullable = null; | |
+ public callable $callable; | |
+ public callable $callableNullable = null; | |
+ | |
+ public stdClass $objectAcc { | |
+ get; set { echo __METHOD__."($value)\n"; $this->objectAcc = $value; } | |
+ } | |
+ public stdClass $objectAccNullable = NULL { | |
+ get; set { echo __METHOD__."($value)\n"; $this->objectAccNullable = $value; } | |
+ } | |
+} | |
+ | |
+set_error_handler(function($errNo, $errStr) { echo $errStr, "\n"; }); | |
+ | |
+$test = new Test; | |
+var_dump( | |
+ $test->array, $test->object, $test->objectNullable, $test->callable, $test->callableNullable | |
+); | |
+ | |
+$test->object = null; | |
+$test->objectNullable = null; | |
+$test->callable = null; | |
+$test->callableNullable = null; | |
+ | |
+$test->objectAcc = null; | |
+$test->objectAccNullable = null; | |
+ | |
+?> | |
+--EXPECT-- | |
+array(3) { | |
+ [0]=> | |
+ int(1) | |
+ [1]=> | |
+ int(2) | |
+ [2]=> | |
+ int(3) | |
+} | |
+NULL | |
+NULL | |
+NULL | |
+NULL | |
+Argument 1 passed to Test::$object->set() must be an instance of stdClass, null given | |
+Argument 1 passed to Test::$callable->set() must be callable, null given | |
+Argument 1 passed to Test::$objectAcc->set() must be an instance of stdClass, null given | |
+Test::$objectAcc->set() | |
+Test::$objectAccNullable->set() | |
diff --git a/Zend/tests/accessors/typehinted_property_with_invalid_default.phpt b/Zend/tests/accessors/typehinted_property_with_invalid_default.phpt | |
new file mode 100644 | |
index 0000000..4aed5fd | |
--- /dev/null | |
+++ b/Zend/tests/accessors/typehinted_property_with_invalid_default.phpt | |
@@ -0,0 +1,12 @@ | |
+--TEST-- | |
+Only null is allowed as default value for properties with accessors | |
+--FILE-- | |
+<?php | |
+ | |
+class Test { | |
+ public $foo = 42 { } | |
+} | |
+ | |
+?> | |
+--EXPECTF-- | |
+Fatal error: Only null is allowed as a default value for properties with accessors in %s on line %d | |
diff --git a/Zend/tests/accessors/typehinted_property_without_accessors.phpt b/Zend/tests/accessors/typehinted_property_without_accessors.phpt | |
new file mode 100644 | |
index 0000000..08ad56e | |
--- /dev/null | |
+++ b/Zend/tests/accessors/typehinted_property_without_accessors.phpt | |
@@ -0,0 +1,35 @@ | |
+--TEST-- | |
+Properties can be typehinted without defining accessors | |
+--FILE-- | |
+<?php | |
+ | |
+class Test { | |
+ public array $array; | |
+ public stdClass $stdClass; | |
+ public callable $callable; | |
+} | |
+ | |
+$test = new Test; | |
+ | |
+$test->array = []; | |
+$test->stdClass = new stdClass; | |
+$test->callable = 'strlen'; | |
+ | |
+var_dump($test->array, $test->stdClass, $test->callable); | |
+ | |
+set_error_handler(function($errNo, $errStr) { echo $errStr, "\n"; }); | |
+ | |
+$test->array = 41; | |
+$test->stdClass = 42; | |
+$test->callable = 43; | |
+ | |
+?> | |
+--EXPECT-- | |
+array(0) { | |
+} | |
+object(stdClass)#2 (0) { | |
+} | |
+string(6) "strlen" | |
+Argument 1 passed to Test::$array->set() must be of the type array, integer given | |
+Argument 1 passed to Test::$stdClass->set() must be an instance of stdClass, integer given | |
+Argument 1 passed to Test::$callable->set() must be callable, integer given | |
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c | |
index b1f7521..69fe7c5 100644 | |
--- a/Zend/zend_compile.c | |
+++ b/Zend/zend_compile.c | |
@@ -1570,34 +1570,46 @@ int zend_do_verify_access_types(const znode *current_access_type, const znode *n | |
} | |
/* }}} */ | |
-void zend_declare_accessor(znode *var_name TSRMLS_DC) { /* {{{ */ | |
+void zend_do_check_accessor_default_value(const znode *value TSRMLS_DC) /* {{{ */ | |
+{ | |
+ if (Z_TYPE(value->u.constant) != IS_NULL && (Z_TYPE(value->u.constant) != IS_CONSTANT || strcasecmp(Z_STRVAL(value->u.constant), "NULL") != 0)) { | |
+ zend_error(E_COMPILE_ERROR, "Only null is allowed as a default value for properties with accessors"); | |
+ } | |
+} | |
+/* }}} */ | |
+ | |
+void zend_do_declare_accessor(const znode *var_name, const znode *value TSRMLS_DC) { /* {{{ */ | |
zend_property_info *property_info; | |
- char *property_name; | |
- int property_name_len; | |
- zend_uint orig_ce_flags = CG(active_class_entry)->ce_flags; | |
+ zval *default_value; | |
if (CG(access_type) & ZEND_ACC_STATIC) { | |
zend_error_noreturn(E_COMPILE_ERROR, "Cannot define static accessor %s::$%s, not supported at this time", CG(active_class_entry)->name, Z_STRVAL(var_name->u.constant)); | |
} | |
- /* zend_do_declare_property free's the string, so copy it */ | |
- property_name_len = Z_STRLEN(var_name->u.constant); | |
- property_name = estrndup(Z_STRVAL(var_name->u.constant), property_name_len); | |
+ if (zend_hash_exists(&CG(active_class_entry)->properties_info, Z_STRVAL(var_name->u.constant), Z_STRLEN(var_name->u.constant) + 1)) { | |
+ zend_error(E_COMPILE_ERROR, "Cannot redeclare %s::$%s", CG(active_class_entry)->name, Z_STRVAL(var_name->u.constant)); | |
+ } | |
+ | |
+ ALLOC_INIT_ZVAL(default_value); | |
+ if (value && value->op_type != IS_UNUSED) { | |
+ *default_value = value->u.constant; | |
+ CG(typehint_node)->EA = 1; | |
+ } else { | |
+ CG(typehint_node)->EA = 0; | |
+ } | |
+ | |
+ zend_declare_property_ex(CG(active_class_entry), zend_new_interned_string(Z_STRVAL(var_name->u.constant), Z_STRLEN(var_name->u.constant) + 1, 0 TSRMLS_CC), Z_STRLEN(var_name->u.constant), default_value, CG(access_type), CG(doc_comment), CG(doc_comment_len) TSRMLS_CC); | |
- /* Hide that we're working with an interface during property accessor declaration */ | |
- CG(active_class_entry)->ce_flags &= ~ZEND_ACC_INTERFACE; | |
- zend_do_declare_property(var_name, NULL, CG(access_type) & ~(ZEND_ACC_FINAL|ZEND_ACC_ABSTRACT) TSRMLS_CC); | |
- CG(active_class_entry)->ce_flags = orig_ce_flags; | |
+ CG(doc_comment) = NULL; | |
+ CG(doc_comment_len) = 0; | |
- if (zend_hash_find(&CG(active_class_entry)->properties_info, property_name, property_name_len + 1, (void **) &property_info)==SUCCESS) { | |
- /* Add back final/abstract flags that were skipped previously */ | |
- property_info->flags |= CG(access_type); | |
+ if (zend_hash_find(&CG(active_class_entry)->properties_info, Z_STRVAL(var_name->u.constant), Z_STRLEN(var_name->u.constant) + 1, (void **) &property_info) == SUCCESS) { | |
property_info->accs = ecalloc(ZEND_NUM_ACCESSORS, sizeof(zend_function *)); | |
CG(current_property_info) = property_info; | |
- efree(property_name); | |
+ efree(Z_STRVAL(var_name->u.constant)); | |
} else { | |
- zend_error_noreturn(E_COMPILE_ERROR, "Property_info for %s::$%s is not present, should not happen in zend_declare_accessor()", CG(active_class_entry)->name, property_name); | |
+ zend_error_noreturn(E_COMPILE_ERROR, "Property_info for %s::$%s is not present, should not happen in zend_declare_accessor()", CG(active_class_entry)->name, Z_STRVAL(var_name->u.constant)); | |
} | |
} | |
/* }}} */ | |
@@ -1612,7 +1624,7 @@ static inline char *create_accessor_function_name(const char *property_name, cha | |
} | |
/* }}} */ | |
-void zend_do_begin_accessor_declaration(znode *function_token, znode *modifiers, int return_reference, int has_params TSRMLS_DC) /* {{{ */ | |
+void zend_do_begin_accessor_declaration(znode *function_token, znode *modifiers, znode *param, int return_reference TSRMLS_DC) /* {{{ */ | |
{ | |
zend_property_info *property_info = CG(current_property_info); | |
const char *property_name = zend_get_property_name(property_info); | |
@@ -1635,7 +1647,7 @@ void zend_do_begin_accessor_declaration(znode *function_token, znode *modifiers, | |
efree(Z_STRVAL(function_token->u.constant)); | |
ZVAL_STRING(&function_token->u.constant, create_accessor_function_name(property_name, "get"), 0); | |
- if (has_params) { | |
+ if (param) { | |
zend_error(E_COMPILE_ERROR, "Getters do not accept parameters for variable %s::$%s", CG(active_class_entry)->name, property_name); | |
} | |
@@ -1643,8 +1655,6 @@ void zend_do_begin_accessor_declaration(znode *function_token, znode *modifiers, | |
zend_do_begin_function_declaration(function_token, function_token, 1, return_reference, modifiers TSRMLS_CC); | |
property_info->accs[ZEND_ACCESSOR_GET] = (zend_function *) CG(active_op_array); | |
} else if (Z_TYPE(function_token->u.constant) == IS_STRING && strcasecmp("set", Z_STRVAL(function_token->u.constant)) == 0) { | |
- znode unused_node, unused_node2, value_node; | |
- | |
efree(Z_STRVAL(function_token->u.constant)); | |
ZVAL_STRING(&function_token->u.constant, create_accessor_function_name(property_name, "set"), 0); | |
@@ -1655,19 +1665,37 @@ void zend_do_begin_accessor_declaration(znode *function_token, znode *modifiers, | |
zend_do_begin_function_declaration(function_token, function_token, 1, ZEND_RETURN_VAL, modifiers TSRMLS_CC); | |
property_info->accs[ZEND_ACCESSOR_SET] = (zend_function *) CG(active_op_array); | |
- if (!has_params) { | |
- /* Add $value parameter to __setHours() */ | |
- unused_node.op_type = unused_node2.op_type = IS_UNUSED; | |
- unused_node.u.op.num = unused_node2.u.op.num = 1; | |
+ { | |
+ znode offset_node, *varname_node, varname_node_local; | |
+ offset_node.op_type = IS_UNUSED; | |
+ offset_node.u.op.num = 1; | |
- ZVAL_STRINGL(&value_node.u.constant, "value", 5, 1); | |
+ if (param) { | |
+ varname_node = param; | |
+ } else { | |
+ ZVAL_STRINGL(&varname_node_local.u.constant, "value", 5, 1); | |
+ varname_node = &varname_node_local; | |
+ } | |
- zend_do_receive_arg(ZEND_RECV, &value_node, &unused_node, NULL, &unused_node2, 0 TSRMLS_CC); | |
+ /* The EA specifies whether an explicit default value was given or if it's just | |
+ * implicit NULL */ | |
+ if (CG(typehint_node)->EA) { | |
+ znode init_node; | |
+ INIT_ZNODE(init_node); | |
+ MAKE_COPY_ZVAL( | |
+ &CG(active_class_entry)->default_properties_table[property_info->offset], | |
+ &init_node.u.constant | |
+ ); | |
+ | |
+ zend_do_receive_arg(ZEND_RECV_INIT, varname_node, &offset_node, &init_node, CG(typehint_node), 0 TSRMLS_CC); | |
+ } else { | |
+ zend_do_receive_arg(ZEND_RECV, varname_node, &offset_node, NULL, CG(typehint_node), 0 TSRMLS_CC); | |
+ } | |
} | |
} else if (Z_TYPE(function_token->u.constant) == IS_LONG && Z_LVAL(function_token->u.constant) == T_ISSET) { | |
ZVAL_STRING(&function_token->u.constant, create_accessor_function_name(property_name, "isset"), 0); | |
- if (has_params) { | |
+ if (param) { | |
zend_error(E_COMPILE_ERROR, "Issetters do not accept parameters for variable %s::$%s", CG(active_class_entry)->name, property_name); | |
} | |
@@ -1677,7 +1705,7 @@ void zend_do_begin_accessor_declaration(znode *function_token, znode *modifiers, | |
} else if (Z_TYPE(function_token->u.constant) == IS_LONG && Z_LVAL(function_token->u.constant) == T_UNSET) { | |
ZVAL_STRING(&function_token->u.constant, create_accessor_function_name(property_name, "unset"), 0); | |
- if (has_params) { | |
+ if (param) { | |
zend_error(E_COMPILE_ERROR, "Unsetters do not accept parameters for variable %s::$%s", CG(active_class_entry)->name, property_name); | |
} | |
@@ -1769,7 +1797,23 @@ void zend_do_end_accessor_declaration(znode *function_token, const znode *body T | |
} | |
/* }}} */ | |
-void zend_finalize_accessor(TSRMLS_D) { /* {{{ */ | |
+static void zend_declare_accessor_method_helper(znode *function_token, long additional_modifiers TSRMLS_DC) /* {{{ */ | |
+{ | |
+ znode zn_modifiers, zn_body; | |
+ | |
+ INIT_ZNODE(zn_modifiers); | |
+ Z_LVAL(zn_modifiers.u.constant) = additional_modifiers; | |
+ | |
+ INIT_ZNODE(zn_body); | |
+ Z_LVAL(zn_body.u.constant) = ZEND_ACC_ABSTRACT; | |
+ | |
+ zend_do_begin_accessor_declaration(function_token, &zn_modifiers, NULL, 0 TSRMLS_CC); | |
+ zend_do_end_accessor_declaration(function_token, &zn_body TSRMLS_CC); | |
+} | |
+/* }}} */ | |
+ | |
+void zend_do_finalize_accessor(TSRMLS_D) /* {{{ */ | |
+{ | |
zend_property_info *property_info = CG(current_property_info); | |
zend_uint keep_flags = 0; | |
@@ -1778,34 +1822,18 @@ void zend_finalize_accessor(TSRMLS_D) { /* {{{ */ | |
} | |
if (!property_info->accs[ZEND_ACCESSOR_ISSET] && property_info->accs[ZEND_ACCESSOR_GET]) { | |
- znode zn_fntoken, zn_modifiers, zn_body; | |
+ znode zn_fntoken; | |
INIT_ZNODE(zn_fntoken); | |
ZVAL_LONG(&zn_fntoken.u.constant, T_ISSET); | |
- INIT_ZNODE(zn_modifiers); | |
- Z_LVAL(zn_modifiers.u.constant) | |
- = property_info->accs[ZEND_ACCESSOR_GET]->common.fn_flags & keep_flags; | |
- | |
- INIT_ZNODE(zn_body); | |
- Z_LVAL(zn_body.u.constant) = ZEND_ACC_ABSTRACT; | |
- | |
- zend_do_begin_accessor_declaration(&zn_fntoken, &zn_modifiers, 0, 0 TSRMLS_CC); | |
- zend_do_end_accessor_declaration(&zn_fntoken, &zn_body TSRMLS_CC); | |
+ zend_declare_accessor_method_helper(&zn_fntoken, property_info->accs[ZEND_ACCESSOR_GET]->common.fn_flags & keep_flags TSRMLS_CC); | |
} | |
if (!property_info->accs[ZEND_ACCESSOR_UNSET] && property_info->accs[ZEND_ACCESSOR_SET]) { | |
- znode zn_fntoken, zn_modifiers, zn_body; | |
+ znode zn_fntoken; | |
INIT_ZNODE(zn_fntoken); | |
ZVAL_LONG(&zn_fntoken.u.constant, T_UNSET); | |
- INIT_ZNODE(zn_modifiers); | |
- Z_LVAL(zn_modifiers.u.constant) | |
- = property_info->accs[ZEND_ACCESSOR_SET]->common.fn_flags & keep_flags; | |
- | |
- INIT_ZNODE(zn_body); | |
- Z_LVAL(zn_body.u.constant) = ZEND_ACC_ABSTRACT; | |
- | |
- zend_do_begin_accessor_declaration(&zn_fntoken, &zn_modifiers, 0, 0 TSRMLS_CC); | |
- zend_do_end_accessor_declaration(&zn_fntoken, &zn_body TSRMLS_CC); | |
+ zend_declare_accessor_method_helper(&zn_fntoken, property_info->accs[ZEND_ACCESSOR_SET]->common.fn_flags & keep_flags TSRMLS_CC); | |
} | |
CG(current_property_info) = NULL; | |
@@ -5598,6 +5626,19 @@ void zend_do_declare_property(const znode *var_name, const znode *value, zend_ui | |
CG(active_class_entry)->name, var_name->u.constant.value.str.val); | |
} | |
+ if (CG(typehint_node)->op_type != IS_UNUSED) { | |
+ znode function_token; | |
+ | |
+ zend_do_declare_accessor(var_name, value TSRMLS_CC); | |
+ MAKE_ZNODE(function_token, "get"); | |
+ zend_declare_accessor_method_helper(&function_token, 0 TSRMLS_CC); | |
+ MAKE_ZNODE(function_token, "set"); | |
+ zend_declare_accessor_method_helper(&function_token, 0 TSRMLS_CC); | |
+ zend_do_finalize_accessor(TSRMLS_C); | |
+ | |
+ return; | |
+ } | |
+ | |
if (zend_hash_find(&CG(active_class_entry)->properties_info, var_name->u.constant.value.str.val, var_name->u.constant.value.str.len+1, (void **) &existing_property_info)==SUCCESS) { | |
zend_error(E_COMPILE_ERROR, "Cannot redeclare %s::$%s", CG(active_class_entry)->name, var_name->u.constant.value.str.val); | |
} | |
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h | |
index 235e3ca..7f5f291 100644 | |
--- a/Zend/zend_compile.h | |
+++ b/Zend/zend_compile.h | |
@@ -546,10 +546,11 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC); | |
void zend_do_yield(znode *result, znode *value, const znode *key, zend_bool is_variable TSRMLS_DC); | |
void zend_do_handle_exception(TSRMLS_D); | |
-void zend_declare_accessor(znode *var_name TSRMLS_DC); | |
-void zend_do_begin_accessor_declaration(znode *function_token, znode *modifiers, int return_reference, int has_params TSRMLS_DC); | |
+void zend_do_check_accessor_default_value(const znode *value TSRMLS_DC); | |
+void zend_do_declare_accessor(const znode *var_name, const znode *value TSRMLS_DC); | |
+void zend_do_begin_accessor_declaration(znode *function_token, znode *modifiers, znode *param, int return_reference TSRMLS_DC); | |
void zend_do_end_accessor_declaration(znode *function_token, const znode *body TSRMLS_DC); | |
-void zend_finalize_accessor(TSRMLS_D); | |
+void zend_do_finalize_accessor(TSRMLS_D); | |
void zend_do_begin_lambda_function_declaration(znode *result, znode *function_token, int return_reference, int is_static TSRMLS_DC); | |
void zend_do_fetch_lexical_variable(znode *varname, zend_bool is_ref TSRMLS_DC); | |
diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h | |
index 73cd8c6d8..d417104 100644 | |
--- a/Zend/zend_globals.h | |
+++ b/Zend/zend_globals.h | |
@@ -122,6 +122,7 @@ struct _zend_compiler_globals { | |
znode implementing_class; | |
zend_property_info *current_property_info; | |
+ znode *typehint_node; | |
zend_uint access_type; | |
diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y | |
index 59a9726..c673f15 100644 | |
--- a/Zend/zend_language_parser.y | |
+++ b/Zend/zend_language_parser.y | |
@@ -587,7 +587,9 @@ class_statement_list: | |
class_statement: | |
- variable_modifiers { CG(access_type) = Z_LVAL($1.u.constant); } class_variable_accessor_declarations | |
+ variable_modifiers { CG(access_type) = Z_LVAL($1.u.constant); } | |
+ optional_class_type { CG(typehint_node) = &$3; } | |
+ class_variable_accessor_declarations | |
| class_constant_declaration ';' | |
| trait_use_statement | |
| method_modifiers function is_reference T_STRING { zend_do_begin_function_declaration(&$2, &$4, 1, $3.op_type, &$1 TSRMLS_CC); } | |
@@ -708,29 +710,33 @@ accessor_optional_parens: | |
accessor_function: | |
accessor_modifiers T_ISSET accessor_optional_parens | |
{ Z_LVAL($2.u.constant) = T_ISSET; | |
- zend_do_begin_accessor_declaration(&$2, &$1, 0, 0 TSRMLS_CC); } | |
+ zend_do_begin_accessor_declaration(&$2, &$1, NULL, 0 TSRMLS_CC); } | |
method_body | |
{ zend_do_end_accessor_declaration(&$2, &$5 TSRMLS_CC); } | |
| accessor_modifiers T_UNSET accessor_optional_parens | |
{ Z_LVAL($2.u.constant) = T_UNSET; | |
- zend_do_begin_accessor_declaration(&$2, &$1, 0, 0 TSRMLS_CC); } | |
+ zend_do_begin_accessor_declaration(&$2, &$1, NULL, 0 TSRMLS_CC); } | |
method_body | |
{ zend_do_end_accessor_declaration(&$2, &$5 TSRMLS_CC); } | |
| accessor_modifiers is_reference T_STRING accessor_optional_parens | |
- { zend_do_begin_accessor_declaration(&$3, &$1, $2.op_type, 0 TSRMLS_CC); } | |
+ { zend_do_begin_accessor_declaration(&$3, &$1, NULL, $2.op_type TSRMLS_CC); } | |
method_body | |
{ zend_do_end_accessor_declaration(&$3, &$6 TSRMLS_CC); } | |
- | accessor_modifiers is_reference T_STRING '(' | |
- { zend_do_begin_accessor_declaration(&$3, &$1, $2.op_type, 1 TSRMLS_CC); } | |
- non_empty_parameter_list ')' method_body | |
+ | accessor_modifiers is_reference T_STRING '(' T_VARIABLE ')' | |
+ { zend_do_begin_accessor_declaration(&$3, &$1, &$5, $2.op_type TSRMLS_CC); } | |
+ method_body | |
{ zend_do_end_accessor_declaration(&$3, &$8 TSRMLS_CC); } | |
; | |
+optional_null: | |
+ /* empty */ { $$.op_type = IS_UNUSED; } | |
+ | '=' static_scalar { $$ = $2; zend_do_check_accessor_default_value(&$2 TSRMLS_CC); } | |
+ | |
class_variable_accessor_declarations: | |
- T_VARIABLE '{' | |
- { zend_declare_accessor(&$1 TSRMLS_CC); } | |
+ T_VARIABLE optional_null '{' | |
+ { zend_do_declare_accessor(&$1, &$2 TSRMLS_CC); } | |
accessors | |
- { zend_finalize_accessor(TSRMLS_C); } | |
+ { zend_do_finalize_accessor(TSRMLS_C); } | |
'}' | |
| class_variable_declaration ';' | |
; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment