Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:31896 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 58274 invoked by uid 1010); 24 Aug 2007 13:26:31 -0000 Delivered-To: ezmlm-scan-internals@lists.php.net Delivered-To: ezmlm-internals@lists.php.net Received: (qmail 58259 invoked from network); 24 Aug 2007 13:26:30 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 24 Aug 2007 13:26:30 -0000 Authentication-Results: pb1.pair.com header.from=colder@php.net; sender-id=unknown Authentication-Results: pb1.pair.com smtp.mail=colder@php.net; spf=unknown; sender-id=unknown Received-SPF: unknown (pb1.pair.com: domain php.net does not designate 91.121.74.6 as permitted sender) X-PHP-List-Original-Sender: colder@php.net X-Host-Fingerprint: 91.121.74.6 mailer.migtechnology.ch Received: from [91.121.74.6] ([91.121.74.6:52572] helo=mailer.migtechnology.ch) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 5A/C0-44283-38CDEC64 for ; Fri, 24 Aug 2007 09:26:30 -0400 Received: (qmail 16301 invoked from network); 24 Aug 2007 13:26:44 -0000 Received: from 155-94.1-85.cust.bluewin.ch (HELO ?192.168.1.115?) (85.1.94.155) by mailer.migtechnology.ch with ESMTPS (DHE-RSA-AES256-SHA encrypted); 24 Aug 2007 13:26:44 -0000 Message-ID: <46CEDAB1.2060204@php.net> Date: Fri, 24 Aug 2007 15:18:41 +0200 User-Agent: Thunderbird 2.0.0.6 (X11/20070728) MIME-Version: 1.0 To: internals@lists.php.net Content-Type: multipart/mixed; boundary="------------080505050400090701000806" Subject: [patch] Late static bindings (LSB) From: colder@php.net (Etienne Kneuss) --------------080505050400090701000806 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Hi internals, here is a patch that implements Late static bindinds in a way that minimizes the performance hits that were feared. There is no significant slowdown or memory usage increase when running Zend/bench.php, which I assume is a good enough bench for that kind of matter, as it involves a stupid amount of (recursive) function calls. You can also find the patch here: http://patches.colder.ch/Zend/late_static_bindings_take6.patch?markup Here is a document that describes its usage: http://colder.ch/news/08-24-2007/28/late-static-bindings-expl.html Regards, -- Etienne Kneuss http://www.colder.ch Men never do evil so completely and cheerfully as when they do it from a religious conviction. -- Pascal --------------080505050400090701000806 Content-Type: text/plain; name="late_static_bindings_take6.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="late_static_bindings_take6.patch" ? tests/classes/late_static_binding_001.phpt ? tests/classes/late_static_binding_002.phpt ? tests/classes/late_static_binding_003.phpt ? tests/classes/late_static_binding_004.phpt ? tests/classes/late_static_binding_005.phpt ? tests/classes/late_static_binding_006.phpt ? tests/classes/late_static_binding_007.phpt Index: Zend/zend_builtin_functions.c =================================================================== RCS file: /repository/ZendEngine2/zend_builtin_functions.c,v retrieving revision 1.347 diff -u -p -r1.347 zend_builtin_functions.c --- Zend/zend_builtin_functions.c 8 Aug 2007 13:32:58 -0000 1.347 +++ Zend/zend_builtin_functions.c 20 Aug 2007 02:15:06 -0000 @@ -44,6 +44,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); @@ -104,6 +105,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) @@ -658,6 +660,30 @@ 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 *lsb_scope; + + if (ZEND_NUM_ARGS()) { + ZEND_WRONG_PARAM_COUNT(); + } + + if (!EG(scope)) { + RETURN_FALSE; + } + + lsb_scope = zend_fetch_class_lsb(EG(scope) TSRMLS_CC); + + if (lsb_scope) { + RETURN_TEXTL(lsb_scope->name, lsb_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.761 diff -u -p -r1.761 zend_compile.c --- Zend/zend_compile.c 17 Aug 2007 12:05:19 -0000 1.761 +++ Zend/zend_compile.c 20 Aug 2007 02:15:13 -0000 @@ -1687,6 +1687,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; @@ -3165,7 +3166,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)); @@ -3199,6 +3202,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; @@ -3311,6 +3317,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; @@ -4771,6 +4780,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.362 diff -u -p -r1.362 zend_compile.h --- Zend/zend_compile.h 27 Jul 2007 13:41:35 -0000 1.362 +++ Zend/zend_compile.h 20 Aug 2007 02:15:14 -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.h =================================================================== RCS file: /repository/ZendEngine2/zend_execute.h,v retrieving revision 1.108 diff -u -p -r1.108 zend_execute.h --- Zend/zend_execute.h 21 Jul 2007 00:34:41 -0000 1.108 +++ Zend/zend_execute.h 20 Aug 2007 02:15:14 -0000 @@ -194,6 +194,7 @@ ZEND_API void zend_unset_timeout(TSRMLS_ ZEND_API void zend_timeout(int dummy); ZEND_API zend_class_entry *zend_fetch_class(char *class_name, uint class_name_len, int fetch_type TSRMLS_DC); ZEND_API zend_class_entry *zend_u_fetch_class(zend_uchar type, zstr class_name, uint class_name_len, int fetch_type TSRMLS_DC); +ZEND_API zend_class_entry *zend_fetch_class_lsb(zend_class_entry *scope TSRMLS_DC); void zend_verify_abstract_class(zend_class_entry *ce TSRMLS_DC); #ifdef ZEND_WIN32 Index: Zend/zend_execute_API.c =================================================================== RCS file: /repository/ZendEngine2/zend_execute_API.c,v retrieving revision 1.410 diff -u -p -r1.410 zend_execute_API.c --- Zend/zend_execute_API.c 27 Jul 2007 13:41:35 -0000 1.410 +++ Zend/zend_execute_API.c 20 Aug 2007 02:15:15 -0000 @@ -159,7 +159,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)); @@ -307,6 +311,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 */ @@ -1021,6 +1027,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) { @@ -1072,7 +1080,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)); @@ -1647,6 +1659,19 @@ void zend_unset_timeout(TSRMLS_D) /* {{{ } /* }}} */ +ZEND_API zend_class_entry *zend_fetch_class_lsb(zend_class_entry *scope TSRMLS_DC) /* {{{ */ +{ + zend_class_entry *last_scope = EG(class_call_stack).elements[EG(class_call_stack).top-1]; + + if (last_scope == NULL || instanceof_function(last_scope, scope TSRMLS_CC) != 1) { + return scope; + } else { + return last_scope; + } + +} +/* }}} */ + ZEND_API zend_class_entry *zend_u_fetch_class(zend_uchar type, zstr class_name, uint class_name_len, int fetch_type TSRMLS_DC) /* {{{ */ { zend_class_entry **pce; @@ -1660,10 +1685,18 @@ 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 *lsb_scope = zend_fetch_class_lsb(EG(scope) TSRMLS_CC); + EG(last_fetched_class) = lsb_scope; + return lsb_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)) { @@ -1672,6 +1705,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) { @@ -1703,6 +1737,7 @@ check_fetch_type: if (lcname.v != class_name.v) { efree(lcname.v); } + EG(last_fetched_class) = *pce; return *pce; } } @@ -1715,6 +1750,7 @@ check_fetch_type: if (lcname.v != class_name.v) { efree(lcname.v); } + EG(last_fetched_class) = *pce; return *pce; } } @@ -1730,11 +1766,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 20 Aug 2007 02:15:16 -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.187 diff -u -p -r1.187 zend_language_parser.y --- Zend/zend_language_parser.y 2 Aug 2007 21:53:53 -0000 1.187 +++ Zend/zend_language_parser.y 20 Aug 2007 02:15:17 -0000 @@ -667,6 +667,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_STRINGL(&tmp.u.constant, "static", sizeof("static")-1, 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.177 diff -u -p -r1.177 zend_vm_def.h --- Zend/zend_vm_def.h 17 Aug 2007 12:05:19 -0000 1.177 +++ Zend/zend_vm_def.h 20 Aug 2007 02:15:22 -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)) { @@ -2662,6 +2666,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.181 diff -u -p -r1.181 zend_vm_execute.h --- Zend/zend_vm_execute.h 17 Aug 2007 12:05:19 -0000 1.181 +++ Zend/zend_vm_execute.h 20 Aug 2007 02:15:51 -0000 @@ -82,6 +82,27 @@ ZEND_API void execute(zend_op_array *op_ EX(function_state).function_symbol_table = NULL; #endif + execute_data.lsb_push_number = 0; + execute_data.lsb_break = 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 (EG(last_fetched_class) == EX(function_state).function->common.scope) { + /* No fallback */ + 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 if (EX(function_state).function->common.scope != NULL) { + /* 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 +111,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 +464,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(); } } @@ -5804,6 +5836,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)) { @@ -6253,6 +6289,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)) { @@ -6704,6 +6744,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)) { @@ -7248,6 +7292,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)) { @@ -10000,6 +10048,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)) { @@ -11702,6 +11754,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)) { @@ -13397,6 +13453,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)) { @@ -15636,6 +15696,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)) { @@ -16960,6 +17024,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)) { @@ -18023,6 +18091,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)) { @@ -19040,6 +19112,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)) { @@ -20322,6 +20398,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)) { @@ -23092,6 +23172,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)) { @@ -24627,6 +24711,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)) { @@ -26201,6 +26289,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)) { @@ -28206,6 +28298,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.7 diff -u -p -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 20 Aug 2007 02:15:51 -0000 @@ -53,6 +53,27 @@ ZEND_API void {%EXECUTOR_NAME%}(zend_op_ EX(function_state).function_symbol_table = NULL; #endif + execute_data.lsb_push_number = 0; + execute_data.lsb_break = 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 (EG(last_fetched_class) == EX(function_state).function->common.scope) { + /* No fallback */ + 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 if (EX(function_state).function->common.scope != NULL) { + /* 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 +83,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_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 20 Aug 2007 02:15:52 -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== Index: tests/classes/late_static_binding_009.phpt =================================================================== RCS file: tests/classes/late_static_binding_009.phpt diff -N tests/classes/late_static_binding_009.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/classes/late_static_binding_009.phpt 20 Aug 2007 02:15:52 -0000 @@ -0,0 +1,49 @@ +--TEST-- +ZE2 Late Static Binding with scope paths +--FILE-- + C -> A +B::test2(); +// code path 1: B -> A -> C +B::test1(); +?> +==DONE== +--EXPECTF-- +A +C +==DONE== --------------080505050400090701000806--