From: Dmitry Stogov Date: Wed, 27 Apr 2005 15:45:36 +0000 (+0000) Subject: Fixed bug #29210 (Function: is_callable - no support for private and protected classes) X-Git-Tag: php-5.0.1b1~398 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c81db6bc5606425e4834a57afa9d06800b837b72;p=php Fixed bug #29210 (Function: is_callable - no support for private and protected classes) --- diff --git a/Zend/tests/bug29210.phpt b/Zend/tests/bug29210.phpt new file mode 100644 index 0000000000..294685499c --- /dev/null +++ b/Zend/tests/bug29210.phpt @@ -0,0 +1,104 @@ +--TEST-- +Bug #29210 Function: is_callable - no support for private and protected classes +--FILE-- +test_func1(); + } else { + echo "test_func1 isn't callable from inside\n"; + } + if (is_callable(array($this,'test_func2'))) { + $this->test_func2(); + } else { + echo "test_func2 isn't callable from inside\n"; + } + if (is_callable(array('test_class','test_func3'))) { + test_class::test_func3(); + } else { + echo "test_func3 isn't callable from inside\n"; + } + if (is_callable(array('test_class','test_func4'))) { + test_class::test_func4(); + } else { + echo "test_func4 isn't callable from inside\n"; + } + } +} + +class foo extends test_class { + function test() { + if (is_callable(array($this,'test_func1'))) { + $this->test_func1(); + } else { + echo "test_func1 isn't callable from child\n"; + } + if (is_callable(array($this,'test_func2'))) { + $this->test_func2(); + } else { + echo "test_func2 isn't callable from child\n"; + } + if (is_callable(array('test_class','test_func3'))) { + test_class::test_func3(); + } else { + echo "test_func3 isn't callable from child\n"; + } + if (is_callable(array('test_class','test_func4'))) { + test_class::test_func4(); + } else { + echo "test_func4 isn't callable from child\n"; + } + } +} + +$object = new test_class; +$object->test(); +if (is_callable(array($object,'test_func1'))) { + $object->test_func1(); +} else { + echo "test_func1 isn't callable from outside\n"; +} +if (is_callable(array($object,'test_func2'))) { + $object->test_func2(); +} else { + echo "test_func2 isn't callable from outside\n"; +} +if (is_callable(array('test_class','test_func3'))) { + test_class::test_func3(); +} else { + echo "test_func3 isn't callable from outside\n"; +} +if (is_callable(array('test_class','test_func4'))) { + test_class::test_func4(); +} else { + echo "test_func4 isn't callable from outside\n"; +} +$object = new foo(); +$object->test(); +?> +--EXPECT-- +test_func1 +test_func2 +test_func3 +test_func4 +test_func1 isn't callable from outside +test_func2 isn't callable from outside +test_func3 isn't callable from outside +test_func4 isn't callable from outside +test_func1 isn't callable from child +test_func2 +test_func3 isn't callable from child +test_func4 diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 6e66cfd288..16a9648af1 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1741,7 +1741,7 @@ ZEND_API int zend_disable_class(char *class_name, uint class_name_length TSRMLS_ return 1; } -ZEND_API zend_bool zend_is_callable(zval *callable, zend_bool syntax_only, char **callable_name) +ZEND_API zend_bool zend_is_callable(zval *callable, uint check_flags, char **callable_name) { char *lcname; zend_bool retval = 0; @@ -1752,7 +1752,7 @@ ZEND_API zend_bool zend_is_callable(zval *callable, zend_bool syntax_only, char if (callable_name) { *callable_name = estrndup(Z_STRVAL_P(callable), Z_STRLEN_P(callable)); } - if (syntax_only) { + if (check_flags & IS_CALLABLE_CHECK_SYNTAX_ONLY) { return 1; } @@ -1789,7 +1789,7 @@ ZEND_API zend_bool zend_is_callable(zval *callable, zend_bool syntax_only, char memcpy(ptr, Z_STRVAL_PP(method), Z_STRLEN_PP(method) + 1); } - if (syntax_only) + if (check_flags & IS_CALLABLE_CHECK_SYNTAX_ONLY) return 1; lcname = zend_str_tolower_dup(Z_STRVAL_PP(obj), Z_STRLEN_PP(obj)); @@ -1818,14 +1818,27 @@ ZEND_API zend_bool zend_is_callable(zval *callable, zend_bool syntax_only, char memcpy(ptr, Z_STRVAL_PP(method), Z_STRLEN_PP(method) + 1); } - if (syntax_only) + if (check_flags & IS_CALLABLE_CHECK_SYNTAX_ONLY) return 1; } if (ce) { + zend_function *fbc; + lcname = zend_str_tolower_dup(Z_STRVAL_PP(method), Z_STRLEN_PP(method)); - if (zend_hash_exists(&ce->function_table, lcname, Z_STRLEN_PP(method)+1)) { - retval = 1; + if (zend_hash_find(&ce->function_table, lcname, Z_STRLEN_PP(method)+1, (void **)&fbc) == SUCCESS) { + retval = 1; + if ((check_flags & IS_CALLABLE_CHECK_NO_ACCESS) == 0) { + if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) { + if (!zend_check_private(fbc, (Z_TYPE_PP(obj) == IS_STRING)?EG(scope):(*obj)->value.obj.handlers->get_class_entry(*obj TSRMLS_CC), lcname, Z_STRLEN_PP(method) TSRMLS_CC)) { + retval = 0; + } + } else if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) { + if (!zend_check_protected(fbc->common.scope, EG(scope))) { + retval = 0; + } + } + } } /* check for __call too */ if (retval == 0 && ce->__call != 0) { diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 131dc9826a..99c7aa4edc 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -184,7 +184,11 @@ ZEND_API int zend_disable_function(char *function_name, uint function_name_lengt ZEND_API int zend_disable_class(char *class_name, uint class_name_length TSRMLS_DC); ZEND_API void zend_wrong_param_count(TSRMLS_D); -ZEND_API zend_bool zend_is_callable(zval *callable, zend_bool syntax_only, char **callable_name); + +#define IS_CALLABLE_CHECK_SYNTAX_ONLY (1<<0) +#define IS_CALLABLE_CHECK_NO_ACCESS (1<<1) + +ZEND_API zend_bool zend_is_callable(zval *callable, uint check_flags, char **callable_name); ZEND_API zend_bool zend_make_callable(zval *callable, char **callable_name TSRMLS_DC); ZEND_API char *zend_get_module_version(char *module_name); ZEND_API int zend_get_module_started(char *module_name); diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 8f2b1e9502..e7ac0a3e78 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -531,7 +531,7 @@ ZEND_API void zend_std_call_user_call(INTERNAL_FUNCTION_PARAMETERS) * Returns the function address that should be called, or NULL * if no such function exists. */ -static inline zend_function *zend_check_private(zend_function *fbc, zend_class_entry *ce, int fn_flags, char *function_name_strval, int function_name_strlen TSRMLS_DC) +static inline zend_function *zend_check_private_int(zend_function *fbc, zend_class_entry *ce, char *function_name_strval, int function_name_strlen TSRMLS_DC) { if (!ce) { return 0; @@ -566,6 +566,12 @@ static inline zend_function *zend_check_private(zend_function *fbc, zend_class_e } +ZEND_API int zend_check_private(zend_function *fbc, zend_class_entry *ce, char *function_name_strval, int function_name_strlen TSRMLS_DC) +{ + return zend_check_private_int(fbc, ce, function_name_strval, function_name_strlen TSRMLS_CC) != NULL; +} + + /* Ensures that we're allowed to call a protected method. */ ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope) @@ -643,7 +649,7 @@ static union _zend_function *zend_std_get_method(zval **object_ptr, char *method /* Ensure that if we're calling a private function, we're allowed to do so. */ - updated_fbc = zend_check_private(fbc, object->value.obj.handlers->get_class_entry(object TSRMLS_CC), fbc->common.fn_flags, lc_method_name, method_len TSRMLS_CC); + updated_fbc = zend_check_private_int(fbc, object->value.obj.handlers->get_class_entry(object TSRMLS_CC), lc_method_name, method_len TSRMLS_CC); if (!updated_fbc) { 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), method_name, EG(scope) ? EG(scope)->name : ""); } @@ -681,7 +687,7 @@ ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, char *f /* Ensure that if we're calling a private function, we're allowed to do so. */ - updated_fbc = zend_check_private(fbc, EG(scope), fbc->common.fn_flags, function_name_strval, function_name_strlen TSRMLS_CC); + updated_fbc = zend_check_private_int(fbc, EG(scope), function_name_strval, function_name_strlen TSRMLS_CC); if (!updated_fbc) { 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 : ""); } diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h index 1cd6c0a8ad..5284ba2efd 100644 --- a/Zend/zend_object_handlers.h +++ b/Zend/zend_object_handlers.h @@ -139,6 +139,8 @@ ZEND_API int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int ty #define IS_ZEND_STD_OBJECT(z) ((z).type == IS_OBJECT && (Z_OBJ_HT((z))->get_class_entry != NULL)) #define HAS_CLASS_ENTRY(z) (Z_OBJ_HT(z)->get_class_entry != NULL) +ZEND_API int zend_check_private(union _zend_function *fbc, zend_class_entry *ce, char *function_name_strval, int function_name_strlen TSRMLS_DC); + ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope); ZEND_API int zend_check_property_access(zend_object *zobj, char *prop_info_name TSRMLS_DC); diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 2e4fb54372..fe9f781e84 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -2051,7 +2051,7 @@ PHP_FUNCTION(call_user_func) convert_to_string_ex(params[0]); } - if (!zend_is_callable(*params[0], 0, &name)) { + if (!zend_is_callable(*params[0], IS_CALLABLE_CHECK_NO_ACCESS, &name)) { php_error_docref1(NULL TSRMLS_CC, name, E_WARNING, "First argument is expected to be a valid callback"); efree(name); efree(params); @@ -2106,7 +2106,7 @@ PHP_FUNCTION(call_user_func_array) convert_to_string_ex(func); } - if (!zend_is_callable(*func, 0, &name)) { + if (!zend_is_callable(*func, IS_CALLABLE_CHECK_NO_ACCESS, &name)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "First argument is expected to be a valid callback, '%s' was given", name); efree(name); RETURN_NULL();