Index: Zend/zend_object_handlers.c =================================================================== RCS file: /repository/ZendEngine2/zend_object_handlers.c,v retrieving revision 1.135.2.6.2.31 diff -u -w -p -r1.135.2.6.2.31 zend_object_handlers.c --- Zend/zend_object_handlers.c 8 Jan 2009 00:39:15 -0000 1.135.2.6.2.31 +++ Zend/zend_object_handlers.c 11 Jan 2009 12:33:05 -0000 @@ -849,33 +849,29 @@ static union _zend_function *zend_std_ge } +/* Checks whether __call() should be used in the case where a method invocation has + * been made using the Scope Resolution Operator (::), but the method was not found + * or is not visible. */ +static inline int zend_use_user_call_with_SRO(zend_class_entry *ce TSRMLS_DC) /* {{{ */ +{ + // Return true if the target class has a __call() method and + // the current object context is compatible with the target class. + return ce->__call && + EG(This) && + Z_OBJ_HT_P(EG(This))->get_class_entry && + instanceof_function(Z_OBJCE_P(EG(This)), ce TSRMLS_CC); +} + /* This is not (yet?) in the API, but it belongs in the built-in objects callbacks */ ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, char *function_name_strval, int function_name_strlen TSRMLS_DC) { zend_function *fbc; if (zend_hash_find(&ce->function_table, function_name_strval, function_name_strlen+1, (void **) &fbc)==FAILURE) { - if (ce->__call && - EG(This) && - Z_OBJ_HT_P(EG(This))->get_class_entry && - instanceof_function(Z_OBJCE_P(EG(This)), ce TSRMLS_CC)) { - zend_internal_function *call_user_call = emalloc(sizeof(zend_internal_function)); - - call_user_call->type = ZEND_INTERNAL_FUNCTION; - call_user_call->module = ce->module; - call_user_call->handler = zend_std_call_user_call; - call_user_call->arg_info = NULL; - call_user_call->num_args = 0; - call_user_call->scope = ce; - call_user_call->fn_flags = 0; - call_user_call->function_name = estrndup(function_name_strval, function_name_strlen); - call_user_call->pass_rest_by_reference = 0; - call_user_call->return_reference = ZEND_RETURN_VALUE; - - return (union _zend_function *)call_user_call; + if (zend_use_user_call_with_SRO(ce TSRMLS_CC)) { + return zend_get_user_call_function(Z_OBJ_P(EG(This)), function_name_strval, function_name_strlen); } else { char *class_name = ce->name; - if (!class_name) { class_name = ""; } @@ -891,13 +887,19 @@ ZEND_API zend_function *zend_std_get_sta */ updated_fbc = zend_check_private_int(fbc, EG(scope), function_name_strval, function_name_strlen TSRMLS_CC); if (!updated_fbc) { + if (zend_use_user_call_with_SRO(ce TSRMLS_CC)) { + return zend_get_user_call_function(Z_OBJ_P(EG(This)), function_name_strval, function_name_strlen); + } else { zend_error(E_ERROR, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), function_name_strval, EG(scope) ? EG(scope)->name : ""); } + } fbc = updated_fbc; } else if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) { /* Ensure that if we're calling a protected function, we're allowed to do so. */ if (!zend_check_protected(zend_get_function_root_class(fbc), EG(scope))) { + // No need to check zend_use_user_call_with_SRO(): if the protected method isn't visible, + // the scopes are not compatible, so we will not fall back to __call(). zend_error(E_ERROR, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), function_name_strval, EG(scope) ? EG(scope)->name : ""); } } Index: Zend/tests/access_modifiers_013.phpt =================================================================== RCS file: Zend/tests/access_modifiers_013.phpt diff -N Zend/tests/access_modifiers_013.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/access_modifiers_013.phpt 11 Jan 2009 11:27:55 -0000 @@ -0,0 +1,22 @@ +--TEST-- +Trigger __call() when attempting to access a private in parent with scope resolution operator. +--FILE-- +test(); +?> +--EXPECTF-- +In C::__call(none) +In C::__call(priv) \ No newline at end of file