From 8e30d96ad895442d77d3a7c93cd70a052a257427 Mon Sep 17 00:00:00 2001 From: Zeev Suraski Date: Mon, 2 Feb 2004 12:28:19 +0000 Subject: [PATCH] Redesign the clone() feature to fix some fundamental flaws in the previous implementation. Using clone directly is now done using $replica = clone $src; Clone methods must now be declared as follows: function __clone($that) { } Clone methods in derived classes can call the __clone method of their parent classes using parent::__clone($that) --- Zend/zend_compile.c | 35 ++++++++++++++++++++++++----------- Zend/zend_compile.h | 1 + Zend/zend_language_parser.y | 5 +++-- Zend/zend_language_scanner.l | 4 ++++ Zend/zend_objects.c | 7 +++++-- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 9387733f49..a5b58f16f6 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1101,9 +1101,17 @@ void zend_do_end_function_declaration(znode *function_token TSRMLS_DC) zend_do_extended_info(TSRMLS_C); zend_do_return(NULL, 0 TSRMLS_CC); pass_two(CG(active_op_array) TSRMLS_CC); + + if (CG(active_class_entry) + && !strcmp(CG(active_op_array)->function_name, ZEND_CLONE_FUNC_NAME) + && (CG(active_op_array)->num_args != 1 || strcmp(CG(active_op_array)->arg_info[0].name, "that")!=0)) { + zend_error(E_COMPILE_ERROR, "The clone method must be declared as __clone($that)"); + } + CG(active_op_array)->line_end = zend_get_compiled_lineno(TSRMLS_C); CG(active_op_array) = function_token->u.op_array; + /* Pop the switch and foreach seperators */ zend_stack_del_top(&CG(switch_cond_stack)); zend_stack_del_top(&CG(foreach_copy_stack)); @@ -1192,21 +1200,14 @@ void zend_do_begin_method_call(znode *left_bracket TSRMLS_DC) if ((last_op->op2.op_type == IS_CONST) && (last_op->op2.u.constant.value.str.len == sizeof(ZEND_CLONE_FUNC_NAME)-1) && !zend_binary_strcasecmp(last_op->op2.u.constant.value.str.val, last_op->op2.u.constant.value.str.len, ZEND_CLONE_FUNC_NAME, sizeof(ZEND_CLONE_FUNC_NAME)-1)) { - last_op->opcode = ZEND_CLONE; - left_bracket->op_type = IS_UNUSED; - zval_dtor(&last_op->op2.u.constant); - SET_UNUSED(last_op->op2); - left_bracket->u.constant.value.lval = get_next_op_number(CG(active_op_array))-1; - zend_stack_push(&CG(function_call_stack), (void *) &ptr, sizeof(zend_function *)); - zend_do_extended_fcall_begin(TSRMLS_C); - return; + zend_error(E_COMPILE_ERROR, "Cannot call __clone() method on objects - use 'clone $obj' instead"); } if (last_op->opcode == ZEND_FETCH_OBJ_R) { last_op->opcode = ZEND_INIT_METHOD_CALL; left_bracket->u.constant.value.lval = ZEND_INIT_FCALL_BY_NAME; } else { - zend_op* opline = get_next_op(CG(active_op_array) TSRMLS_CC); + zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_INIT_FCALL_BY_NAME; opline->op2 = *left_bracket; opline->extended_value = 0; @@ -1217,7 +1218,20 @@ void zend_do_begin_method_call(znode *left_bracket TSRMLS_DC) zend_do_extended_fcall_begin(TSRMLS_C); } - + +void zend_do_clone(znode *result, znode *expr TSRMLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); + + opline->opcode = ZEND_CLONE; + opline->op1 = *expr; + SET_UNUSED(opline->op2); + opline->result.op_type = IS_VAR; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + *result = opline->result; +} + + void zend_do_begin_dynamic_function_call(znode *function_name TSRMLS_DC) { unsigned char *ptr = NULL; @@ -1321,7 +1335,6 @@ void zend_do_begin_class_member_function_call(TSRMLS_D) void zend_do_end_function_call(znode *function_name, znode *result, znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC) { zend_op *opline; - if (is_method && function_name && function_name->op_type == IS_UNUSED) { /* clone */ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index e90654b098..5f9b775d00 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -345,6 +345,7 @@ void zend_do_end_function_declaration(znode *function_token TSRMLS_DC); void zend_do_receive_arg(zend_uchar op, znode *var, znode *offset, znode *initialization, znode *class_type, znode *varname, zend_bool pass_by_reference TSRMLS_DC); int zend_do_begin_function_call(znode *function_name TSRMLS_DC); void zend_do_begin_method_call(znode *left_bracket TSRMLS_DC); +void zend_do_clone(znode *result, znode *expr TSRMLS_DC); void zend_do_begin_dynamic_function_call(znode *function_name TSRMLS_DC); void zend_do_fetch_class(znode *result, znode *class_name TSRMLS_DC); void zend_do_fetch_class_name(znode *result, znode *class_entry, znode *class_name TSRMLS_DC); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index f71c1b6712..1987d00fc2 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -69,7 +69,7 @@ %left '*' '/' '%' %right '!' '~' T_INC T_DEC T_INT_CAST T_DOUBLE_CAST T_STRING_CAST T_ARRAY_CAST T_OBJECT_CAST T_BOOL_CAST T_UNSET_CAST '@' %right '[' -%nonassoc T_NEW T_INSTANCEOF +%nonassoc T_NEW T_INSTANCEOF T_CLONE %token T_EXIT %token T_IF %left T_ELSEIF @@ -519,8 +519,9 @@ expr_without_variable: T_LIST '(' { zend_do_list_init(TSRMLS_C); } assignment_list ')' '=' expr { zend_do_list_end(&$$, &$7 TSRMLS_CC); } | variable '=' expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(BP_VAR_W, 0 TSRMLS_CC); zend_do_assign(&$$, &$1, &$3 TSRMLS_CC); } | variable '=' '&' variable { zend_do_end_variable_parse(BP_VAR_W, 0 TSRMLS_CC); zend_do_end_variable_parse(BP_VAR_W, 0 TSRMLS_CC); zend_do_assign_ref(&$$, &$1, &$4 TSRMLS_CC); } - | variable '=' '&' T_NEW class_name_reference { zend_check_writable_variable(&$1); zend_do_extended_fcall_begin(TSRMLS_C); zend_do_begin_new_object(&$4, &$5 TSRMLS_CC); } ctor_arguments { zend_do_end_new_object(&$3, &$4, &$7 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); zend_do_end_variable_parse(BP_VAR_W, 0 TSRMLS_CC); zend_do_assign_ref(&$$, &$1, &$3 TSRMLS_CC); } + | variable '=' '&' T_NEW class_name_reference { zend_error(E_STRICT, "Assigning the return value of new by reference is deprecated"); zend_check_writable_variable(&$1); zend_do_extended_fcall_begin(TSRMLS_C); zend_do_begin_new_object(&$4, &$5 TSRMLS_CC); } ctor_arguments { zend_do_end_new_object(&$3, &$4, &$7 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); zend_do_end_variable_parse(BP_VAR_W, 0 TSRMLS_CC); zend_do_assign_ref(&$$, &$1, &$3 TSRMLS_CC); } | T_NEW class_name_reference { zend_do_extended_fcall_begin(TSRMLS_C); zend_do_begin_new_object(&$1, &$2 TSRMLS_CC); } ctor_arguments { zend_do_end_new_object(&$$, &$1, &$4 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} + | T_CLONE expr { zend_do_clone(&$$, &$2 TSRMLS_CC); } | variable T_PLUS_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_ADD, &$$, &$1, &$3 TSRMLS_CC); } | variable T_MINUS_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_SUB, &$$, &$1, &$3 TSRMLS_CC); } | variable T_MUL_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_MUL, &$$, &$1, &$3 TSRMLS_CC); } diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 8f9a966d00..13efdaffab 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -928,6 +928,10 @@ NEWLINE ("\r"|"\n"|"\r\n") return T_NEW; } +"clone" { + return T_CLONE; +} + "var" { return T_VAR; } diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index 05595e84e9..5488511c5c 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -108,6 +108,7 @@ ZEND_API void zend_objects_clone_members(zend_object *new_object, zend_object_va zval *retval_ptr; HashTable symbol_table; zend_class_entry *ce = old_object->ce; + zval **args[1]; zend_hash_copy(new_object->properties, &ce->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); @@ -129,9 +130,11 @@ ZEND_API void zend_objects_clone_members(zend_object *new_object, zend_object_va clone_func_name->value.str.len = sizeof("__clone")-1; ZEND_INIT_SYMTABLE(&symbol_table); - ZEND_SET_SYMBOL(&symbol_table, "that", old_obj); + args[0] = &old_obj; - call_user_function_ex(NULL, &new_obj, clone_func_name, &retval_ptr, 0, NULL, 0, &symbol_table TSRMLS_CC); + call_user_function_ex(NULL, &new_obj, clone_func_name, &retval_ptr, 1, args, 1, &symbol_table TSRMLS_CC); + + zval_ptr_dtor(args[0]); zend_hash_destroy(&symbol_table); zval_ptr_dtor(&new_obj); -- 2.40.0