Created
January 20, 2013 15:17
-
-
Save nikic/4579298 to your computer and use it in GitHub Desktop.
Patch for property typehinting
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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