Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:30898 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 14994 invoked by uid 1010); 14 Jul 2007 00:54:02 -0000 Delivered-To: ezmlm-scan-internals@lists.php.net Delivered-To: ezmlm-internals@lists.php.net Received: (qmail 14972 invoked from network); 14 Jul 2007 00:54:01 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 14 Jul 2007 00:54:01 -0000 Authentication-Results: pb1.pair.com header.from=pollita@php.net; sender-id=pass; domainkeys=good Authentication-Results: pb1.pair.com smtp.mail=pollita@php.net; spf=pass; sender-id=pass Received-SPF: pass (pb1.pair.com: domain php.net designates 140.211.166.39 as permitted sender) DomainKey-Status: good X-DomainKeys: Ecelerity dk_validate implementing draft-delany-domainkeys-base-01 X-PHP-List-Original-Sender: pollita@php.net X-Host-Fingerprint: 140.211.166.39 osu1.php.net Linux 2.4/2.6 Received: from [140.211.166.39] ([140.211.166.39:37474] helo=osu1.php.net) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id B0/80-09657-5AE18964 for ; Fri, 13 Jul 2007 20:54:00 -0400 DomainKey-Signature: q=dns; a=rsa-sha1; c=nofws; s=mx; d=php.net; h=From:Subject:To:Date; b=zI5IX5Q0tjsEUdb3pSx+mlrJqMLVz9VTMH/So+GiPsERCfF3eVkTus+XVojOvRvN aw5SwMue7aU25AU459hJ4SAsZsrIMZbmpMtXy1eaHr3t+712Hm8CDpDnWiRM0KPi Authentication-Results: osu1.php.net smtp.mail=pollita@php.net; spf=neutral; sender-id=neutral Authentication-Results: osu1.php.net header.from=pollita@php.net; sender-id=neutral Authentication-Results: osu1.php.net smtp.user=pollita; auth=pass (LOGIN) Received-SPF: neutral (osu1.php.net: 207.126.230.225 is neither permitted nor denied by domain of php.net) Received: from [207.126.230.225] ([207.126.230.225:61725] helo=[10.72.106.237]) by osu1.php.net (envelope-from ) (ecelerity 2.2.0.9 r(17728)) with ESMTPSA (cipher=none) id B5/F0-29568-A9028964 for ; Fri, 13 Jul 2007 18:02:19 -0700 Message-ID: <46981E98.30803@php.net> Date: Fri, 13 Jul 2007 17:53:44 -0700 User-Agent: Thunderbird 1.5.0.12 (Windows/20070509) MIME-Version: 1.0 To: internals@lists.php.net Content-Type: multipart/mixed; boundary="------------020908000703050307010005" Subject: __call_static() Magic Method From: pollita@php.net (Sara Golemon) --------------020908000703050307010005 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Attached is a patch which exports an internals hook in zend_class_entry for fetching function pointers similar to the object hook get_method() available to instance methods. This patch also exports a userspace hook __call_static() which operates in the fashion of the current __call() magic method, but for static calls. Wez called for some functionality like this a few weeks ago for a COM wrapper (or something similar), and I noticed there was actually a comment in the engine about how this should eventually be done anyway... Usage example: class foo { public static function __call_static($fname, $args) { echo "foo::{$fname}() called staticly with ", count($args), " parameters\n"; } } foo::randomMethod(1,2,3); I considered setting get_static_method to zend_std_get_static_method() by default (avoiding the if set check during runtime), but all the other hoooks follow this pattern so I went with consistency. If noone comments to the negative, I'll commit next friday (7/20)... -Sara --------------020908000703050307010005 Content-Type: text/plain; name="__call_static.diff.txt" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="__call_static.diff.txt" Index: Zend/zend.h =================================================================== RCS file: /repository/ZendEngine2/zend.h,v retrieving revision 1.334 diff -u -p -r1.334 zend.h --- Zend/zend.h 26 Apr 2007 19:08:24 -0000 1.334 +++ Zend/zend.h 14 Jul 2007 00:42:05 -0000 @@ -378,6 +378,7 @@ struct _zend_class_entry { union _zend_function *__unset; union _zend_function *__isset; union _zend_function *__call; + union _zend_function *__call_static; union _zend_function *__tostring; union _zend_function *serialize_func; union _zend_function *unserialize_func; @@ -388,6 +389,7 @@ struct _zend_class_entry { zend_object_value (*create_object)(zend_class_entry *class_type TSRMLS_DC); zend_object_iterator *(*get_iterator)(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC); int (*interface_gets_implemented)(zend_class_entry *iface, zend_class_entry *class_type TSRMLS_DC); /* a class implements this interface */ + union _zend_function *(*get_static_method)(zend_class_entry *ce, zstr method, int method_len TSRMLS_DC); /* serializer callbacks */ int (*serialize)(zval *object, int *type, zstr *buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC); Index: Zend/zend_API.c =================================================================== RCS file: /repository/ZendEngine2/zend_API.c,v retrieving revision 1.439 diff -u -p -r1.439 zend_API.c --- Zend/zend_API.c 13 Jul 2007 23:51:51 -0000 1.439 +++ Zend/zend_API.c 14 Jul 2007 00:42:05 -0000 @@ -2026,6 +2026,9 @@ ZEND_API void zend_check_magic_method_im } else if (lcname_len == sizeof(ZEND_CALL_FUNC_NAME) - 1 && ZEND_U_EQUAL(utype, lcname, lcname_len, ZEND_CALL_FUNC_NAME, sizeof(ZEND_CALL_FUNC_NAME)-1) && fptr->common.num_args != 2) { zend_error(error_type, "Method %v::%s() must take exactly 2 arguments", ce->name, ZEND_CALL_FUNC_NAME); + } else if (lcname_len == sizeof(ZEND_CALL_STATIC_FUNC_NAME) - 1 && + ZEND_U_EQUAL(utype, lcname, lcname_len, ZEND_CALL_STATIC_FUNC_NAME, sizeof(ZEND_CALL_STATIC_FUNC_NAME)-1) && fptr->common.num_args != 2) { + zend_error(error_type, "Method %v::%s() must take exactly 2 arguments", ce->name, ZEND_CALL_STATIC_FUNC_NAME); } else if (lcname_len == sizeof(ZEND_TOSTRING_FUNC_NAME) - 1 && ZEND_U_EQUAL(utype, lcname, lcname_len, ZEND_TOSTRING_FUNC_NAME, sizeof(ZEND_TOSTRING_FUNC_NAME)-1) && fptr->common.num_args != 0) { zend_error(error_type, "Method %v::%s() cannot take arguments", ce->name, ZEND_TOSTRING_FUNC_NAME); @@ -2043,7 +2046,7 @@ ZEND_API int zend_register_functions(zen int count=0, unload=0; HashTable *target_function_table = function_table; int error_type; - zend_function *ctor = NULL, *dtor = NULL, *clone = NULL, *__get = NULL, *__set = NULL, *__unset = NULL, *__isset = NULL, *__call = NULL, *__tostring = NULL; + zend_function *ctor = NULL, *dtor = NULL, *clone = NULL, *__get = NULL, *__set = NULL, *__unset = NULL, *__isset = NULL, *__call = NULL, *__call_static = NULL, *__tostring = NULL; char *lowercase_name; int fname_len; zstr lc_class_name = NULL_ZSTR; @@ -2186,6 +2189,8 @@ ZEND_API int zend_register_functions(zen clone = reg_function; } else if ((fname_len == sizeof(ZEND_CALL_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_CALL_FUNC_NAME, sizeof(ZEND_CALL_FUNC_NAME))) { __call = reg_function; + } else if ((fname_len == sizeof(ZEND_CALL_STATIC_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_CALL_STATIC_FUNC_NAME, sizeof(ZEND_CALL_STATIC_FUNC_NAME))) { + __call_static = reg_function; } else if ((fname_len == sizeof(ZEND_TOSTRING_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_TOSTRING_FUNC_NAME, sizeof(ZEND_TOSTRING_FUNC_NAME))) { __tostring = reg_function; } else if ((fname_len == sizeof(ZEND_GET_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_GET_FUNC_NAME, sizeof(ZEND_GET_FUNC_NAME))) { @@ -2226,6 +2231,7 @@ ZEND_API int zend_register_functions(zen scope->destructor = dtor; scope->clone = clone; scope->__call = __call; + scope->__call_static = __call_static; scope->__tostring = __tostring; scope->__get = __get; scope->__set = __set; @@ -2258,6 +2264,12 @@ ZEND_API int zend_register_functions(zen } __call->common.fn_flags &= ~ZEND_ACC_ALLOW_STATIC; } + if (__call_static) { + if (!(__call_static->common.fn_flags & ZEND_ACC_STATIC)) { + zend_error(error_type, "Method %v::%v() must be static", scope->name, __call->common.function_name); + } + __call->common.fn_flags |= ZEND_ACC_STATIC; + } if (__tostring) { if (__tostring->common.fn_flags & ZEND_ACC_STATIC) { zend_error(error_type, "Method %v::%v() cannot be static", scope->name, __tostring->common.function_name); @@ -2656,6 +2668,10 @@ static int zend_is_callable_check_func(i retval = (*ce_ptr)->__call != NULL; *fptr_ptr = (*ce_ptr)->__call; } + if (!*zobj_ptr_ptr && *ce_ptr && (*ce_ptr)->__call_static) { + retval = 1; + *fptr_ptr = (*ce_ptr)->__call_static; + } } else { *fptr_ptr = fptr; if (*ce_ptr) { Index: Zend/zend_API.h =================================================================== RCS file: /repository/ZendEngine2/zend_API.h,v retrieving revision 1.289 diff -u -p -r1.289 zend_API.h --- Zend/zend_API.h 11 Jul 2007 15:17:41 -0000 1.289 +++ Zend/zend_API.h 14 Jul 2007 00:42:05 -0000 @@ -148,6 +148,7 @@ typedef struct _zend_function_entry { class_container.unserialize = NULL; \ class_container.create_object = NULL; \ class_container.interface_gets_implemented = NULL; \ + class_container.get_static_method = NULL; \ class_container.__call = handle_fcall; \ class_container.__tostring = NULL; \ class_container.__get = handle_propget; \ 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 14 Jul 2007 00:42:05 -0000 @@ -1222,6 +1222,8 @@ void zend_do_begin_function_declaration( CG(active_class_entry)->clone = (zend_function *) CG(active_op_array); } else if ((lcname_len == sizeof(ZEND_CALL_FUNC_NAME)-1) && (ZEND_U_EQUAL(Z_TYPE(function_name->u.constant), lcname, lcname_len, ZEND_CALL_FUNC_NAME, sizeof(ZEND_CALL_FUNC_NAME)-1))) { CG(active_class_entry)->__call = (zend_function *) CG(active_op_array); + } else if ((lcname_len == sizeof(ZEND_CALL_STATIC_FUNC_NAME)-1) && (ZEND_U_EQUAL(Z_TYPE(function_name->u.constant), lcname, lcname_len, ZEND_CALL_STATIC_FUNC_NAME, sizeof(ZEND_CALL_STATIC_FUNC_NAME)-1))) { + CG(active_class_entry)->__call_static = (zend_function *) CG(active_op_array); } else if ((lcname_len == sizeof(ZEND_GET_FUNC_NAME)-1) && (ZEND_U_EQUAL(Z_TYPE(function_name->u.constant), lcname, lcname_len, ZEND_GET_FUNC_NAME, sizeof(ZEND_GET_FUNC_NAME)-1))) { CG(active_class_entry)->__get = (zend_function *) CG(active_op_array); } else if ((lcname_len == sizeof(ZEND_SET_FUNC_NAME)-1) && (ZEND_U_EQUAL(Z_TYPE(function_name->u.constant), lcname, lcname_len, ZEND_SET_FUNC_NAME, sizeof(ZEND_SET_FUNC_NAME)-1))) { @@ -2185,6 +2187,9 @@ static void do_inherit_parent_constructo if (!ce->__call) { ce->__call = ce->parent->__call; } + if (!ce->__call_static) { + ce->__call_static = ce->parent->__call_static; + } if (!ce->__tostring) { ce->__tostring = ce->parent->__tostring; } @@ -4670,11 +4675,13 @@ ZEND_API void zend_initialize_class_data ce->__unset = NULL; ce->__isset = NULL; ce->__call = NULL; + ce->__call_static = NULL; ce->__tostring = NULL; ce->create_object = NULL; ce->get_iterator = NULL; ce->iterator_funcs.funcs = NULL; ce->interface_gets_implemented = NULL; + ce->get_static_method = NULL; ce->parent = NULL; ce->num_interfaces = 0; ce->interfaces = NULL; 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 14 Jul 2007 00:42:05 -0000 @@ -742,6 +742,7 @@ END_EXTERN_C() #define ZEND_UNSET_FUNC_NAME "__unset" #define ZEND_ISSET_FUNC_NAME "__isset" #define ZEND_CALL_FUNC_NAME "__call" +#define ZEND_CALL_STATIC_FUNC_NAME "__call_static" #define ZEND_TOSTRING_FUNC_NAME "__tostring" #define ZEND_AUTOLOAD_FUNC_NAME "__autoload" 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 14 Jul 2007 00:42:05 -0000 @@ -881,8 +881,11 @@ int zend_call_function(zend_fcall_info * unsigned int lcname_len; zstr lcname = zend_u_str_case_fold(Z_TYPE_P(fci->function_name), fname, fname_len, 1, &lcname_len); - EX(function_state).function = - zend_std_get_static_method(calling_scope, lcname, lcname_len TSRMLS_CC); + if (calling_scope->get_static_method) { + EX(function_state).function = calling_scope->get_static_method(calling_scope, lcname, lcname_len TSRMLS_CC); + } else { + EX(function_state).function = zend_std_get_static_method(calling_scope, lcname, lcname_len TSRMLS_CC); + } efree(lcname.v); if (check_scope_or_static && EX(function_state).function && !(EX(function_state).function->common.fn_flags & ZEND_ACC_STATIC) Index: Zend/zend_object_handlers.c =================================================================== RCS file: /repository/ZendEngine2/zend_object_handlers.c,v retrieving revision 1.186 diff -u -p -r1.186 zend_object_handlers.c --- Zend/zend_object_handlers.c 12 Jul 2007 10:32:26 -0000 1.186 +++ Zend/zend_object_handlers.c 14 Jul 2007 00:42:05 -0000 @@ -858,7 +858,51 @@ static union _zend_function *zend_std_ge } /* }}} */ -/* This is not (yet?) in the API, but it belongs in the built-in objects callbacks */ +ZEND_API void zend_std_call_static_user_call(INTERNAL_FUNCTION_PARAMETERS) /* {{{ */ +{ + zend_internal_function *func = (zend_internal_function *)EG(function_state_ptr)->function; + zval *method_name_ptr, *method_args_ptr; + zval *method_result_ptr = NULL; + zend_class_entry *ce = EG(scope); + + ALLOC_ZVAL(method_args_ptr); + INIT_PZVAL(method_args_ptr); + array_init(method_args_ptr); + + if (zend_copy_parameters_array(ZEND_NUM_ARGS(), method_args_ptr TSRMLS_CC) == FAILURE) { + zval_dtor(method_args_ptr); + zend_error(E_ERROR, "Cannot get arguments for " ZEND_CALL_STATIC_FUNC_NAME); + RETURN_FALSE; + } + + ALLOC_ZVAL(method_name_ptr); + INIT_PZVAL(method_name_ptr); + ZVAL_TEXT(method_name_ptr, func->function_name, 0); /* no dup - it's a copy */ + + /* __call_static handler is called with two arguments: + method name + array of method parameters + */ + zend_call_method_with_2_params(NULL, ce, &ce->__call_static, ZEND_CALL_STATIC_FUNC_NAME, &method_result_ptr, method_name_ptr, method_args_ptr); + + if (method_result_ptr) { + if (method_result_ptr->is_ref || method_result_ptr->refcount > 1) { + RETVAL_ZVAL(method_result_ptr, 1, 1); + } else { + RETVAL_ZVAL(method_result_ptr, 0, 1); + } + } + + /* now destruct all auxiliaries */ + zval_ptr_dtor(&method_args_ptr); + zval_ptr_dtor(&method_name_ptr); + + /* destruct the function also, then - we have allocated it in get_method */ + efree(func); +} +/* }}} */ + + ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, zstr function_name_strval, int function_name_strlen TSRMLS_DC) /* {{{ */ { zend_function *fbc; @@ -866,12 +910,32 @@ ZEND_API zend_function *zend_std_get_sta zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; if (zend_u_hash_find(&ce->function_table, type, function_name_strval, function_name_strlen+1, (void **) &fbc)==FAILURE) { - zstr class_name = ce->name; + if (ce->__call_static) { + zend_internal_function *call_static_user_call = emalloc(sizeof(zend_internal_function)); + call_static_user_call->type = ZEND_INTERNAL_FUNCTION; + call_static_user_call->module = ce->module; + call_static_user_call->handler = zend_std_call_static_user_call; + call_static_user_call->arg_info = NULL; + call_static_user_call->num_args = 0; + call_static_user_call->scope = ce; + call_static_user_call->fn_flags = ZEND_ACC_STATIC | ZEND_ACC_PUBLIC; + if (UG(unicode)) { + call_static_user_call->function_name.u = eustrndup(function_name_strval.u, function_name_strlen); + } else { + call_static_user_call->function_name.s = estrndup(function_name_strval.s, function_name_strlen); + } + call_static_user_call->pass_rest_by_reference = 0; + call_static_user_call->return_reference = ZEND_RETURN_VALUE; - if (!class_name.v) { - class_name.u = EMPTY_STR; + return (zend_function *)call_static_user_call; + } else { + zstr class_name = ce->name; + + if (!class_name.v) { + class_name.u = EMPTY_STR; + } + zend_error(E_ERROR, "Call to undefined method %R::%R()", type, class_name, type, function_name_strval); } - zend_error(E_ERROR, "Call to undefined method %R::%R()", type, class_name, type, function_name_strval); } #if MBO_0 /* right now this function is used for non static method lookup too */ Index: Zend/zend_vm_def.h =================================================================== RCS file: /repository/ZendEngine2/zend_vm_def.h,v retrieving revision 1.171 diff -u -p -r1.171 zend_vm_def.h --- Zend/zend_vm_def.h 12 Jul 2007 09:23:48 -0000 1.171 +++ Zend/zend_vm_def.h 14 Jul 2007 00:42:05 -0000 @@ -1848,7 +1848,11 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_ME function_name_strval = zend_u_str_case_fold(Z_TYPE_P(function_name), Z_UNIVAL_P(function_name), Z_UNILEN_P(function_name), 1, &function_name_strlen); } - EX(fbc) = zend_std_get_static_method(ce, function_name_strval, function_name_strlen TSRMLS_CC); + if (ce->get_static_method) { + EX(fbc) = ce->get_static_method(ce, function_name_strval, function_name_strlen TSRMLS_CC); + } else { + EX(fbc) = zend_std_get_static_method(ce, function_name_strval, function_name_strlen TSRMLS_CC); + } if (!is_const) { efree(function_name_strval.v); --------------020908000703050307010005--