RE: [PHP-DEV] [patch] Late static bindings (LSB)

From: Date: Tue, 18 Sep 2007 12:59:15 +0000
Subject: RE: [PHP-DEV] [patch] Late static bindings (LSB)
References: 1  Groups: php.internals 
Request: Send a blank email to internals+get-32367@lists.php.net to get a copy of this message
Hi Etienne, At first thank you for catching the issue. I deside not to delay review of your patch and found only one serious bug. It doesn't handle nested calls (test lsb_017.phpt in my patch). --TEST-- ZE2 nested calls --FILE-- <?php class A { public static function test($x=null) { if (!is_null($x)) { echo "$x\n"; } return get_caller_class(); } } class B extends A { } class C extends A { } class D extends A { } echo A::test(B::test(C::test(D::test())))."\n"; ?> ==DONE== --EXPECT-- D C B A ==DONE== Your patch outputs 4 D. BTW I like other aspects of your patch. I think your get_called_class() is better than my get_caller_class(). (I changed this in my new patch). I also made the same behavior that you maintained in private email. I didn't removed the corresponding code yet but wrapped it with #ifdef ZEND_LSB2. (tests lsb_018.phpt and test_019.phpt must be failed without it). I also made several optimizations. The latest version of my patch is attached. I think if you fix the bug that I maintained before our patches will near identical. BTW may be your patch will be better, so please make a mixture of our patches. I assume they have exactly the same behavior, so we need the fastest one. Thanks. Dmitry. > -----Original Message----- > From: Etienne Kneuss [mailto:colder@php.net] > Sent: Sunday, September 16, 2007 8:18 PM > To: Dmitry Stogov > Cc: internals@lists.php.net > Subject: Re: [PHP-DEV] [patch] Late static bindings (LSB) > > > Hello, > > I made the update anyway as it looked like it wouldn't take much time. > > Attached is a updated version of my patch, along with all the tests > related to it: > > http://patches.colder.ch/Zend/late_static_bindings_take7.patch?markup > > -- > Etienne Kneuss > http://www.colder.ch > > Men never do evil so completely and cheerfully as > when they do it from a religious conviction. > -- Pascal > >

Index: Zend/zend_API.c =================================================================== RCS file: /repository/ZendEngine2/zend_API.c,v retrieving revision 1.443 diff -u -p -d -r1.443 zend_API.c --- Zend/zend_API.c 31 Aug 2007 12:36:00 -0000 1.443 +++ Zend/zend_API.c 18 Sep 2007 12:35:00 -0000 @@ -2860,6 +2860,9 @@ ZEND_API zend_bool zend_is_callable_ex(z lcname_len == sizeof("parent")-1 && ZEND_U_EQUAL(Z_TYPE_PP(obj), lcname, lcname_len, "parent", sizeof("parent")-1)) { ce = EG(active_op_array)->scope->parent; + } else if (lcname_len == sizeof("static")-1 && + ZEND_U_EQUAL(Z_TYPE_PP(obj), lcname, lcname_len, "static", sizeof("static")-1)) { + ce = EG(called_scope); } else if (zend_u_lookup_class(Z_TYPE_PP(obj), Z_UNIVAL_PP(obj), Z_UNILEN_PP(obj), &pce TSRMLS_CC) == SUCCESS) { ce = *pce; } Index: Zend/zend_builtin_functions.c =================================================================== RCS file: /repository/ZendEngine2/zend_builtin_functions.c,v retrieving revision 1.352 diff -u -p -d -r1.352 zend_builtin_functions.c --- Zend/zend_builtin_functions.c 11 Sep 2007 11:35:46 -0000 1.352 +++ Zend/zend_builtin_functions.c 18 Sep 2007 12:35:00 -0000 @@ -43,6 +43,7 @@ static ZEND_FUNCTION(error_reporting); static ZEND_FUNCTION(define); static ZEND_FUNCTION(defined); static ZEND_FUNCTION(get_class); +static ZEND_FUNCTION(get_called_class); static ZEND_FUNCTION(get_parent_class); static ZEND_FUNCTION(method_exists); static ZEND_FUNCTION(property_exists); @@ -103,6 +104,7 @@ static zend_function_entry builtin_funct ZEND_FE(define, NULL) ZEND_FE(defined, NULL) ZEND_FE(get_class, NULL) + ZEND_FE(get_called_class, NULL) ZEND_FE(get_parent_class, NULL) ZEND_FE(method_exists, NULL) ZEND_FE(property_exists, NULL) @@ -614,6 +616,27 @@ ZEND_FUNCTION(get_class) } /* }}} */ +/* {{{ proto string get_called_class() + Retrieves the class name */ +ZEND_FUNCTION(get_called_class) +{ + int dup; + + if (!ZEND_NUM_ARGS()) { + if (EG(called_scope)) { + RETURN_TEXTL(EG(called_scope)->name, EG(called_scope)->name_length, 1); + } else { + zend_error(E_WARNING, "get_called_class() called from outside a class"); + RETURN_FALSE; + } + } else { + ZEND_WRONG_PARAM_COUNT(); + RETURN_FALSE; + } +} +/* }}} */ + + /* {{{ proto string get_parent_class([mixed object]) U Retrieves the parent class name for object or class or current scope. */ ZEND_FUNCTION(get_parent_class) Index: Zend/zend_compile.c =================================================================== RCS file: /repository/ZendEngine2/zend_compile.c,v retrieving revision 1.767 diff -u -p -d -r1.767 zend_compile.c --- Zend/zend_compile.c 5 Sep 2007 07:24:52 -0000 1.767 +++ Zend/zend_compile.c 18 Sep 2007 12:35:00 -0000 @@ -1702,6 +1702,7 @@ void zend_do_fetch_class(znode *result, switch (fetch_type) { case ZEND_FETCH_CLASS_SELF: case ZEND_FETCH_CLASS_PARENT: + case ZEND_FETCH_CLASS_STATIC: SET_UNUSED(opline->op2); opline->extended_value = fetch_type; zval_dtor(&class_name->u.constant); @@ -3220,6 +3221,9 @@ void zend_do_begin_class_declaration(zno case ZEND_FETCH_CLASS_PARENT: zend_error(E_COMPILE_ERROR, "Cannot use 'parent' as class name as it is reserved"); break; + case ZEND_FETCH_CLASS_STATIC: + zend_error(E_COMPILE_ERROR, "Cannot use 'static' as class name as it is reserved"); + break; default: break; } @@ -3332,6 +3336,9 @@ void zend_do_implements_interface(znode case ZEND_FETCH_CLASS_PARENT: zend_error(E_COMPILE_ERROR, "Cannot use 'parent' as interface name as it is reserved"); break; + case ZEND_FETCH_CLASS_STATIC: + zend_error(E_COMPILE_ERROR, "Cannot use 'static' as interface name as it is reserved"); + break; default: if (CG(active_op_array)->last > 0) { opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1]; @@ -4876,6 +4883,9 @@ int zend_get_class_fetch_type(zend_uchar } else if ((class_name_len == sizeof("parent")-1) && ZEND_U_EQUAL(type, class_name, class_name_len, "parent", sizeof("parent")-1)) { return ZEND_FETCH_CLASS_PARENT; + } else if ((class_name_len == sizeof("static")-1) && + ZEND_U_EQUAL(type, class_name, class_name_len, "static", sizeof("static")-1)) { + return ZEND_FETCH_CLASS_STATIC; } else { return ZEND_FETCH_CLASS_DEFAULT; } Index: Zend/zend_compile.h =================================================================== RCS file: /repository/ZendEngine2/zend_compile.h,v retrieving revision 1.364 diff -u -p -d -r1.364 zend_compile.h --- Zend/zend_compile.h 24 Aug 2007 13:50:52 -0000 1.364 +++ Zend/zend_compile.h 18 Sep 2007 12:35:00 -0000 @@ -300,6 +300,7 @@ struct _zend_execute_data { struct _zend_op *opline; zend_function_state function_state; zend_function *fbc; /* Function Being Called */ + zend_class_entry *called_scope; zend_op_array *op_array; zval *object; union _temp_variable *Ts; @@ -632,6 +633,7 @@ int zendlex(znode *zendlval TSRMLS_DC); #define ZEND_FETCH_CLASS_GLOBAL 4 #define ZEND_FETCH_CLASS_AUTO 5 #define ZEND_FETCH_CLASS_INTERFACE 6 +#define ZEND_FETCH_CLASS_STATIC 7 #define ZEND_FETCH_CLASS_FLAGS 0xF0 #define ZEND_FETCH_CLASS_NO_NORMALIZE 0x10 #define ZEND_FETCH_CLASS_RT_NS_CHECK 0x20 Index: Zend/zend_constants.c =================================================================== RCS file: /repository/ZendEngine2/zend_constants.c,v retrieving revision 1.100 diff -u -p -d -r1.100 zend_constants.c --- Zend/zend_constants.c 11 Sep 2007 11:23:12 -0000 1.100 +++ Zend/zend_constants.c 18 Sep 2007 12:35:00 -0000 @@ -382,6 +382,14 @@ ZEND_API int zend_u_get_constant_ex(zend ce = scope->parent; } efree(lcname.v); + } else if (lcname_len == sizeof("static")-1 && + ZEND_U_EQUAL(type, lcname, lcname_len, "static", sizeof("static")-1)) { + if (EG(called_scope)) { + ce = EG(called_scope); + } else { + zend_error(E_ERROR, "Cannot access static:: when no class scope is active"); + } + efree(lcname.v); } else { /* Check for namespace constant */ zstr nsname; Index: Zend/zend_execute.c =================================================================== RCS file: /repository/ZendEngine2/zend_execute.c,v retrieving revision 1.768 diff -u -p -d -r1.768 zend_execute.c --- Zend/zend_execute.c 19 Jul 2007 15:28:47 -0000 1.768 +++ Zend/zend_execute.c 18 Sep 2007 12:35:00 -0000 @@ -150,6 +150,17 @@ static inline void zend_pzval_unlock_fre #define CV_OF(i) (EG(current_execute_data)->CVs[i]) #define CV_DEF_OF(i) (EG(active_op_array)->vars[i]) +#define CTOR_CALL_BIT 0x1 +#define CTOR_USED_BIT 0x2 + +#define IS_CTOR_CALL(ce) (((zend_uintptr_t)(ce)) & CTOR_CALL_BIT) +#define IS_CTOR_USED(ce) (((zend_uintptr_t)(ce)) & CTOR_USED_BIT) + +#define ENCODE_CTOR(ce, used) \ + ((zend_class_entry*)(((zend_uintptr_t)(ce)) | CTOR_CALL_BIT | ((used) ? CTOR_USED_BIT : 0))) +#define DECODE_CTOR(ce) \ + ((zend_class_entry*)(((zend_uintptr_t)(ce)) & ~(CTOR_CALL_BIT|CTOR_USED_BIT))) + ZEND_API zval** zend_get_compiled_variable_value(zend_execute_data *execute_data_ptr, zend_uint var) /* {{{ */ { return execute_data_ptr->CVs[var]; Index: Zend/zend_execute_API.c =================================================================== RCS file: /repository/ZendEngine2/zend_execute_API.c,v retrieving revision 1.415 diff -u -p -d -r1.415 zend_execute_API.c --- Zend/zend_execute_API.c 11 Sep 2007 11:23:12 -0000 1.415 +++ Zend/zend_execute_API.c 18 Sep 2007 12:35:00 -0000 @@ -201,6 +201,7 @@ void init_executor(TSRMLS_D) /* {{{ */ EG(exception) = NULL; EG(scope) = NULL; + EG(called_scope) = NULL; EG(This) = NULL; @@ -676,6 +677,7 @@ int zend_call_function(zend_fcall_info * zend_op_array *original_op_array; zend_op **original_opline_ptr; zend_class_entry *current_scope; + zend_class_entry *current_called_scope; zend_class_entry *calling_scope = NULL; zend_class_entry *check_scope_or_static = NULL; zval *current_this; @@ -784,6 +786,15 @@ int zend_call_function(zend_fcall_info * found = (*ce != NULL?SUCCESS:FAILURE); fci->object_pp = EG(This)?&EG(This):NULL; EX(object) = EG(This); + } else if (Z_UNILEN_PP(fci->object_pp) == sizeof("static")-1 && + ZEND_U_EQUAL(Z_TYPE_PP(fci->object_pp), Z_UNIVAL_PP(fci->object_pp), Z_UNILEN_PP(fci->object_pp), "static", sizeof("static")-1)) { + if (!EG(called_scope)) { + zend_error(E_ERROR, "Cannot access static:: when no class scope is active"); + } + ce = &(EG(called_scope)); + found = (*ce != NULL?SUCCESS:FAILURE); + fci->object_pp = EG(This)?&EG(This):NULL; + EX(object) = EG(This); } else { zend_class_entry *scope; scope = EG(active_op_array) ? EG(active_op_array)->scope : NULL; @@ -852,6 +863,9 @@ int zend_call_function(zend_fcall_info * } else if (calling_scope && clen == sizeof("parent") - 1 && ZEND_U_EQUAL(Z_TYPE_P(fci->function_name), lcname, clen, "parent", sizeof("parent")-1)) { ce_child = EG(active_op_array) && EG(active_op_array)->scope ? EG(scope)->parent : NULL; + } else if (clen == sizeof("static") - 1 && + ZEND_U_EQUAL(Z_TYPE_P(fci->function_name), lcname, clen, "static", sizeof("static")-1)) { + ce_child = EG(called_scope); } else if (zend_u_lookup_class_ex(Z_TYPE_P(fci->function_name), lcname, clen, Z_UNIVAL_P(fci->function_name), 0, &pce TSRMLS_CC) == SUCCESS) { ce_child = *pce; } @@ -1028,6 +1042,20 @@ int zend_call_function(zend_fcall_info * current_this = EG(This); + current_called_scope = EG(called_scope); + if (calling_scope) { +#ifdef ZEND_LSB2 + if (!EG(called_scope) || !EX(object) || + !instanceof_function(EG(called_scope), calling_scope TSRMLS_CC)) { +#endif + EG(called_scope) = calling_scope; +#ifdef ZEND_LSB2 + } +#endif + } else if (EX(function_state).function->type != ZEND_INTERNAL_FUNCTION) { + EG(called_scope) = NULL; + } + if (fci->object_pp) { if ((EX(function_state).function->common.fn_flags & ZEND_ACC_STATIC)) { EG(This) = NULL; @@ -1109,6 +1137,7 @@ int zend_call_function(zend_fcall_info * if (EG(This)) { zval_ptr_dtor(&EG(This)); } + EG(called_scope) = current_called_scope; EG(scope) = current_scope; EG(This) = current_this; EG(current_execute_data) = EX(prev_execute_data); @@ -1679,6 +1708,11 @@ check_fetch_type: zend_error(E_ERROR, "Cannot access parent:: when current class scope has no parent"); } return EG(scope)->parent; + case ZEND_FETCH_CLASS_STATIC: + if (!EG(called_scope)) { + zend_error(E_ERROR, "Cannot access static:: when no class scope is active"); + } + return EG(called_scope); case ZEND_FETCH_CLASS_AUTO: { if (do_normalize) { lcname = zend_u_str_case_fold(type, class_name, class_name_len, 1, &class_name_len); Index: Zend/zend_globals.h =================================================================== RCS file: /repository/ZendEngine2/zend_globals.h,v retrieving revision 1.168 diff -u -p -d -r1.168 zend_globals.h --- Zend/zend_globals.h 12 Jul 2007 09:23:48 -0000 1.168 +++ Zend/zend_globals.h 18 Sep 2007 12:35:00 -0000 @@ -185,6 +185,7 @@ struct _zend_executor_globals { HashTable *zend_constants; /* constants table */ zend_class_entry *scope; + zend_class_entry *called_scope; /* Scope of the calling class */ zval *This; Index: Zend/zend_language_parser.y =================================================================== RCS file: /repository/ZendEngine2/zend_language_parser.y,v retrieving revision 1.189 diff -u -p -d -r1.189 zend_language_parser.y --- Zend/zend_language_parser.y 24 Aug 2007 13:50:52 -0000 1.189 +++ Zend/zend_language_parser.y 18 Sep 2007 12:35:00 -0000 @@ -670,6 +670,7 @@ function_call: fully_qualified_class_name: T_STRING { $$ = $1; } + | T_STATIC { $$.op_type = IS_CONST; ZVAL_ASCII_STRINGL(&$$.u.constant, "static", sizeof("static")-1, 1);} | T_PAAMAYIM_NEKUDOTAYIM T_STRING { zend_do_build_namespace_name(&$$, NULL, &$2 TSRMLS_CC); } | fully_qualified_class_name T_PAAMAYIM_NEKUDOTAYIM T_STRING { zend_do_build_namespace_name(&$$, &$1, &$3 TSRMLS_CC); } ; Index: Zend/zend_vm_def.h =================================================================== RCS file: /repository/ZendEngine2/zend_vm_def.h,v retrieving revision 1.182 diff -u -p -d -r1.182 zend_vm_def.h --- Zend/zend_vm_def.h 11 Sep 2007 11:23:12 -0000 1.182 +++ Zend/zend_vm_def.h 18 Sep 2007 12:35:01 -0000 @@ -1736,7 +1736,7 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CA /* FIXME: type is default */ zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); function_name = GET_OP2_ZVAL_PTR(BP_VAR_R); @@ -1763,6 +1763,8 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CA zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval); } + EX(called_scope) = Z_OBJCE_P(EX(object)); + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -1789,7 +1791,7 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_ME zval *function_name; zend_class_entry *ce; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (OP1_TYPE == IS_CONST && OP2_TYPE == IS_CONST) { /* try a function in namespace */ @@ -1850,6 +1852,17 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_ME EX(fbc) = ce->constructor; } +#ifdef ZEND_LSB2 + if (EG(called_scope) && + instanceof_function(EG(called_scope), ce TSRMLS_CC)) { + EX(called_scope) = EG(called_scope); + } else { +#endif + EX(called_scope) = ce; +#ifdef ZEND_LSB2 + } +#endif + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -1864,6 +1877,7 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_ME } if ((EX(object) = EG(This))) { EX(object)->refcount++; + EX(called_scope) = Z_OBJCE_P(EX(object)); } } @@ -1879,7 +1893,7 @@ ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_N unsigned int function_name_strlen, lcname_len; zend_free_op free_op2; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (OP2_TYPE == IS_CONST) { function_name = &opline->op2.u.constant; @@ -1956,11 +1970,12 @@ ZEND_VM_HELPER(zend_do_fcall_common_help { zend_op *opline = EX(opline); zval **original_return_value; - zend_class_entry *current_scope = NULL; - zval *current_this = NULL; + zend_class_entry *current_scope; + zend_class_entry *current_called_scope; + zval *current_this; int return_value_used = RETURN_VALUE_USED(opline); zend_bool should_change_scope; - zend_op *ctor_opline; + zval *ex_object; if (EX(function_state).function->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) { if (EX(function_state).function->common.fn_flags & ZEND_ACC_ABSTRACT) { @@ -1984,9 +1999,11 @@ ZEND_VM_HELPER(zend_do_fcall_common_help || EX(function_state).function->common.scope) { should_change_scope = 1; current_this = EG(This); - EG(This) = EX(object); current_scope = EG(scope); + current_called_scope = EG(called_scope); + EG(This) = EX(object); EG(scope) = (EX(function_state).function->type == ZEND_USER_FUNCTION || !EX(object)) ? EX(function_state).function->common.scope : NULL; + EG(called_scope) = EX(function_state).function->common.scope ? EX(called_scope) : NULL; } else { should_change_scope = 0; } @@ -2008,6 +2025,9 @@ ZEND_VM_HELPER(zend_do_fcall_common_help zend_error(severity, "Non-static method %v::%v() %s be called statically", EX(function_state).function->common.scope->name, EX(function_state).function->common.function_name, severity_word); } } + + zend_ptr_stack_3_pop(&EG(arg_types_stack), (void*)&EX(called_scope), (void**)&ex_object, (void**)&EX(fbc)); + if (EX(function_state).function->type == ZEND_INTERNAL_FUNCTION) { unsigned char return_reference = EX(function_state).function->common.return_reference; @@ -2094,7 +2114,7 @@ ZEND_VM_HELPER(zend_do_fcall_common_help /* Not sure what should be done here if it's a static method */ if (EX(object)) { - Z_OBJ_HT_P(EX(object))->call_method(EX(fbc)->common.function_name, opline->extended_value, EX_T(opline->result.u.var).var.ptr, &EX_T(opline->result.u.var).var.ptr, EX(object), return_value_used TSRMLS_CC); + Z_OBJ_HT_P(EX(object))->call_method(EX(function_state).function->common.function_name, opline->extended_value, EX_T(opline->result.u.var).var.ptr, &EX_T(opline->result.u.var).var.ptr, EX(object), return_value_used TSRMLS_CC); } else { zend_error_noreturn(E_ERROR, "Cannot call overloaded function for non-object"); } @@ -2102,7 +2122,7 @@ ZEND_VM_HELPER(zend_do_fcall_common_help if (EX(function_state).function->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) { efree(EX(function_state).function->common.function_name.v); } - efree(EX(fbc)); + efree(EX(function_state).function); if (!return_value_used) { zval_ptr_dtor(&EX_T(opline->result.u.var).var.ptr); @@ -2112,11 +2132,9 @@ ZEND_VM_HELPER(zend_do_fcall_common_help } } - ctor_opline = (zend_op*)zend_ptr_stack_pop(&EG(arg_types_stack)); - if (EG(This)) { - if (EG(exception) && ctor_opline) { - if (RETURN_VALUE_USED(ctor_opline)) { + if (EG(exception) && IS_CTOR_CALL(EX(called_scope))) { + if (IS_CTOR_USED(EX(called_scope))) { EG(This)->refcount--; } if (EG(This)->refcount == 1) { @@ -2128,11 +2146,14 @@ ZEND_VM_HELPER(zend_do_fcall_common_help } } + EX(object) = ex_object; + EX(called_scope) = DECODE_CTOR(EX(called_scope)); + if (should_change_scope) { EG(This) = current_this; EG(scope) = current_scope; + EG(called_scope) = current_called_scope; } - zend_ptr_stack_2_pop(&EG(arg_types_stack), (void**)&EX(object), (void**)&EX(fbc)); EX(function_state).function = (zend_function *) EX(op_array); EG(function_state_ptr) = &EX(function_state); @@ -2160,7 +2181,7 @@ ZEND_VM_HANDLER(60, ZEND_DO_FCALL, CONST zend_free_op free_op1; zval *fname = GET_OP1_ZVAL_PTR(BP_VAR_R); - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (zend_u_hash_find(EG(function_table), Z_TYPE_P(fname), Z_UNIVAL_P(fname), Z_UNILEN_P(fname)+1, (void **) &EX(function_state).function)==FAILURE) { /* FIXME: output identifiers properly */ @@ -2631,11 +2652,12 @@ ZEND_VM_HANDLER(68, ZEND_NEW, ANY, ANY) EX_T(opline->result.u.var).var.ptr_ptr = &EX_T(opline->result.u.var).var.ptr; EX_T(opline->result.u.var).var.ptr = object_zval; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), opline); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), ENCODE_CTOR(EX(called_scope), RETURN_VALUE_USED(opline))); /* We are not handling overloaded classes right now */ EX(object) = object_zval; EX(fbc) = constructor; + EX(called_scope) = EX_T(opline->op1.u.var).class_entry; ZEND_VM_NEXT_OPCODE(); } @@ -4030,14 +4052,14 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTI } while (EX(fbc)) { - zend_op *ctor_opline = (zend_op*)zend_ptr_stack_pop(&EG(arg_types_stack)); - + EX(called_scope) = (zend_class_entry*)zend_ptr_stack_pop(&EG(arg_types_stack)); if (EX(object)) { - if (ctor_opline && RETURN_VALUE_USED(ctor_opline)) { + if (IS_CTOR_USED(EX(called_scope))) { EX(object)->refcount--; } zval_ptr_dtor(&EX(object)); } + EX(called_scope) = DECODE_CTOR(EX(called_scope)); zend_ptr_stack_2_pop(&EG(arg_types_stack), (void**)&EX(object), (void**)&EX(fbc)); } Index: Zend/zend_vm_execute.h =================================================================== RCS file: /repository/ZendEngine2/zend_vm_execute.h,v retrieving revision 1.186 diff -u -p -d -r1.186 zend_vm_execute.h --- Zend/zend_vm_execute.h 11 Sep 2007 11:23:12 -0000 1.186 +++ Zend/zend_vm_execute.h 18 Sep 2007 12:35:02 -0000 @@ -42,6 +42,7 @@ ZEND_API void execute(zend_op_array *op_ /* Initialize execute_data */ EX(fbc) = NULL; + EX(called_scope) = NULL; EX(object) = NULL; EX(old_error_reporting) = NULL; if (op_array->T < TEMP_VAR_STACK_LIMIT) { @@ -133,11 +134,12 @@ static int zend_do_fcall_common_helper_S { zend_op *opline = EX(opline); zval **original_return_value; - zend_class_entry *current_scope = NULL; - zval *current_this = NULL; + zend_class_entry *current_scope; + zend_class_entry *current_called_scope; + zval *current_this; int return_value_used = RETURN_VALUE_USED(opline); zend_bool should_change_scope; - zend_op *ctor_opline; + zval *ex_object; if (EX(function_state).function->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) { if (EX(function_state).function->common.fn_flags & ZEND_ACC_ABSTRACT) { @@ -161,9 +163,11 @@ static int zend_do_fcall_common_helper_S || EX(function_state).function->common.scope) { should_change_scope = 1; current_this = EG(This); - EG(This) = EX(object); current_scope = EG(scope); + current_called_scope = EG(called_scope); + EG(This) = EX(object); EG(scope) = (EX(function_state).function->type == ZEND_USER_FUNCTION || !EX(object)) ? EX(function_state).function->common.scope : NULL; + EG(called_scope) = EX(function_state).function->common.scope ? EX(called_scope) : NULL; } else { should_change_scope = 0; } @@ -185,6 +189,9 @@ static int zend_do_fcall_common_helper_S zend_error(severity, "Non-static method %v::%v() %s be called statically", EX(function_state).function->common.scope->name, EX(function_state).function->common.function_name, severity_word); } } + + zend_ptr_stack_3_pop(&EG(arg_types_stack), (void*)&EX(called_scope), (void**)&ex_object, (void**)&EX(fbc)); + if (EX(function_state).function->type == ZEND_INTERNAL_FUNCTION) { unsigned char return_reference = EX(function_state).function->common.return_reference; @@ -271,7 +278,7 @@ static int zend_do_fcall_common_helper_S /* Not sure what should be done here if it's a static method */ if (EX(object)) { - Z_OBJ_HT_P(EX(object))->call_method(EX(fbc)->common.function_name, opline->extended_value, EX_T(opline->result.u.var).var.ptr, &EX_T(opline->result.u.var).var.ptr, EX(object), return_value_used TSRMLS_CC); + Z_OBJ_HT_P(EX(object))->call_method(EX(function_state).function->common.function_name, opline->extended_value, EX_T(opline->result.u.var).var.ptr, &EX_T(opline->result.u.var).var.ptr, EX(object), return_value_used TSRMLS_CC); } else { zend_error_noreturn(E_ERROR, "Cannot call overloaded function for non-object"); } @@ -279,7 +286,7 @@ static int zend_do_fcall_common_helper_S if (EX(function_state).function->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) { efree(EX(function_state).function->common.function_name.v); } - efree(EX(fbc)); + efree(EX(function_state).function); if (!return_value_used) { zval_ptr_dtor(&EX_T(opline->result.u.var).var.ptr); @@ -289,11 +296,9 @@ static int zend_do_fcall_common_helper_S } } - ctor_opline = (zend_op*)zend_ptr_stack_pop(&EG(arg_types_stack)); - if (EG(This)) { - if (EG(exception) && ctor_opline) { - if (RETURN_VALUE_USED(ctor_opline)) { + if (EG(exception) && IS_CTOR_CALL(EX(called_scope))) { + if (IS_CTOR_USED(EX(called_scope))) { EG(This)->refcount--; } if (EG(This)->refcount == 1) { @@ -305,11 +310,14 @@ static int zend_do_fcall_common_helper_S } } + EX(object) = ex_object; + EX(called_scope) = DECODE_CTOR(EX(called_scope)); + if (should_change_scope) { EG(This) = current_this; EG(scope) = current_scope; + EG(called_scope) = current_called_scope; } - zend_ptr_stack_2_pop(&EG(arg_types_stack), (void**)&EX(object), (void**)&EX(fbc)); EX(function_state).function = (zend_function *) EX(op_array); EG(function_state_ptr) = &EX(function_state); @@ -431,11 +439,12 @@ static int ZEND_NEW_SPEC_HANDLER(ZEND_OP EX_T(opline->result.u.var).var.ptr_ptr = &EX_T(opline->result.u.var).var.ptr; EX_T(opline->result.u.var).var.ptr = object_zval; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), opline); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), ENCODE_CTOR(EX(called_scope), RETURN_VALUE_USED(opline))); /* We are not handling overloaded classes right now */ EX(object) = object_zval; EX(fbc) = constructor; + EX(called_scope) = EX_T(opline->op1.u.var).class_entry; ZEND_VM_NEXT_OPCODE(); } @@ -564,14 +573,14 @@ static int ZEND_HANDLE_EXCEPTION_SPEC_HA } while (EX(fbc)) { - zend_op *ctor_opline = (zend_op*)zend_ptr_stack_pop(&EG(arg_types_stack)); - + EX(called_scope) = (zend_class_entry*)zend_ptr_stack_pop(&EG(arg_types_stack)); if (EX(object)) { - if (ctor_opline && RETURN_VALUE_USED(ctor_opline)) { + if (IS_CTOR_USED(EX(called_scope))) { EX(object)->refcount--; } zval_ptr_dtor(&EX(object)); } + EX(called_scope) = DECODE_CTOR(EX(called_scope)); zend_ptr_stack_2_pop(&EG(arg_types_stack), (void**)&EX(object), (void**)&EX(fbc)); } @@ -676,7 +685,7 @@ static int ZEND_INIT_FCALL_BY_NAME_SPEC_ unsigned int function_name_strlen, lcname_len; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (IS_CONST == IS_CONST) { function_name = &opline->op2.u.constant; @@ -874,7 +883,7 @@ static int ZEND_INIT_FCALL_BY_NAME_SPEC_ unsigned int function_name_strlen, lcname_len; zend_free_op free_op2; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (IS_TMP_VAR == IS_CONST) { function_name = &opline->op2.u.constant; @@ -987,7 +996,7 @@ static int ZEND_INIT_FCALL_BY_NAME_SPEC_ unsigned int function_name_strlen, lcname_len; zend_free_op free_op2; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (IS_VAR == IS_CONST) { function_name = &opline->op2.u.constant; @@ -1129,7 +1138,7 @@ static int ZEND_INIT_FCALL_BY_NAME_SPEC_ unsigned int function_name_strlen, lcname_len; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (IS_CV == IS_CONST) { function_name = &opline->op2.u.constant; @@ -1501,7 +1510,7 @@ static int ZEND_DO_FCALL_SPEC_CONST_HAND zval *fname = &opline->op1.u.constant; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (zend_u_hash_find(EG(function_table), Z_TYPE_P(fname), Z_UNIVAL_P(fname), Z_UNILEN_P(fname)+1, (void **) &EX(function_state).function)==FAILURE) { /* FIXME: output identifiers properly */ @@ -2541,7 +2550,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ zval *function_name; zend_class_entry *ce; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (IS_CONST == IS_CONST && IS_CONST == IS_CONST) { /* try a function in namespace */ @@ -2602,6 +2611,17 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ EX(fbc) = ce->constructor; } +#ifdef ZEND_LSB2 + if (EG(called_scope) && + instanceof_function(EG(called_scope), ce TSRMLS_CC)) { + EX(called_scope) = EG(called_scope); + } else { +#endif + EX(called_scope) = ce; +#ifdef ZEND_LSB2 + } +#endif + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -2616,6 +2636,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ } if ((EX(object) = EG(This))) { EX(object)->refcount++; + EX(called_scope) = Z_OBJCE_P(EX(object)); } } @@ -3092,7 +3113,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ zval *function_name; zend_class_entry *ce; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (IS_CONST == IS_CONST && IS_TMP_VAR == IS_CONST) { /* try a function in namespace */ @@ -3153,6 +3174,17 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ EX(fbc) = ce->constructor; } +#ifdef ZEND_LSB2 + if (EG(called_scope) && + instanceof_function(EG(called_scope), ce TSRMLS_CC)) { + EX(called_scope) = EG(called_scope); + } else { +#endif + EX(called_scope) = ce; +#ifdef ZEND_LSB2 + } +#endif + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -3167,6 +3199,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ } if ((EX(object) = EG(This))) { EX(object)->refcount++; + EX(called_scope) = Z_OBJCE_P(EX(object)); } } @@ -3539,7 +3572,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ zval *function_name; zend_class_entry *ce; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (IS_CONST == IS_CONST && IS_VAR == IS_CONST) { /* try a function in namespace */ @@ -3600,6 +3633,17 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ EX(fbc) = ce->constructor; } +#ifdef ZEND_LSB2 + if (EG(called_scope) && + instanceof_function(EG(called_scope), ce TSRMLS_CC)) { + EX(called_scope) = EG(called_scope); + } else { +#endif + EX(called_scope) = ce; +#ifdef ZEND_LSB2 + } +#endif + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -3614,6 +3658,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ } if ((EX(object) = EG(This))) { EX(object)->refcount++; + EX(called_scope) = Z_OBJCE_P(EX(object)); } } @@ -3752,7 +3797,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ zval *function_name; zend_class_entry *ce; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (IS_CONST == IS_CONST && IS_UNUSED == IS_CONST) { /* try a function in namespace */ @@ -3813,6 +3858,17 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ EX(fbc) = ce->constructor; } +#ifdef ZEND_LSB2 + if (EG(called_scope) && + instanceof_function(EG(called_scope), ce TSRMLS_CC)) { + EX(called_scope) = EG(called_scope); + } else { +#endif + EX(called_scope) = ce; +#ifdef ZEND_LSB2 + } +#endif + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -3827,6 +3883,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ } if ((EX(object) = EG(This))) { EX(object)->refcount++; + EX(called_scope) = Z_OBJCE_P(EX(object)); } } @@ -4167,7 +4224,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ zval *function_name; zend_class_entry *ce; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (IS_CONST == IS_CONST && IS_CV == IS_CONST) { /* try a function in namespace */ @@ -4228,6 +4285,17 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ EX(fbc) = ce->constructor; } +#ifdef ZEND_LSB2 + if (EG(called_scope) && + instanceof_function(EG(called_scope), ce TSRMLS_CC)) { + EX(called_scope) = EG(called_scope); + } else { +#endif + EX(called_scope) = ce; +#ifdef ZEND_LSB2 + } +#endif + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -4242,6 +4310,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ } if ((EX(object) = EG(This))) { EX(object)->refcount++; + EX(called_scope) = Z_OBJCE_P(EX(object)); } } @@ -5766,7 +5835,7 @@ static int ZEND_INIT_METHOD_CALL_SPEC_TM /* FIXME: type is default */ zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); function_name = &opline->op2.u.constant; @@ -5793,6 +5862,8 @@ static int ZEND_INIT_METHOD_CALL_SPEC_TM zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval); } + EX(called_scope) = Z_OBJCE_P(EX(object)); + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -6215,7 +6286,7 @@ static int ZEND_INIT_METHOD_CALL_SPEC_TM /* FIXME: type is default */ zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); function_name = _get_zval_ptr_tmp(&opline->op2, EX(Ts), &free_op2 TSRMLS_CC); @@ -6242,6 +6313,8 @@ static int ZEND_INIT_METHOD_CALL_SPEC_TM zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval); } + EX(called_scope) = Z_OBJCE_P(EX(object)); + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -6666,7 +6739,7 @@ static int ZEND_INIT_METHOD_CALL_SPEC_TM /* FIXME: type is default */ zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); function_name = _get_zval_ptr_var(&opline->op2, EX(Ts), &free_op2 TSRMLS_CC); @@ -6693,6 +6766,8 @@ static int ZEND_INIT_METHOD_CALL_SPEC_TM zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval); } + EX(called_scope) = Z_OBJCE_P(EX(object)); + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -7210,7 +7285,7 @@ static int ZEND_INIT_METHOD_CALL_SPEC_TM /* FIXME: type is default */ zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); function_name = _get_zval_ptr_cv(&opline->op2, EX(Ts), BP_VAR_R TSRMLS_CC); @@ -7237,6 +7312,8 @@ static int ZEND_INIT_METHOD_CALL_SPEC_TM zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval); } + EX(called_scope) = Z_OBJCE_P(EX(object)); + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -9962,7 +10039,7 @@ static int ZEND_INIT_METHOD_CALL_SPEC_VA /* FIXME: type is default */ zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); function_name = &opline->op2.u.constant; @@ -9989,6 +10066,8 @@ static int ZEND_INIT_METHOD_CALL_SPEC_VA zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval); } + EX(called_scope) = Z_OBJCE_P(EX(object)); + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -10014,7 +10093,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ zval *function_name; zend_class_entry *ce; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (IS_VAR == IS_CONST && IS_CONST == IS_CONST) { /* try a function in namespace */ @@ -10075,6 +10154,17 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ EX(fbc) = ce->constructor; } +#ifdef ZEND_LSB2 + if (EG(called_scope) && + instanceof_function(EG(called_scope), ce TSRMLS_CC)) { + EX(called_scope) = EG(called_scope); + } else { +#endif + EX(called_scope) = ce; +#ifdef ZEND_LSB2 + } +#endif + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -10089,6 +10179,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ } if ((EX(object) = EG(This))) { EX(object)->refcount++; + EX(called_scope) = Z_OBJCE_P(EX(object)); } } @@ -11659,7 +11750,7 @@ static int ZEND_INIT_METHOD_CALL_SPEC_VA /* FIXME: type is default */ zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); function_name = _get_zval_ptr_tmp(&opline->op2, EX(Ts), &free_op2 TSRMLS_CC); @@ -11686,6 +11777,8 @@ static int ZEND_INIT_METHOD_CALL_SPEC_VA zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval); } + EX(called_scope) = Z_OBJCE_P(EX(object)); + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -11712,7 +11805,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ zval *function_name; zend_class_entry *ce; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (IS_VAR == IS_CONST && IS_TMP_VAR == IS_CONST) { /* try a function in namespace */ @@ -11773,6 +11866,17 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ EX(fbc) = ce->constructor; } +#ifdef ZEND_LSB2 + if (EG(called_scope) && + instanceof_function(EG(called_scope), ce TSRMLS_CC)) { + EX(called_scope) = EG(called_scope); + } else { +#endif + EX(called_scope) = ce; +#ifdef ZEND_LSB2 + } +#endif + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -11787,6 +11891,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ } if ((EX(object) = EG(This))) { EX(object)->refcount++; + EX(called_scope) = Z_OBJCE_P(EX(object)); } } @@ -13329,7 +13434,7 @@ static int ZEND_INIT_METHOD_CALL_SPEC_VA /* FIXME: type is default */ zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); function_name = _get_zval_ptr_var(&opline->op2, EX(Ts), &free_op2 TSRMLS_CC); @@ -13356,6 +13461,8 @@ static int ZEND_INIT_METHOD_CALL_SPEC_VA zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval); } + EX(called_scope) = Z_OBJCE_P(EX(object)); + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -13382,7 +13489,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ zval *function_name; zend_class_entry *ce; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (IS_VAR == IS_CONST && IS_VAR == IS_CONST) { /* try a function in namespace */ @@ -13443,6 +13550,17 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ EX(fbc) = ce->constructor; } +#ifdef ZEND_LSB2 + if (EG(called_scope) && + instanceof_function(EG(called_scope), ce TSRMLS_CC)) { + EX(called_scope) = EG(called_scope); + } else { +#endif + EX(called_scope) = ce; +#ifdef ZEND_LSB2 + } +#endif + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -13457,6 +13575,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ } if ((EX(object) = EG(This))) { EX(object)->refcount++; + EX(called_scope) = Z_OBJCE_P(EX(object)); } } @@ -14250,7 +14369,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ zval *function_name; zend_class_entry *ce; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (IS_VAR == IS_CONST && IS_UNUSED == IS_CONST) { /* try a function in namespace */ @@ -14311,6 +14430,17 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ EX(fbc) = ce->constructor; } +#ifdef ZEND_LSB2 + if (EG(called_scope) && + instanceof_function(EG(called_scope), ce TSRMLS_CC)) { + EX(called_scope) = EG(called_scope); + } else { +#endif + EX(called_scope) = ce; +#ifdef ZEND_LSB2 + } +#endif + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -14325,6 +14455,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ } if ((EX(object) = EG(This))) { EX(object)->refcount++; + EX(called_scope) = Z_OBJCE_P(EX(object)); } } @@ -15518,7 +15649,7 @@ static int ZEND_INIT_METHOD_CALL_SPEC_VA /* FIXME: type is default */ zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); function_name = _get_zval_ptr_cv(&opline->op2, EX(Ts), BP_VAR_R TSRMLS_CC); @@ -15545,6 +15676,8 @@ static int ZEND_INIT_METHOD_CALL_SPEC_VA zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval); } + EX(called_scope) = Z_OBJCE_P(EX(object)); + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -15570,7 +15703,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ zval *function_name; zend_class_entry *ce; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (IS_VAR == IS_CONST && IS_CV == IS_CONST) { /* try a function in namespace */ @@ -15631,6 +15764,17 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ EX(fbc) = ce->constructor; } +#ifdef ZEND_LSB2 + if (EG(called_scope) && + instanceof_function(EG(called_scope), ce TSRMLS_CC)) { + EX(called_scope) = EG(called_scope); + } else { +#endif + EX(called_scope) = ce; +#ifdef ZEND_LSB2 + } +#endif + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -15645,6 +15789,7 @@ static int ZEND_INIT_STATIC_METHOD_CALL_ } if ((EX(object) = EG(This))) { EX(object)->refcount++; + EX(called_scope) = Z_OBJCE_P(EX(object)); } } @@ -16817,7 +16962,7 @@ static int ZEND_INIT_METHOD_CALL_SPEC_UN /* FIXME: type is default */ zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); function_name = &opline->op2.u.constant; @@ -16844,6 +16989,8 @@ static int ZEND_INIT_METHOD_CALL_SPEC_UN zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval); } + EX(called_scope) = Z_OBJCE_P(EX(object)); + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -17900,7 +18047,7 @@ static int ZEND_INIT_METHOD_CALL_SPEC_UN /* FIXME: type is default */ zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); function_name = _get_zval_ptr_tmp(&opline->op2, EX(Ts), &free_op2 TSRMLS_CC); @@ -17927,6 +18074,8 @@ static int ZEND_INIT_METHOD_CALL_SPEC_UN zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval); } + EX(called_scope) = Z_OBJCE_P(EX(object)); + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -18917,7 +19066,7 @@ static int ZEND_INIT_METHOD_CALL_SPEC_UN /* FIXME: type is default */ zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); function_name = _get_zval_ptr_var(&opline->op2, EX(Ts), &free_op2 TSRMLS_CC); @@ -18944,6 +19093,8 @@ static int ZEND_INIT_METHOD_CALL_SPEC_UN zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval); } + EX(called_scope) = Z_OBJCE_P(EX(object)); + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -20199,7 +20350,7 @@ static int ZEND_INIT_METHOD_CALL_SPEC_UN /* FIXME: type is default */ zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); function_name = _get_zval_ptr_cv(&opline->op2, EX(Ts), BP_VAR_R TSRMLS_CC); @@ -20226,6 +20377,8 @@ static int ZEND_INIT_METHOD_CALL_SPEC_UN zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval); } + EX(called_scope) = Z_OBJCE_P(EX(object)); + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -22969,7 +23122,7 @@ static int ZEND_INIT_METHOD_CALL_SPEC_CV /* FIXME: type is default */ zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); function_name = &opline->op2.u.constant; @@ -22996,6 +23149,8 @@ static int ZEND_INIT_METHOD_CALL_SPEC_CV zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval); } + EX(called_scope) = Z_OBJCE_P(EX(object)); + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -24504,7 +24659,7 @@ static int ZEND_INIT_METHOD_CALL_SPEC_CV /* FIXME: type is default */ zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); function_name = _get_zval_ptr_tmp(&opline->op2, EX(Ts), &free_op2 TSRMLS_CC); @@ -24531,6 +24686,8 @@ static int ZEND_INIT_METHOD_CALL_SPEC_CV zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval); } + EX(called_scope) = Z_OBJCE_P(EX(object)); + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -26078,7 +26235,7 @@ static int ZEND_INIT_METHOD_CALL_SPEC_CV /* FIXME: type is default */ zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); function_name = _get_zval_ptr_var(&opline->op2, EX(Ts), &free_op2 TSRMLS_CC); @@ -26105,6 +26262,8 @@ static int ZEND_INIT_METHOD_CALL_SPEC_CV zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval); } + EX(called_scope) = Z_OBJCE_P(EX(object)); + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { @@ -28083,7 +28242,7 @@ static int ZEND_INIT_METHOD_CALL_SPEC_CV /* FIXME: type is default */ zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; - zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), NULL); + zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); function_name = _get_zval_ptr_cv(&opline->op2, EX(Ts), BP_VAR_R TSRMLS_CC); @@ -28110,6 +28269,8 @@ static int ZEND_INIT_METHOD_CALL_SPEC_CV zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval); } + EX(called_scope) = Z_OBJCE_P(EX(object)); + if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) { EX(object) = NULL; } else { Index: Zend/zend_vm_execute.skl =================================================================== RCS file: /repository/ZendEngine2/zend_vm_execute.skl,v retrieving revision 1.7 diff -u -p -d -r1.7 zend_vm_execute.skl --- Zend/zend_vm_execute.skl 21 Jul 2007 00:34:41 -0000 1.7 +++ Zend/zend_vm_execute.skl 18 Sep 2007 12:35:02 -0000 @@ -13,6 +13,7 @@ ZEND_API void {%EXECUTOR_NAME%}(zend_op_ /* Initialize execute_data */ EX(fbc) = NULL; + EX(called_scope) = NULL; EX(object) = NULL; EX(old_error_reporting) = NULL; if (op_array->T < TEMP_VAR_STACK_LIMIT) { Index: Zend/tests/lsb_001.phpt =================================================================== RCS file: Zend/tests/lsb_001.phpt diff -N Zend/tests/lsb_001.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_001.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,61 @@ +--TEST-- +ZE2 Late Static Binding in a static function +--FILE-- +<?php + +class TestClass { + protected static $staticVar = 'TestClassStatic'; + const CLASS_CONST = 'TestClassConst'; + + protected static function staticFunction() { + return 'TestClassFunction'; + } + + public static function testStaticVar() { + return static::$staticVar; + } + + public static function testClassConst() { + return static::CLASS_CONST; + } + + public static function testStaticFunction() { + return static::staticFunction(); + } +} + +class ChildClass1 extends TestClass { + protected static $staticVar = 'ChildClassStatic'; + const CLASS_CONST = 'ChildClassConst'; + + protected static function staticFunction() { + return 'ChildClassFunction'; + } +} + +class ChildClass2 extends TestClass {} + +echo TestClass::testStaticVar() . "\n"; +echo TestClass::testClassConst() . "\n"; +echo TestClass::testStaticFunction() . "\n"; + +echo ChildClass1::testStaticVar() . "\n"; +echo ChildClass1::testClassConst() . "\n"; +echo ChildClass1::testStaticFunction() . "\n"; + +echo ChildClass2::testStaticVar() . "\n"; +echo ChildClass2::testClassConst() . "\n"; +echo ChildClass2::testStaticFunction() . "\n"; +?> +==DONE== +--EXPECTF-- +TestClassStatic +TestClassConst +TestClassFunction +ChildClassStatic +ChildClassConst +ChildClassFunction +TestClassStatic +TestClassConst +TestClassFunction +==DONE== Index: Zend/tests/lsb_002.phpt =================================================================== RCS file: Zend/tests/lsb_002.phpt diff -N Zend/tests/lsb_002.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_002.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,66 @@ +--TEST-- +ZE2 Late Static Binding in an instance function +--FILE-- +<?php + +class TestClass { + protected static $staticVar = 'TestClassStatic'; + const CLASS_CONST = 'TestClassConst'; + + protected static function staticFunction() { + return 'TestClassFunction'; + } + + public function testStaticVar() { + return static::$staticVar; + } + + public function testClassConst() { + return static::CLASS_CONST; + } + + public function testStaticFunction() { + return static::staticFunction(); + } +} + +class ChildClass1 extends TestClass { + protected static $staticVar = 'ChildClassStatic'; + const CLASS_CONST = 'ChildClassConst'; + + protected static function staticFunction() { + return 'ChildClassFunction'; + } +} + +class ChildClass2 extends TestClass {} + +$testClass = new TestClass(); +$childClass1 = new ChildClass1(); +$childClass2 = new ChildClass2(); + + +echo $testClass->testStaticVar() . "\n"; +echo $testClass->testClassConst() . "\n"; +echo $testClass->testStaticFunction() . "\n"; + +echo $childClass1->testStaticVar() . "\n"; +echo $childClass1->testClassConst() . "\n"; +echo $childClass1->testStaticFunction() . "\n"; + +echo $childClass2->testStaticVar() . "\n"; +echo $childClass2->testClassConst() . "\n"; +echo $childClass2->testStaticFunction() . "\n"; +?> +==DONE== +--EXPECTF-- +TestClassStatic +TestClassConst +TestClassFunction +ChildClassStatic +ChildClassConst +ChildClassFunction +TestClassStatic +TestClassConst +TestClassFunction +==DONE== Index: Zend/tests/lsb_003.phpt =================================================================== RCS file: Zend/tests/lsb_003.phpt diff -N Zend/tests/lsb_003.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_003.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,24 @@ +--TEST-- +ZE2 Late Static Binding creating a new class with 'static' +--FILE-- +<?php + +class TestClass { + public static function createInstance() { + return new static(); + } +} + +class ChildClass extends TestClass {} + +$testClass = TestClass::createInstance(); +$childClass = ChildClass::createInstance(); + +echo get_class($testClass) . "\n"; +echo get_class($childClass) . "\n"; +?> +==DONE== +--EXPECTF-- +TestClass +ChildClass +==DONE== Index: Zend/tests/lsb_004.phpt =================================================================== RCS file: Zend/tests/lsb_004.phpt diff -N Zend/tests/lsb_004.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_004.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,21 @@ +--TEST-- +ZE2 Late Static Binding testing get_called_class() +--FILE-- +<?php + +class TestClass { + public static function getClassName() { + return get_called_class(); + } +} + +class ChildClass extends TestClass {} + +echo TestClass::getClassName() . "\n"; +echo ChildClass::getClassName() . "\n"; +?> +==DONE== +--EXPECTF-- +TestClass +ChildClass +==DONE== Index: Zend/tests/lsb_005.phpt =================================================================== RCS file: Zend/tests/lsb_005.phpt diff -N Zend/tests/lsb_005.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_005.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,51 @@ +--TEST-- +ZE2 Late Static Binding stacking static calleds +--FILE-- +<?php + +class TestA { + public static function test() { + echo get_class(new static()) . "\n"; + TestB::test(); + echo get_class(new static()) . "\n"; + TestC::test(); + echo get_class(new static()) . "\n"; + TestBB::test(); + echo get_class(new static()) . "\n"; + } +} + +class TestB { + public static function test() { + echo get_class(new static()) . "\n"; + TestC::test(); + echo get_class(new static()) . "\n"; + } +} + +class TestC { + public static function test() { + echo get_class(new static()) . "\n"; + } +} + +class TestBB extends TestB { +} + +TestA::test(); + +?> +==DONE== +--EXPECTF-- +TestA +TestB +TestC +TestB +TestA +TestC +TestA +TestBB +TestC +TestBB +TestA +==DONE== Index: Zend/tests/lsb_006.phpt =================================================================== RCS file: Zend/tests/lsb_006.phpt diff -N Zend/tests/lsb_006.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_006.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,12 @@ +--TEST-- +ZE2 Late Static Binding ensuring extending 'static' is not allowed +--FILE-- +<?php + +class Foo extends static { +} + +?> +==DONE== +--EXPECTF-- +Fatal error: Cannot use 'static' as class name as it is reserved in %s on line %d Index: Zend/tests/lsb_007.phpt =================================================================== RCS file: Zend/tests/lsb_007.phpt diff -N Zend/tests/lsb_007.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_007.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,12 @@ +--TEST-- +ZE2 Late Static Binding ensuring implementing 'static' is not allowed +--FILE-- +<?php + +class Foo implements static { +} + +?> +==DONE== +--EXPECTF-- +Fatal error: Cannot use 'static' as interface name as it is reserved in %s on line %d Index: Zend/tests/lsb_008.phpt =================================================================== RCS file: Zend/tests/lsb_008.phpt diff -N Zend/tests/lsb_008.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_008.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,8 @@ +--TEST-- +ZE2 Late Static Binding class name "static" +--FILE-- +<?php +class static { +} +--EXPECTF-- +Parse error: syntax error, unexpected T_STATIC, expecting T_STRING in %slsb_008.php on line 2 Index: Zend/tests/lsb_009.phpt =================================================================== RCS file: Zend/tests/lsb_009.phpt diff -N Zend/tests/lsb_009.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_009.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,8 @@ +--TEST-- +ZE2 Late Static Binding interface name "static" +--FILE-- +<?php +interface static { +} +--EXPECTF-- +Parse error: syntax error, unexpected T_STATIC, expecting T_STRING in %slsb_009.php on line 2 Index: Zend/tests/lsb_010.phpt =================================================================== RCS file: Zend/tests/lsb_010.phpt diff -N Zend/tests/lsb_010.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_010.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,38 @@ +--TEST-- +ZE2 Late Static Binding using static:: in functions called by non execute() calls and constructors. +--FILE-- +<?php + +class Foo { + protected static $className = 'Foo'; + public static function bar() { + echo static::$className . "::bar\n"; + } + public function __construct() { + echo static::$className . "::__construct\n"; + } + public function __destruct() { + echo static::$className . "::__destruct\n"; + } +} + +class FooChild extends Foo { + protected static $className = 'FooChild'; +} + +register_shutdown_function(array('Foo', 'bar')); +register_shutdown_function(array('FooChild', 'bar')); + +$foo = new Foo(); +$fooChild = new FooChild(); +unset($foo); +unset($fooChild); + +?> +--EXPECTF-- +Foo::__construct +FooChild::__construct +Foo::__destruct +FooChild::__destruct +Foo::bar +FooChild::bar Index: Zend/tests/lsb_011.phpt =================================================================== RCS file: Zend/tests/lsb_011.phpt diff -N Zend/tests/lsb_011.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_011.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,23 @@ +--TEST-- +ZE2 Late Static Binding call to static::method() from internal function (array) +--FILE-- +<?php + +class Test1 { + static function ok() { + echo "bug"; + } + static function test() { + call_user_func(array("static","ok")); + } +} + +class Test2 extends Test1 { + static function ok() { + echo "ok"; + } +} +Test2::test(); +?> +--EXPECT-- +ok Index: Zend/tests/lsb_012.phpt =================================================================== RCS file: Zend/tests/lsb_012.phpt diff -N Zend/tests/lsb_012.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_012.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,23 @@ +--TEST-- +ZE2 Late Static Binding call to static::method() from internal function (string) +--FILE-- +<?php + +class Test1 { + static function ok() { + echo "bug"; + } + static function test() { + call_user_func("static::ok"); + } +} + +class Test2 extends Test1 { + static function ok() { + echo "ok"; + } +} +Test2::test(); +?> +--EXPECT-- +ok Index: Zend/tests/lsb_013.phpt =================================================================== RCS file: Zend/tests/lsb_013.phpt diff -N Zend/tests/lsb_013.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_013.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,24 @@ +--TEST-- +ZE2 Late Static Binding is_callable() and static::method() +--FILE-- +<?php + +class Test1 { + static function test() { + var_dump(is_callable("static::ok")); + var_dump(is_callable(array("static","ok"))); + } +} + +class Test2 extends Test1 { + static function ok() { + } +} +Test1::test(); +Test2::test(); +?> +--EXPECT-- +bool(false) +bool(false) +bool(true) +bool(true) Index: Zend/tests/lsb_014.phpt =================================================================== RCS file: Zend/tests/lsb_014.phpt diff -N Zend/tests/lsb_014.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_014.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,24 @@ +--TEST-- +ZE2 Late Static Binding access to static::const through defined() anf get_constant() +--FILE-- +<?php + +class Test1 { + static function test() { + var_dump(defined("static::ok")); + if (defined("static::ok")) { + echo constant("static::ok"); + } + } +} + +class Test2 extends Test1 { + const ok = "ok"; +} +Test1::test(); +Test2::test(); +?> +--EXPECT-- +bool(false) +bool(true) +ok Index: Zend/tests/lsb_015.phpt =================================================================== RCS file: Zend/tests/lsb_015.phpt diff -N Zend/tests/lsb_015.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_015.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,92 @@ +--TEST-- +ZE2 Late Static Binding with exceptions +--FILE-- +<?php +function foo() { + B::throwException(); +} +class C { + public static function bla() { + B::throwException(); + } + public static function getException() { + return new Exception(); + + } +} +class A { + + public static function throwException_after() { + C::bla(); + } + public static function throwException() { + throw C::getException(); + } + public static function test() { + static::who(); + } + public static function who() { + echo "A\n"; + } + + public static function mycatch() { + try { + static::who(); + B::throwException_after(); + } catch(Exception $e) { + static::who(); + A::test(); + static::who(); + B::test(); + static::who(); + + self::simpleCatch(); + static::who(); + } + } + + public static function simpleCatch() { + try { + static::who(); + throw new Exception(); + } catch (Exception $e) { + static::who(); + } + } +} + +class B extends A { + public static function who() { + echo "B\n"; + } + +} + +echo "via A:\n"; +A::myCatch(); +echo "via B:\n"; +B::myCatch(); +?> +==DONE== +--EXPECTF-- +via A: +A +A +A +A +B +A +A +A +A +via B: +B +B +A +B +B +B +A +A +B +==DONE== Index: Zend/tests/lsb_016.phpt =================================================================== RCS file: Zend/tests/lsb_016.phpt diff -N Zend/tests/lsb_016.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_016.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,41 @@ +--TEST-- +ZE2 Late Static Binding within hooks/magic methods +--FILE-- +<?php + +class TestChild extends TestParent { + + public static function who() { + echo __CLASS__."\n"; + } +} + +class TestParent { + + public function __get($var) { + static::who(); + } + + public function __set($var, $val) { + static::who(); + } + + public function __call($name, $args) { + static::who(); + } + + public static function who() { + echo __CLASS__."\n"; + } +} +$o = new TestChild; +$o->test(); +$o->a = "b"; +echo $o->a; +?> +==DONE== +--EXPECTF-- +TestChild +TestChild +TestChild +==DONE== Index: Zend/tests/lsb_017.phpt =================================================================== RCS file: Zend/tests/lsb_017.phpt diff -N Zend/tests/lsb_017.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_017.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,29 @@ +--TEST-- +ZE2 Late Static Binding nested calls +--FILE-- +<?php +class A { + public static function test($x=null) { + if (!is_null($x)) { + echo "$x\n"; + } + return get_called_class(); + } +} + +class B extends A { +} +class C extends A { +} +class D extends A { +} + +echo A::test(B::test(C::test(D::test())))."\n"; +?> +==DONE== +--EXPECT-- +D +C +B +A +==DONE== Index: Zend/tests/lsb_018.phpt =================================================================== RCS file: Zend/tests/lsb_018.phpt diff -N Zend/tests/lsb_018.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_018.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,29 @@ +--TEST-- +ZE2 Late Static Binding static:: and inheritance +--FILE-- +<?php + +class Test1 { + const ok = "bug1"; + static function test() { + echo static::ok; + } +} + +class Test2 extends Test1 { + const ok = "bug2"; + static function test() { + Test1::test(); + } +} + +class Test3 extends Test2 { + const ok = "ok"; + static function test() { + Test2::test(); + } +} +Test3::test(); +?> +--EXPECT-- +ok Index: Zend/tests/lsb_019.phpt =================================================================== RCS file: Zend/tests/lsb_019.phpt diff -N Zend/tests/lsb_019.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/lsb_019.phpt 18 Sep 2007 12:35:02 -0000 @@ -0,0 +1,29 @@ +--TEST-- +ZE2 Late Static Binding static:: and inheritance +--FILE-- +<?php + +class Test1 { + const ok = "bug1"; + static function test() { + echo static::ok; + } +} + +class Test2 extends Test1 { + const ok = "ok"; + static function test() { + Test1::test(); + } +} + +class Test3 extends Test1 { + const ok = "bug3"; + static function test() { + Test2::test(); + } +} +Test3::test(); +?> +--EXPECT-- +ok
« previous php.internals (#32367) next »