Index: Zend/zend_builtin_functions.c =================================================================== RCS file: /repository/ZendEngine2/zend_builtin_functions.c,v retrieving revision 1.342 diff -u -p -r1.342 zend_builtin_functions.c --- Zend/zend_builtin_functions.c 10 Jul 2007 15:06:58 -0000 1.342 +++ Zend/zend_builtin_functions.c 24 Jul 2007 06:30:06 -0000 @@ -43,6 +43,7 @@ static ZEND_FUNCTION(define); static ZEND_FUNCTION(defined); static ZEND_FUNCTION(get_class); static ZEND_FUNCTION(get_parent_class); +static ZEND_FUNCTION(get_called_class); static ZEND_FUNCTION(method_exists); static ZEND_FUNCTION(property_exists); static ZEND_FUNCTION(class_exists); @@ -103,6 +104,7 @@ static zend_function_entry builtin_funct ZEND_FE(defined, NULL) ZEND_FE(get_class, NULL) ZEND_FE(get_parent_class, NULL) + ZEND_FE(get_called_class, NULL) ZEND_FE(method_exists, NULL) ZEND_FE(property_exists, NULL) ZEND_FE(class_exists, NULL) @@ -657,6 +659,46 @@ ZEND_FUNCTION(get_parent_class) } /* }}} */ +/* {{{ proto string get_called_class() U + Retrieves the class name that were initially called. @see static:: */ +ZEND_FUNCTION(get_called_class) +{ + zend_class_entry *ce = EG(scope); + zend_class_entry *last_scope = EG(scope); + + if (ZEND_NUM_ARGS()) { + ZEND_WRONG_PARAM_COUNT(); + } + + if (!ce) { + RETURN_FALSE; + } + + int i = EG(class_call_stack).top; + while (--i >= 0) { + zend_class_entry *current_scope = EG(class_call_stack).elements[i]; + + if (current_scope == NULL) { + break; + } + + /* check the relationship between the scopes */ + if(instanceof_function(ce, current_scope) != 1 && + instanceof_function(current_scope, ce) != 1) { + break; + } else { + last_scope = current_scope; + } + } + + if (last_scope) { + RETURN_TEXTL(last_scope->name, last_scope->name_length, 1); + } else { + RETURN_FALSE; + } +} +/* }}} */ + static void is_a_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool only_subclass) /* {{{ */ { zval **obj, **class_name; Index: Zend/zend_compile.c =================================================================== RCS file: /repository/ZendEngine2/zend_compile.c,v retrieving revision 1.755 diff -u -p -r1.755 zend_compile.c --- Zend/zend_compile.c 13 Jul 2007 08:19:51 -0000 1.755 +++ Zend/zend_compile.c 24 Jul 2007 06:30:08 -0000 @@ -1674,6 +1674,7 @@ void zend_do_fetch_class(znode *result, fetch_type = zend_get_class_fetch_type(Z_TYPE(class_name->u.constant), Z_UNIVAL(class_name->u.constant), Z_UNILEN(class_name->u.constant)); switch (fetch_type) { case ZEND_FETCH_CLASS_SELF: + case ZEND_FETCH_CLASS_LATE: case ZEND_FETCH_CLASS_PARENT: SET_UNUSED(opline->op2); opline->extended_value = fetch_type; @@ -3099,7 +3100,9 @@ void zend_do_begin_class_declaration(zno if ((lcname_len == sizeof("self")-1 && ZEND_U_EQUAL(Z_TYPE(class_name->u.constant), lcname, lcname_len, "self", sizeof("self")-1)) || - (lcname_len == sizeof("parent")-1 && + (lcname_len == sizeof("static")-1 && + ZEND_U_EQUAL(Z_TYPE(class_name->u.constant), lcname, lcname_len, "static", sizeof("static")-1)) || + (lcname_len == sizeof("parent")-1 && ZEND_U_EQUAL(Z_TYPE(class_name->u.constant), lcname, lcname_len, "parent", sizeof("parent")-1))) { efree(lcname.v); zend_error(E_COMPILE_ERROR, "Cannot use '%R' as class name as it is reserved", Z_TYPE(class_name->u.constant), Z_UNIVAL(class_name->u.constant)); @@ -3128,6 +3131,9 @@ void zend_do_begin_class_declaration(zno if (parent_class_name && parent_class_name->op_type != IS_UNUSED) { switch (parent_class_name->u.EA.type) { + case ZEND_FETCH_CLASS_LATE: + zend_error(E_COMPILE_ERROR, "Cannot use 'static' as class name as it is reserved"); + break; case ZEND_FETCH_CLASS_SELF: zend_error(E_COMPILE_ERROR, "Cannot use 'self' as class name as it is reserved"); break; @@ -3240,6 +3246,9 @@ void zend_do_implements_interface(znode zend_do_fetch_class(&interface_node, interface_name TSRMLS_CC); switch (interface_node.u.EA.type) { + case ZEND_FETCH_CLASS_LATE: + zend_error(E_COMPILE_ERROR, "Cannot use 'static' as interface name as it is reserved"); + break; case ZEND_FETCH_CLASS_SELF: zend_error(E_COMPILE_ERROR, "Cannot use 'self' as interface name as it is reserved"); break; @@ -4693,6 +4702,9 @@ int zend_get_class_fetch_type(zend_uchar if ((class_name_len == sizeof("self")-1) && ZEND_U_EQUAL(type, class_name, class_name_len, "self", sizeof("self")-1)) { return ZEND_FETCH_CLASS_SELF; + } 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_LATE; } 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; Index: Zend/zend_compile.h =================================================================== RCS file: /repository/ZendEngine2/zend_compile.h,v retrieving revision 1.360 diff -u -p -r1.360 zend_compile.h --- Zend/zend_compile.h 12 Jul 2007 09:23:48 -0000 1.360 +++ Zend/zend_compile.h 24 Jul 2007 06:30:09 -0000 @@ -308,6 +308,8 @@ struct _zend_execute_data { HashTable *symbol_table; struct _zend_execute_data *prev_execute_data; zval *old_error_reporting; + int lsb_push_number; + int lsb_break; }; #define EX(element) execute_data.element @@ -631,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_LATE 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_execute_API.c =================================================================== RCS file: /repository/ZendEngine2/zend_execute_API.c,v retrieving revision 1.407 diff -u -p -r1.407 zend_execute_API.c --- Zend/zend_execute_API.c 12 Jul 2007 09:23:48 -0000 1.407 +++ Zend/zend_execute_API.c 24 Jul 2007 06:30:10 -0000 @@ -158,7 +158,11 @@ void init_executor(TSRMLS_D) /* {{{ */ EG(in_autoload) = NULL; EG(autoload_func) = NULL; + EG(last_fetched_class) = NULL; + zend_ptr_stack_init(&EG(class_call_stack)); + zend_ptr_stack_init(&EG(argument_stack)); + zend_ptr_stack_push(&EG(argument_stack), (void *) NULL); zend_u_hash_init(&EG(symbol_table), 50, NULL, ZVAL_PTR_DTOR, 0, UG(unicode)); @@ -306,6 +310,8 @@ void shutdown_executor(TSRMLS_D) /* {{{ } zend_hash_apply(EG(class_table), (apply_func_t) zend_cleanup_class_data TSRMLS_CC); + zend_ptr_stack_destroy(&EG(class_call_stack)); + zend_ptr_stack_destroy(&EG(argument_stack)); /* Destroy all op arrays */ @@ -1016,6 +1022,8 @@ int zend_call_function(zend_fcall_info * current_scope = EG(scope); EG(scope) = calling_scope; + EG(last_fetched_class) = calling_scope; + current_this = EG(This); if (fci->object_pp) { @@ -1067,7 +1075,11 @@ int zend_call_function(zend_fcall_info * EG(return_value_ptr_ptr) = fci->retval_ptr_ptr; EG(active_op_array) = (zend_op_array *) EX(function_state).function; original_opline_ptr = EG(opline_ptr); + + zend_ptr_stack_push(&EG(class_call_stack), calling_scope); zend_execute(EG(active_op_array) TSRMLS_CC); + zend_ptr_stack_pop(&EG(class_call_stack)); + if (!fci->symbol_table) { zend_hash_destroy(EG(active_symbol_table)); FREE_HASHTABLE(EG(active_symbol_table)); @@ -1654,10 +1666,38 @@ ZEND_API zend_class_entry *zend_u_fetch_ check_fetch_type: switch (fetch_type) { + case ZEND_FETCH_CLASS_LATE: + if (!EG(scope)) { + zend_error(E_ERROR, "Cannot access static:: when no class scope is active"); + } + zend_class_entry *last_scope = EG(scope); + + /* The late binding scope has to be found on the top-most + * block of the stack */ + int i = EG(class_call_stack).top; + while (--i >= 0) { + zend_class_entry *current_scope = EG(class_call_stack).elements[i]; + + if (current_scope == NULL) { + break; + } + + /* check the relationship between the scopes */ + if(instanceof_function(EG(scope), current_scope) != 1 && + instanceof_function(current_scope, EG(scope)) != 1) { + break; + } else { + last_scope = current_scope; + } + } + + EG(last_fetched_class) = last_scope; + return last_scope; case ZEND_FETCH_CLASS_SELF: if (!EG(scope)) { zend_error(E_ERROR, "Cannot access self:: when no class scope is active"); } + EG(last_fetched_class) = EG(scope); return EG(scope); case ZEND_FETCH_CLASS_PARENT: if (!EG(scope)) { @@ -1666,6 +1706,7 @@ check_fetch_type: if (!EG(scope)->parent) { zend_error(E_ERROR, "Cannot access parent:: when current class scope has no parent"); } + EG(last_fetched_class) = EG(scope)->parent; return EG(scope)->parent; case ZEND_FETCH_CLASS_AUTO: { if (do_normalize) { @@ -1697,6 +1738,7 @@ check_fetch_type: if (lcname.v != class_name.v) { efree(lcname.v); } + EG(last_fetched_class) = *pce; return *pce; } } @@ -1709,6 +1751,7 @@ check_fetch_type: if (lcname.v != class_name.v) { efree(lcname.v); } + EG(last_fetched_class) = *pce; return *pce; } } @@ -1724,11 +1767,13 @@ check_fetch_type: if (lcname.v != class_name.v) { efree(lcname.v); } + EG(last_fetched_class) = NULL; return NULL; } else { if (lcname.v != class_name.v) { efree(lcname.v); } + EG(last_fetched_class) = *pce; return *pce; } } Index: Zend/zend_globals.h =================================================================== RCS file: /repository/ZendEngine2/zend_globals.h,v retrieving revision 1.168 diff -u -p -r1.168 zend_globals.h --- Zend/zend_globals.h 12 Jul 2007 09:23:48 -0000 1.168 +++ Zend/zend_globals.h 24 Jul 2007 06:30:10 -0000 @@ -160,6 +160,9 @@ struct _zend_executor_globals { zend_function_state *function_state_ptr; zend_ptr_stack arg_types_stack; + zend_ptr_stack class_call_stack; + zend_class_entry *last_fetched_class; + /* symbol table cache */ HashTable *symtable_cache[SYMTABLE_CACHE_SIZE]; HashTable **symtable_cache_limit; Index: Zend/zend_language_parser.y =================================================================== RCS file: /repository/ZendEngine2/zend_language_parser.y,v retrieving revision 1.185 diff -u -p -r1.185 zend_language_parser.y --- Zend/zend_language_parser.y 12 Jul 2007 09:23:48 -0000 1.185 +++ Zend/zend_language_parser.y 24 Jul 2007 06:30:11 -0000 @@ -661,6 +661,7 @@ fully_qualified_class_name: T_STRING { $$ = $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); } + | T_STATIC { znode tmp; tmp.op_type = IS_CONST; ZVAL_STRING(&tmp.u.constant, "static", 1); INIT_PZVAL(&tmp.u.constant); $$ = tmp; } ; Index: Zend/zend_vm_def.h =================================================================== RCS file: /repository/ZendEngine2/zend_vm_def.h,v retrieving revision 1.172 diff -u -p -r1.172 zend_vm_def.h --- Zend/zend_vm_def.h 14 Jul 2007 08:51:17 -0000 1.172 +++ Zend/zend_vm_def.h 24 Jul 2007 06:30:13 -0000 @@ -1754,6 +1754,10 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CA zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + if (Z_OBJCE_P(EX(object))) { + EG(last_fetched_class) = Z_OBJCE_P(EX(object)); + } + /* First, locate the function. */ EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), function_name_strval, function_name_strlen TSRMLS_CC); if (!EX(fbc)) { @@ -2651,6 +2655,11 @@ ZEND_VM_HANDLER(68, ZEND_NEW, ANY, ANY) EX(object) = object_zval; EX(fbc) = constructor; + if (constructor->common.fn_flags && constructor->type == ZEND_USER_FUNCTION) { + /* Affect the lsb_push_number of the next execute data */ + EG(current_execute_data)->lsb_break = 1; + } + ZEND_VM_NEXT_OPCODE(); } } Index: Zend/zend_vm_execute.h =================================================================== RCS file: /repository/ZendEngine2/zend_vm_execute.h,v retrieving revision 1.175 diff -u -p -r1.175 zend_vm_execute.h --- Zend/zend_vm_execute.h 14 Jul 2007 08:51:17 -0000 1.175 +++ Zend/zend_vm_execute.h 24 Jul 2007 06:30:27 -0000 @@ -82,6 +82,29 @@ ZEND_API void execute(zend_op_array *op_ EX(function_state).function_symbol_table = NULL; #endif + execute_data.lsb_push_number = 0; + + if (EX(prev_execute_data) && EX(prev_execute_data)->lsb_break) { + /* Detect that last execute_data triggered a break */ + zend_ptr_stack_push(&EG(class_call_stack), NULL); + execute_data.lsb_push_number++; + EX(prev_execute_data)->lsb_break = 0; + } + + if (EX(function_state).function->common.scope == NULL) { + /* normal function, scope is always broken, new block on + * class_call_stack if necessary */ + if (EX(function_state).function->common.fn_flags && EX(function_state).function->type == ZEND_USER_FUNCTION) { + zend_ptr_stack_push(&EG(class_call_stack), NULL); + zend_ptr_stack_push(&EG(class_call_stack), EG(scope)); + execute_data.lsb_push_number += 2; + } + } else { + /* we are executing a method, static or not check scope */ + zend_ptr_stack_push(&EG(class_call_stack), EG(last_fetched_class)); + execute_data.lsb_push_number += 1; + } + while (1) { #ifdef ZEND_WIN32 if (EG(timed_out)) { @@ -90,6 +113,12 @@ ZEND_API void execute(zend_op_array *op_ #endif if (EX(opline)->handler(&execute_data TSRMLS_CC) > 0) { + if (execute_data.lsb_push_number) { + /* clean block separation(s) */ + while(--execute_data.lsb_push_number >= 0) { + zend_ptr_stack_pop(&EG(class_call_stack)); + } + } return; } @@ -437,6 +466,11 @@ static int ZEND_NEW_SPEC_HANDLER(ZEND_OP EX(object) = object_zval; EX(fbc) = constructor; + if (constructor->common.fn_flags && constructor->type == ZEND_USER_FUNCTION) { + /* Affect the lsb_push_number of the next execute data */ + EG(current_execute_data)->lsb_break = 1; + } + ZEND_VM_NEXT_OPCODE(); } } @@ -5753,6 +5787,10 @@ static int ZEND_INIT_METHOD_CALL_SPEC_TM zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + if (Z_OBJCE_P(EX(object))) { + EG(last_fetched_class) = Z_OBJCE_P(EX(object)); + } + /* First, locate the function. */ EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), function_name_strval, function_name_strlen TSRMLS_CC); if (!EX(fbc)) { @@ -6202,6 +6240,10 @@ static int ZEND_INIT_METHOD_CALL_SPEC_TM zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + if (Z_OBJCE_P(EX(object))) { + EG(last_fetched_class) = Z_OBJCE_P(EX(object)); + } + /* First, locate the function. */ EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), function_name_strval, function_name_strlen TSRMLS_CC); if (!EX(fbc)) { @@ -6653,6 +6695,10 @@ static int ZEND_INIT_METHOD_CALL_SPEC_TM zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + if (Z_OBJCE_P(EX(object))) { + EG(last_fetched_class) = Z_OBJCE_P(EX(object)); + } + /* First, locate the function. */ EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), function_name_strval, function_name_strlen TSRMLS_CC); if (!EX(fbc)) { @@ -7197,6 +7243,10 @@ static int ZEND_INIT_METHOD_CALL_SPEC_TM zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + if (Z_OBJCE_P(EX(object))) { + EG(last_fetched_class) = Z_OBJCE_P(EX(object)); + } + /* First, locate the function. */ EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), function_name_strval, function_name_strlen TSRMLS_CC); if (!EX(fbc)) { @@ -9951,6 +10001,10 @@ static int ZEND_INIT_METHOD_CALL_SPEC_VA zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + if (Z_OBJCE_P(EX(object))) { + EG(last_fetched_class) = Z_OBJCE_P(EX(object)); + } + /* First, locate the function. */ EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), function_name_strval, function_name_strlen TSRMLS_CC); if (!EX(fbc)) { @@ -11642,6 +11696,10 @@ static int ZEND_INIT_METHOD_CALL_SPEC_VA zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + if (Z_OBJCE_P(EX(object))) { + EG(last_fetched_class) = Z_OBJCE_P(EX(object)); + } + /* First, locate the function. */ EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), function_name_strval, function_name_strlen TSRMLS_CC); if (!EX(fbc)) { @@ -13326,6 +13384,10 @@ static int ZEND_INIT_METHOD_CALL_SPEC_VA zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + if (Z_OBJCE_P(EX(object))) { + EG(last_fetched_class) = Z_OBJCE_P(EX(object)); + } + /* First, locate the function. */ EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), function_name_strval, function_name_strlen TSRMLS_CC); if (!EX(fbc)) { @@ -15543,6 +15605,10 @@ static int ZEND_INIT_METHOD_CALL_SPEC_VA zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + if (Z_OBJCE_P(EX(object))) { + EG(last_fetched_class) = Z_OBJCE_P(EX(object)); + } + /* First, locate the function. */ EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), function_name_strval, function_name_strlen TSRMLS_CC); if (!EX(fbc)) { @@ -16856,6 +16922,10 @@ static int ZEND_INIT_METHOD_CALL_SPEC_UN zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + if (Z_OBJCE_P(EX(object))) { + EG(last_fetched_class) = Z_OBJCE_P(EX(object)); + } + /* First, locate the function. */ EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), function_name_strval, function_name_strlen TSRMLS_CC); if (!EX(fbc)) { @@ -17919,6 +17989,10 @@ static int ZEND_INIT_METHOD_CALL_SPEC_UN zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + if (Z_OBJCE_P(EX(object))) { + EG(last_fetched_class) = Z_OBJCE_P(EX(object)); + } + /* First, locate the function. */ EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), function_name_strval, function_name_strlen TSRMLS_CC); if (!EX(fbc)) { @@ -18936,6 +19010,10 @@ static int ZEND_INIT_METHOD_CALL_SPEC_UN zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + if (Z_OBJCE_P(EX(object))) { + EG(last_fetched_class) = Z_OBJCE_P(EX(object)); + } + /* First, locate the function. */ EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), function_name_strval, function_name_strlen TSRMLS_CC); if (!EX(fbc)) { @@ -20218,6 +20296,10 @@ static int ZEND_INIT_METHOD_CALL_SPEC_UN zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + if (Z_OBJCE_P(EX(object))) { + EG(last_fetched_class) = Z_OBJCE_P(EX(object)); + } + /* First, locate the function. */ EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), function_name_strval, function_name_strlen TSRMLS_CC); if (!EX(fbc)) { @@ -22990,6 +23072,10 @@ static int ZEND_INIT_METHOD_CALL_SPEC_CV zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + if (Z_OBJCE_P(EX(object))) { + EG(last_fetched_class) = Z_OBJCE_P(EX(object)); + } + /* First, locate the function. */ EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), function_name_strval, function_name_strlen TSRMLS_CC); if (!EX(fbc)) { @@ -24525,6 +24611,10 @@ static int ZEND_INIT_METHOD_CALL_SPEC_CV zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + if (Z_OBJCE_P(EX(object))) { + EG(last_fetched_class) = Z_OBJCE_P(EX(object)); + } + /* First, locate the function. */ EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), function_name_strval, function_name_strlen TSRMLS_CC); if (!EX(fbc)) { @@ -26099,6 +26189,10 @@ static int ZEND_INIT_METHOD_CALL_SPEC_CV zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + if (Z_OBJCE_P(EX(object))) { + EG(last_fetched_class) = Z_OBJCE_P(EX(object)); + } + /* First, locate the function. */ EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), function_name_strval, function_name_strlen TSRMLS_CC); if (!EX(fbc)) { @@ -28104,6 +28198,10 @@ static int ZEND_INIT_METHOD_CALL_SPEC_CV zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + if (Z_OBJCE_P(EX(object))) { + EG(last_fetched_class) = Z_OBJCE_P(EX(object)); + } + /* First, locate the function. */ EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), function_name_strval, function_name_strlen TSRMLS_CC); if (!EX(fbc)) { Index: Zend/zend_vm_execute.skl =================================================================== RCS file: /repository/ZendEngine2/zend_vm_execute.skl,v retrieving revision 1.6 diff -u -p -r1.6 zend_vm_execute.skl --- Zend/zend_vm_execute.skl 19 Sep 2006 10:38:30 -0000 1.6 +++ Zend/zend_vm_execute.skl 24 Jul 2007 06:30:28 -0000 @@ -53,6 +53,29 @@ ZEND_API void {%EXECUTOR_NAME%}(zend_op_ EX(function_state).function_symbol_table = NULL; #endif + execute_data.lsb_push_number = 0; + + if (EX(prev_execute_data) && EX(prev_execute_data)->lsb_break) { + /* Detect that last execute_data triggered a break */ + zend_ptr_stack_push(&EG(class_call_stack), NULL); + execute_data.lsb_push_number++; + EX(prev_execute_data)->lsb_break = 0; + } + + if (EX(function_state).function->common.scope == NULL) { + /* normal function, scope is always broken, new block on + * class_call_stack if necessary */ + if (EX(function_state).function->common.fn_flags && EX(function_state).function->type == ZEND_USER_FUNCTION) { + zend_ptr_stack_push(&EG(class_call_stack), NULL); + zend_ptr_stack_push(&EG(class_call_stack), EG(scope)); + execute_data.lsb_push_number += 2; + } + } else { + /* we are executing a method, static or not check scope */ + zend_ptr_stack_push(&EG(class_call_stack), EG(last_fetched_class)); + execute_data.lsb_push_number += 1; + } + while (1) { {%ZEND_VM_CONTINUE_LABEL%} #ifdef ZEND_WIN32 @@ -62,6 +85,12 @@ ZEND_API void {%EXECUTOR_NAME%}(zend_op_ #endif {%ZEND_VM_DISPATCH%} { + if (execute_data.lsb_push_number) { + /* clean block separation(s) */ + while(--execute_data.lsb_push_number >= 0) { + zend_ptr_stack_pop(&EG(class_call_stack)); + } + } {%INTERNAL_EXECUTOR%} } Index: tests/classes/late_static_binding_001.phpt =================================================================== RCS file: tests/classes/late_static_binding_001.phpt diff -N tests/classes/late_static_binding_001.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/classes/late_static_binding_001.phpt 24 Jul 2007 06:30:28 -0000 @@ -0,0 +1,61 @@ +--TEST-- +ZE2 Late Static Binding in a static function +--FILE-- + +==DONE== +--EXPECTF-- +TestClassStatic +TestClassConst +TestClassFunction +ChildClassStatic +ChildClassConst +ChildClassFunction +TestClassStatic +TestClassConst +TestClassFunction +==DONE== Index: tests/classes/late_static_binding_002.phpt =================================================================== RCS file: tests/classes/late_static_binding_002.phpt diff -N tests/classes/late_static_binding_002.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/classes/late_static_binding_002.phpt 24 Jul 2007 06:30:28 -0000 @@ -0,0 +1,65 @@ +--TEST-- +ZE2 Late Static Binding with instances +--FILE-- +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: tests/classes/late_static_binding_003.phpt =================================================================== RCS file: tests/classes/late_static_binding_003.phpt diff -N tests/classes/late_static_binding_003.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/classes/late_static_binding_003.phpt 24 Jul 2007 06:30:28 -0000 @@ -0,0 +1,68 @@ +--TEST-- +ZE2 Late Static Binding with dynamic method names +--FILE-- +$func1() . "\n"; +echo $testClass->$func2() . "\n"; +echo $testClass->$func3() . "\n"; + +echo $childClass1->$func1() . "\n"; +echo $childClass1->$func2() . "\n"; +echo $childClass1->$func3() . "\n"; + +echo $childClass2->$func1() . "\n"; +echo $childClass2->$func2() . "\n"; +echo $childClass2->$func3() . "\n"; +?> +==DONE== +--EXPECTF-- +TestClassStatic +TestClassConst +TestClassFunction +ChildClassStatic +ChildClassConst +ChildClassFunction +TestClassStatic +TestClassConst +TestClassFunction +==DONE== Index: tests/classes/late_static_binding_004.phpt =================================================================== RCS file: tests/classes/late_static_binding_004.phpt diff -N tests/classes/late_static_binding_004.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/classes/late_static_binding_004.phpt 24 Jul 2007 06:30:28 -0000 @@ -0,0 +1,95 @@ +--TEST-- +ZE2 Late Static Binding with dynamic function calls +--FILE-- + +==DONE== +--EXPECTF-- +TestClassStatic +TestClassConst +TestClassFunction +TestClassStatic +TestClassConst +TestClassFunction +ChildClassStatic +ChildClassConst +ChildClassFunction +ChildClassStatic +ChildClassConst +ChildClassFunction +TestClassStatic +TestClassConst +TestClassFunction +TestClassStatic +TestClassConst +TestClassFunction +==DONE== Index: tests/classes/late_static_binding_005.phpt =================================================================== RCS file: tests/classes/late_static_binding_005.phpt diff -N tests/classes/late_static_binding_005.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/classes/late_static_binding_005.phpt 24 Jul 2007 06:30:28 -0000 @@ -0,0 +1,92 @@ +--TEST-- +ZE2 Late Static Binding with exceptions +--FILE-- + +==DONE== +--EXPECTF-- +via A: +A +A +A +A +A +A +A +A +A +via B: +B +B +B +B +B +B +B +B +B +==DONE== Index: tests/classes/late_static_binding_006.phpt =================================================================== RCS file: tests/classes/late_static_binding_006.phpt diff -N tests/classes/late_static_binding_006.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/classes/late_static_binding_006.phpt 24 Jul 2007 06:30:28 -0000 @@ -0,0 +1,36 @@ +--TEST-- +ZE2 Late Static Binding within constructors +--FILE-- +test(); +?> +==DONE== +--EXPECTF-- +TestChild +TestParent +==DONE== Index: tests/classes/late_static_binding_007.phpt =================================================================== RCS file: tests/classes/late_static_binding_007.phpt diff -N tests/classes/late_static_binding_007.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/classes/late_static_binding_007.phpt 24 Jul 2007 06:30:28 -0000 @@ -0,0 +1,41 @@ +--TEST-- +ZE2 Late Static Binding within hooks/magic methods +--FILE-- +test(); +$o->a = "b"; +echo $o->a; +?> +==DONE== +--EXPECTF-- +TestChild +TestChild +TestChild +==DONE== Index: tests/classes/late_static_binding_008.phpt =================================================================== RCS file: tests/classes/late_static_binding_008.phpt diff -N tests/classes/late_static_binding_008.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/classes/late_static_binding_008.phpt 24 Jul 2007 06:30:28 -0000 @@ -0,0 +1,45 @@ +--TEST-- +ZE2 Late Static Binding with get_called_class() +--FILE-- +test(); +$o->a = "b"; +echo $o->a; + +TestParent::test(); +TestChild::test(); + +var_dump(get_called_class()); +?> +==DONE== +--EXPECTF-- +TestChild +TestChild +TestChild +TestParent +TestChild +bool(false) +==DONE==