Make zend_call_function() failure handling consistent

This API had rather peculiar behavior in case the provided function
is not callable. For some types of failures, it would silently
return FAILURE (e.g. a function does not exist), while for others
(e.g. a class does not exist) it would generate a warning. Depending
on what the calling code does, this can either result in silent
failure or duplicate errors.

This commit switches the contract such that zend_call_function()
always (*) succeeds, though that success might be in the form of
throwing an exception. Calling a non-callable will now consistently
throw an exception.

There are some rare callers that do want to ignore missing methods,
for legacy APIs that are specific with optional methods. For these
use cases a new zend_call_method_if_exists() API is provided.

Calling code generally does not need to explicitly check for and
report zend_call_function() failures -- it can rely on
zend_call_function() having already done so. However, existing
code that does check for failure should continue to work fine.

(*) The only exception to this is if EG(active) being false during
late engine shutdown. This is not relevant to most code, but code
running in destructors and similar may need to be aware of the
possibility.
This commit is contained in:
Nikita Popov 2021-09-01 12:16:03 +02:00
parent 0b743d5d95
commit 485d3acfe6
7 changed files with 105 additions and 137 deletions

View File

@ -646,6 +646,9 @@ ZEND_API void zend_fcall_info_argn(zend_fcall_info *fci, uint32_t argc, ...);
*/
ZEND_API zend_result zend_fcall_info_call(zend_fcall_info *fci, zend_fcall_info_cache *fcc, zval *retval, zval *args);
/* Can only return FAILURE if EG(active) is false during late engine shutdown.
* If the call or call setup throws, EG(exception) will be set and the retval
* will be UNDEF. Otherwise, the retval will be a non-UNDEF value. */
ZEND_API zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache);
/* Call the provided zend_function with the given params.
@ -679,6 +682,13 @@ static zend_always_inline void zend_call_known_instance_method_with_1_params(
ZEND_API void zend_call_known_instance_method_with_2_params(
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param1, zval *param2);
/* Call method if it exists. Return FAILURE if method does not exist or call failed.
* If FAILURE is returned, retval will be UNDEF. As such, destroying retval unconditionally
* is legal. */
ZEND_API zend_result zend_call_method_if_exists(
zend_object *object, zend_string *method_name, zval *retval,
uint32_t param_count, zval *params);
ZEND_API zend_result zend_set_hash_symbol(zval *symbol, const char *name, size_t name_length, bool is_ref, int num_symbol_tables, ...);
ZEND_API zend_result zend_delete_global_variable(zend_string *name);

View File

@ -719,7 +719,7 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_
}
if (EG(exception)) {
return FAILURE; /* we would result in an instable executor otherwise */
return SUCCESS; /* we would result in an instable executor otherwise */
}
ZEND_ASSERT(fci->size == sizeof(zend_fcall_info));
@ -731,15 +731,14 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_
fci_cache = &fci_cache_local;
}
if (!zend_is_callable_ex(&fci->function_name, fci->object, IS_CALLABLE_CHECK_SILENT, NULL, fci_cache, &error)) {
if (error) {
zend_string *callable_name
= zend_get_callable_name_ex(&fci->function_name, fci->object);
zend_error(E_WARNING, "Invalid callback %s, %s", ZSTR_VAL(callable_name), error);
efree(error);
zend_string_release_ex(callable_name, 0);
}
return FAILURE;
if (!zend_is_callable_ex(&fci->function_name, fci->object, 0, NULL, fci_cache, &error)) {
ZEND_ASSERT(error && "Should have error if not callable");
zend_string *callable_name
= zend_get_callable_name_ex(&fci->function_name, fci->object);
zend_throw_error(NULL, "Invalid callback %s, %s", ZSTR_VAL(callable_name), error);
efree(error);
zend_string_release_ex(callable_name, 0);
return SUCCESS;
}
ZEND_ASSERT(!error);
@ -762,7 +761,7 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_
if (UNEXPECTED(EG(exception))) {
zend_vm_stack_free_call_frame(call);
return FAILURE;
return SUCCESS;
}
}
@ -789,7 +788,7 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_
cleanup_args:
zend_vm_stack_free_args(call);
zend_vm_stack_free_call_frame(call);
return FAILURE;
return SUCCESS;
}
}
}
@ -1010,6 +1009,28 @@ ZEND_API void zend_call_known_instance_method_with_2_params(
zend_call_known_instance_method(fn, object, retval_ptr, 2, params);
}
ZEND_API zend_result zend_call_method_if_exists(
zend_object *object, zend_string *method_name, zval *retval,
uint32_t param_count, zval *params)
{
zend_fcall_info fci;
fci.size = sizeof(zend_fcall_info);
fci.object = object;
ZVAL_STR(&fci.function_name, method_name);
fci.retval = retval;
fci.param_count = param_count;
fci.params = params;
fci.named_params = NULL;
zend_fcall_info_cache fcc;
if (!zend_is_callable_ex(&fci.function_name, fci.object, 0, NULL, &fcc, NULL)) {
ZVAL_UNDEF(retval);
return FAILURE;
}
return zend_call_function(&fci, &fcc);
}
/* 0-9 a-z A-Z _ \ 0x80-0xff */
static const uint32_t valid_chars[8] = {
0x00000000,

View File

@ -47,11 +47,15 @@ var_dump($r2=assert(0 != 0));
echo"\n";
echo "Reset the name of the callback routine to a class method and check that it works\n";
echo "Reset the name of the callback routine to a class method\n";
var_dump($rc=assert_options(ASSERT_CALLBACK, "c1"));
echo "assert_options(ASSERT_CALLBACK) => [".assert_options(ASSERT_CALLBACK)."]\n";
echo "ini.get(\"assert.callback\") => [".ini_get("assert.callback")."]\n";
var_dump($r2=assert(0 != 0));
try {
var_dump($r2=assert(0 != 0));
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
echo"\n";
echo "Reset callback options to use a class method \n";
@ -94,11 +98,11 @@ ini.get("assert.callback") => [f2]
f3 called
bool(false)
Reset the name of the callback routine to a class method and check that it works
Reset the name of the callback routine to a class method
string(2) "f3"
assert_options(ASSERT_CALLBACK) => [c1]
ini.get("assert.callback") => [f2]
bool(false)
Invalid callback c1, function "c1" not found or invalid function name
Reset callback options to use a class method
string(2) "c1"
@ -110,7 +114,7 @@ array(2) {
}
ini.get("assert.callback") => [f2]
Class assertion failed 52, "assert(0 != 0)"
Class assertion failed 56, "assert(0 != 0)"
bool(false)
Reset callback options to use an object method
@ -122,14 +126,14 @@ array(2) {
}
array(2) {
[0]=>
&object(c1)#1 (0) {
&object(c1)#2 (0) {
}
[1]=>
string(6) "assert"
}
ini.get("assert.callback") => [f2]
Class assertion failed 60, "assert(0 != 0)"
Class assertion failed 64, "assert(0 != 0)"
bool(false)
Set callback to something silly

View File

@ -3,14 +3,11 @@ Bad unserialize_callback_func
--FILE--
<?php
ini_set('unserialize_callback_func','Nonexistent');
$o = unserialize('O:3:"FOO":0:{}');
var_dump($o);
echo "Done";
?>
--EXPECTF--
Warning: unserialize(): defined (Nonexistent) but not found in %s on line %d
object(__PHP_Incomplete_Class)#%d (1) {
["__PHP_Incomplete_Class_Name"]=>
string(3) "FOO"
try {
unserialize('O:3:"FOO":0:{}');
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
Done
?>
--EXPECT--
Invalid callback Nonexistent, function "Nonexistent" not found or invalid function name

View File

@ -111,7 +111,6 @@ PHP_RSHUTDOWN_FUNCTION(user_filters)
static void userfilter_dtor(php_stream_filter *thisfilter)
{
zval *obj = &thisfilter->abstract;
zval func_name;
zval retval;
if (Z_ISUNDEF_P(obj)) {
@ -119,16 +118,11 @@ static void userfilter_dtor(php_stream_filter *thisfilter)
return;
}
ZVAL_STRINGL(&func_name, "onclose", sizeof("onclose")-1);
call_user_function(NULL,
obj,
&func_name,
&retval,
0, NULL);
zend_string *func_name = zend_string_init("onclose", sizeof("onclose")-1, 0);
zend_call_method_if_exists(Z_OBJ_P(obj), func_name, &retval, 0, NULL);
zend_string_release(func_name);
zval_ptr_dtor(&retval);
zval_ptr_dtor(&func_name);
/* kill the object */
zval_ptr_dtor(obj);
@ -243,7 +237,6 @@ static php_stream_filter *user_filter_factory_create(const char *filtername,
struct php_user_filter_data *fdat = NULL;
php_stream_filter *filter;
zval obj;
zval func_name;
zval retval;
size_t len;
@ -319,15 +312,9 @@ static php_stream_filter *user_filter_factory_create(const char *filtername,
}
/* invoke the constructor */
ZVAL_STRINGL(&func_name, "oncreate", sizeof("oncreate")-1);
call_user_function(NULL,
&obj,
&func_name,
&retval,
0, NULL);
zval_ptr_dtor(&func_name);
zend_string *func_name = zend_string_init("oncreate", sizeof("oncreate")-1, 0);
zend_call_method_if_exists(Z_OBJ(obj), func_name, &retval, 0, NULL);
zend_string_release(func_name);
if (Z_TYPE(retval) != IS_UNDEF) {
if (Z_TYPE(retval) == IS_FALSE) {

View File

@ -6,45 +6,11 @@ xml
<?php
$var1 = xml_parser_create_ns();
xml_set_element_handler($var1, new Exception(""), 4096);
xml_parse($var1, str_repeat("<a>", 10));
try {
xml_parse($var1, str_repeat("<a>", 10));
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECTF--
Warning: Invalid callback Exception::__invoke, no array or string given in %s on line %d
Warning: xml_parse(): Unable to call handler in %s on line %d
Warning: Invalid callback Exception::__invoke, no array or string given in %s on line %d
Warning: xml_parse(): Unable to call handler in %s on line %d
Warning: Invalid callback Exception::__invoke, no array or string given in %s on line %d
Warning: xml_parse(): Unable to call handler in %s on line %d
Warning: Invalid callback Exception::__invoke, no array or string given in %s on line %d
Warning: xml_parse(): Unable to call handler in %s on line %d
Warning: Invalid callback Exception::__invoke, no array or string given in %s on line %d
Warning: xml_parse(): Unable to call handler in %s on line %d
Warning: Invalid callback Exception::__invoke, no array or string given in %s on line %d
Warning: xml_parse(): Unable to call handler in %s on line %d
Warning: Invalid callback Exception::__invoke, no array or string given in %s on line %d
Warning: xml_parse(): Unable to call handler in %s on line %d
Warning: Invalid callback Exception::__invoke, no array or string given in %s on line %d
Warning: xml_parse(): Unable to call handler in %s on line %d
Warning: Invalid callback Exception::__invoke, no array or string given in %s on line %d
Warning: xml_parse(): Unable to call handler in %s on line %d
Warning: Invalid callback Exception::__invoke, no array or string given in %s on line %d
Warning: xml_parse(): Unable to call handler in %s on line %d
--EXPECT--
Invalid callback Exception::__invoke, no array or string given

View File

@ -275,6 +275,13 @@ typedef struct _php_userstream_data php_userstream_data_t;
}}} **/
static zend_result call_method_if_exists(
zval *object, zval *method_name, zval *retval, uint32_t param_count, zval *params)
{
return zend_call_method_if_exists(
Z_OBJ_P(object), Z_STR_P(method_name), retval, param_count, params);
}
static void user_stream_create_object(struct php_user_stream_wrapper *uwrap, php_stream_context *context, zval *object)
{
if (uwrap->ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) {
@ -350,7 +357,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *
ZVAL_STRING(&zfuncname, USERSTREAM_OPEN);
zend_try {
call_result = call_user_function(NULL, &us->object, &zfuncname, &zretval, 4, args);
call_result = call_method_if_exists(&us->object, &zfuncname, &zretval, 4, args);
} zend_catch {
FG(user_stream_current_filename) = NULL;
zend_bailout();
@ -424,7 +431,7 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char
ZVAL_STRING(&zfuncname, USERSTREAM_DIR_OPEN);
call_result = call_user_function(NULL, &us->object, &zfuncname, &zretval, 2, args);
call_result = call_method_if_exists(&us->object, &zfuncname, &zretval, 2, args);
if (call_result == SUCCESS && Z_TYPE(zretval) != IS_UNDEF && zval_is_true(&zretval)) {
/* the stream is now open! */
@ -563,7 +570,7 @@ static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_
ZVAL_STRINGL(&args[0], (char*)buf, count);
call_result = call_user_function(NULL, &us->object, &func_name, &retval, 1, args);
call_result = call_method_if_exists(&us->object, &func_name, &retval, 1, args);
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&func_name);
@ -612,7 +619,7 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count
ZVAL_LONG(&args[0], count);
call_result = call_user_function(NULL, &us->object, &func_name, &retval, 1, args);
call_result = call_method_if_exists(&us->object, &func_name, &retval, 1, args);
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&func_name);
@ -651,7 +658,7 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count
/* since the user stream has no way of setting the eof flag directly, we need to ask it if we hit eof */
ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1);
call_result = call_user_function(NULL, &us->object, &func_name, &retval, 0, NULL);
call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
zval_ptr_dtor(&func_name);
if (EG(exception)) {
@ -684,7 +691,7 @@ static int php_userstreamop_close(php_stream *stream, int close_handle)
ZVAL_STRINGL(&func_name, USERSTREAM_CLOSE, sizeof(USERSTREAM_CLOSE)-1);
call_user_function(NULL, &us->object, &func_name, &retval, 0, NULL);
call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
zval_ptr_dtor(&retval);
zval_ptr_dtor(&func_name);
@ -708,7 +715,7 @@ static int php_userstreamop_flush(php_stream *stream)
ZVAL_STRINGL(&func_name, USERSTREAM_FLUSH, sizeof(USERSTREAM_FLUSH)-1);
call_result = call_user_function(NULL, &us->object, &func_name, &retval, 0, NULL);
call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF && zval_is_true(&retval))
call_result = 0;
@ -736,7 +743,7 @@ static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int when
ZVAL_LONG(&args[0], offset);
ZVAL_LONG(&args[1], whence);
call_result = call_user_function(NULL, &us->object, &func_name, &retval, 2, args);
call_result = call_method_if_exists(&us->object, &func_name, &retval, 2, args);
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&args[1]);
@ -766,7 +773,7 @@ static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int when
/* now determine where we are */
ZVAL_STRINGL(&func_name, USERSTREAM_TELL, sizeof(USERSTREAM_TELL)-1);
call_result = call_user_function(NULL, &us->object, &func_name, &retval, 0, NULL);
call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
if (call_result == SUCCESS && Z_TYPE(retval) == IS_LONG) {
*newoffs = Z_LVAL(retval);
@ -832,7 +839,7 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb)
ZVAL_STRINGL(&func_name, USERSTREAM_STAT, sizeof(USERSTREAM_STAT)-1);
call_result = call_user_function(NULL, &us->object, &func_name, &retval, 0, NULL);
call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
if (call_result == SUCCESS && Z_TYPE(retval) == IS_ARRAY) {
if (SUCCESS == statbuf_from_array(&retval, ssb))
@ -862,7 +869,7 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value
switch (option) {
case PHP_STREAM_OPTION_CHECK_LIVENESS:
ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1);
call_result = call_user_function(NULL, &us->object, &func_name, &retval, 0, NULL);
call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
if (call_result == SUCCESS && (Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
ret = zval_is_true(&retval) ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
} else {
@ -896,7 +903,7 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value
/* TODO wouldblock */
ZVAL_STRINGL(&func_name, USERSTREAM_LOCK, sizeof(USERSTREAM_LOCK)-1);
call_result = call_user_function(NULL, &us->object, &func_name, &retval, 1, args);
call_result = call_method_if_exists(&us->object, &func_name, &retval, 1, args);
if (call_result == SUCCESS && (Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
ret = (Z_TYPE(retval) == IS_FALSE);
@ -931,7 +938,7 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value
ptrdiff_t new_size = *(ptrdiff_t*) ptrparam;
if (new_size >= 0 && new_size <= (ptrdiff_t)LONG_MAX) {
ZVAL_LONG(&args[0], (zend_long)new_size);
call_result = call_user_function(NULL, &us->object, &func_name, &retval, 1, args);
call_result = call_method_if_exists(&us->object, &func_name, &retval, 1, args);
if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
if (Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE) {
ret = (Z_TYPE(retval) == IS_TRUE) ? PHP_STREAM_OPTION_RETURN_OK :
@ -991,7 +998,7 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value
break;
}
call_result = call_user_function(NULL, &us->object, &func_name, &retval, 3, args);
call_result = call_method_if_exists(&us->object, &func_name, &retval, 3, args);
if (call_result == FAILURE) {
php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_SET_OPTION " is not implemented!",
@ -1037,11 +1044,7 @@ static int user_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int
ZVAL_STRING(&zfuncname, USERSTREAM_UNLINK);
call_result = call_user_function(NULL,
&object,
&zfuncname,
&zretval,
1, args);
call_result = call_method_if_exists(&object, &zfuncname, &zretval, 1, args);
if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
ret = (Z_TYPE(zretval) == IS_TRUE);
@ -1081,11 +1084,7 @@ static int user_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from
ZVAL_STRING(&zfuncname, USERSTREAM_RENAME);
call_result = call_user_function(NULL,
&object,
&zfuncname,
&zretval,
2, args);
call_result = call_method_if_exists(&object, &zfuncname, &zretval, 2, args);
if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
ret = (Z_TYPE(zretval) == IS_TRUE);
@ -1127,11 +1126,7 @@ static int user_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url, int
ZVAL_STRING(&zfuncname, USERSTREAM_MKDIR);
call_result = call_user_function(NULL,
&object,
&zfuncname,
&zretval,
3, args);
call_result = call_method_if_exists(&object, &zfuncname, &zretval, 3, args);
if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
ret = (Z_TYPE(zretval) == IS_TRUE);
@ -1173,11 +1168,7 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url,
ZVAL_STRING(&zfuncname, USERSTREAM_RMDIR);
call_result = call_user_function(NULL,
&object,
&zfuncname,
&zretval,
2, args);
call_result = call_method_if_exists(&object, &zfuncname, &zretval, 2, args);
if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
ret = (Z_TYPE(zretval) == IS_TRUE);
@ -1243,11 +1234,7 @@ static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url, i
ZVAL_STRING(&zfuncname, USERSTREAM_METADATA);
call_result = call_user_function(NULL,
&object,
&zfuncname,
&zretval,
3, args);
call_result = call_method_if_exists(&object, &zfuncname, &zretval, 3, args);
if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) {
ret = Z_TYPE(zretval) == IS_TRUE;
@ -1290,11 +1277,7 @@ static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char *url, i
ZVAL_STRING(&zfuncname, USERSTREAM_STATURL);
call_result = call_user_function(NULL,
&object,
&zfuncname,
&zretval,
2, args);
call_result = call_method_if_exists(&object, &zfuncname, &zretval, 2, args);
if (call_result == SUCCESS && Z_TYPE(zretval) == IS_ARRAY) {
/* We got the info we needed */
@ -1334,7 +1317,7 @@ static ssize_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t co
ZVAL_STRINGL(&func_name, USERSTREAM_DIR_READ, sizeof(USERSTREAM_DIR_READ)-1);
call_result = call_user_function(NULL, &us->object, &func_name, &retval, 0, NULL);
call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
if (call_result == SUCCESS && Z_TYPE(retval) != IS_FALSE && Z_TYPE(retval) != IS_TRUE) {
convert_to_string(&retval);
@ -1362,7 +1345,7 @@ static int php_userstreamop_closedir(php_stream *stream, int close_handle)
ZVAL_STRINGL(&func_name, USERSTREAM_DIR_CLOSE, sizeof(USERSTREAM_DIR_CLOSE)-1);
call_user_function(NULL, &us->object, &func_name, &retval, 0, NULL);
call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
zval_ptr_dtor(&retval);
zval_ptr_dtor(&func_name);
@ -1382,7 +1365,7 @@ static int php_userstreamop_rewinddir(php_stream *stream, zend_off_t offset, int
ZVAL_STRINGL(&func_name, USERSTREAM_DIR_REWIND, sizeof(USERSTREAM_DIR_REWIND)-1);
call_user_function(NULL, &us->object, &func_name, &retval, 0, NULL);
call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
zval_ptr_dtor(&retval);
zval_ptr_dtor(&func_name);
@ -1412,7 +1395,7 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr)
break;
}
call_result = call_user_function(NULL, &us->object, &func_name, &retval, 1, args);
call_result = call_method_if_exists(&us->object, &func_name, &retval, 1, args);
do {
if (call_result == FAILURE) {