[php-src] master: main: refactor userstream method calling

From: Date: Fri, 15 Aug 2025 16:41:37 +0000
Subject: [php-src] master: main: refactor userstream method calling
Groups: php.cvs 
Request: Send a blank email to [email protected] to get a copy of this message
Author: Gina Peter Banyard (Girgias)
Date: 2025-08-15T18:41:17+02:00

Commit: https://github.com/php/php-src/commit/0992265611e0cd6abdc4bc0e6938d881f0f08eef
Raw diff: https://github.com/php/php-src/commit/0992265611e0cd6abdc4bc0e6938d881f0f08eef.diff

main: refactor userstream method calling

Closes GH-19312

Changed paths:
  M  Zend/tests/bug41421.phpt
  M  ext/standard/tests/file/userstreams_005.phpt
  M  ext/zip/tests/bug53603.phpt
  M  main/streams/userspace.c


Diff:

diff --git a/Zend/tests/bug41421.phpt b/Zend/tests/bug41421.phpt
index 762c32b96df80..fdf86da443867 100644
--- a/Zend/tests/bug41421.phpt
+++ b/Zend/tests/bug41421.phpt
@@ -9,22 +9,19 @@ class wrapper {
         return true;
     }
     function stream_eof() {
-        throw new exception();
+        throw new Exception('cannot eof');
     }
 }
 
 stream_wrapper_register("wrap", "wrapper");
 $fp = fopen("wrap://...", "r");
-feof($fp);
 
-echo "Done\n";
-?>
---EXPECTF--
-Warning: feof(): wrapper::stream_eof is not implemented! Assuming EOF in %s on line %d
+try {
+    feof($fp);
+} catch (Throwable $e) {
+    echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+}
 
-Fatal error: Uncaught Exception in %s:%d
-Stack trace:
-#0 [internal function]: wrapper->stream_eof()
-#1 %s(%d): feof(Resource id #%d)
-#2 {main}
-  thrown in %s on line %d
+?>
+--EXPECT--
+Exception: cannot eof
diff --git a/ext/standard/tests/file/userstreams_005.phpt
b/ext/standard/tests/file/userstreams_005.phpt
index 8d37040e1950b..11dc8d45b0cd6 100644
--- a/ext/standard/tests/file/userstreams_005.phpt
+++ b/ext/standard/tests/file/userstreams_005.phpt
@@ -65,5 +65,5 @@ ftruncate(): Argument #2 ($size) must be greater than or equal to 0
 ------ stream_truncate bad return: -------
 truncation with new_size=0
 
-Warning: ftruncate(): test_wrapper_bad::stream_truncate did not return a boolean! in %s on line %d
+Warning: ftruncate(): test_wrapper_bad::stream_truncate value must be of type bool, string given in
%s on line %d
 bool(false)
diff --git a/ext/zip/tests/bug53603.phpt b/ext/zip/tests/bug53603.phpt
index 5423206233977..914b0b8440c61 100644
--- a/ext/zip/tests/bug53603.phpt
+++ b/ext/zip/tests/bug53603.phpt
@@ -28,5 +28,5 @@ $a = $zip->extractTo('teststream://test');
 var_dump($a);
 ?>
 --EXPECTF--
-Warning: ZipArchive::extractTo(teststream://test/foo): Failed to open stream:
"TestStream::stream_open" call failed in %s on line %d
+Warning: ZipArchive::extractTo(teststream://test/foo): Failed to open stream:
"TestStream::stream_open" is not implemented in %s on line %d
 bool(false)
diff --git a/main/streams/userspace.c b/main/streams/userspace.c
index a25900eda3054..8e378120978d6 100644
--- a/main/streams/userspace.c
+++ b/main/streams/userspace.c
@@ -251,13 +251,6 @@ 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))
{
@@ -295,7 +288,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *
 {
 	struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
 	php_userstream_data_t *us;
-	zval zretval, zfuncname;
+	zval zretval;
 	zval args[4];
 	php_stream *stream = NULL;
 	bool old_in_user_include;
@@ -320,34 +313,46 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char
*
 
 	us = emalloc(sizeof(*us));
 	us->wrapper = uwrap;
-	/* call_method_if_exists() may unregister the stream wrapper. Hold on to it. */
+	/* zend_call_method_if_exists() may unregister the stream wrapper. Hold on to it. */
 	GC_ADDREF(us->wrapper->resource);
 
 	user_stream_create_object(uwrap, context, &us->object);
-	if (Z_TYPE(us->object) == IS_UNDEF) {
-		FG(user_stream_current_filename) = NULL;
-		PG(in_user_include) = old_in_user_include;
-		efree(us);
-		return NULL;
+	if (Z_ISUNDEF(us->object)) {
+		goto end;
 	}
 
 	/* call it's stream_open method - set up params first */
-	ZVAL_STRING(&args[0], filename);
-	ZVAL_STRING(&args[1], mode);
-	ZVAL_LONG(&args[2], options);
-	ZVAL_NEW_REF(&args[3], &EG(uninitialized_zval));
-
-	ZVAL_STRING(&zfuncname, USERSTREAM_OPEN);
-
 	zend_result call_result;
 	zend_try {
-		call_result = call_method_if_exists(&us->object, &zfuncname, &zretval, 4, args);
+		ZVAL_STRING(&args[0], filename);
+		ZVAL_STRING(&args[1], mode);
+		ZVAL_LONG(&args[2], options);
+		ZVAL_NEW_REF(&args[3], &EG(uninitialized_zval));
+
+		zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_OPEN, false);
+		call_result = zend_call_method_if_exists(Z_OBJ(us->object), func_name, &zretval, 4, args);
+		zend_string_release_ex(func_name, false);
+
+		/* Keep arg3 alive if it has assigned the reference */
+		zval_ptr_dtor(&args[1]);
+		zval_ptr_dtor(&args[0]);
 	} zend_catch {
 		FG(user_stream_current_filename) = NULL;
 		zend_bailout();
 	} zend_end_try();
 
-	if (call_result == SUCCESS && Z_TYPE(zretval) != IS_UNDEF &&
zval_is_true(&zretval)) {
+	if (UNEXPECTED(call_result == FAILURE)) {
+		php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_OPEN
"\" is not implemented",
+			ZSTR_VAL(us->wrapper->ce->name));
+		zval_ptr_dtor(&args[3]);
+		goto end;
+	}
+	/* Exception occurred */
+	if (UNEXPECTED(Z_ISUNDEF(zretval))) {
+		zval_ptr_dtor(&args[3]);
+		goto end;
+	}
+	if (zval_is_true(&zretval)) {
 		/* the stream is now open! */
 		stream = php_stream_alloc_rel(&php_stream_userspace_ops, us, 0, mode);
 
@@ -355,6 +360,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *
 		if (Z_ISREF(args[3]) && Z_TYPE_P(Z_REFVAL(args[3])) == IS_STRING && opened_path)
{
 			*opened_path = zend_string_copy(Z_STR_P(Z_REFVAL(args[3])));
 		}
+		// TODO Warn when assigning a non string value to the reference?
 
 		/* set wrapper data to be a reference to our object */
 		ZVAL_COPY(&stream->wrapperdata, &us->object);
@@ -363,23 +369,17 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char
*
 			ZSTR_VAL(us->wrapper->ce->name));
 	}
 
-	/* destroy everything else */
-	if (stream == NULL) {
-		zval_ptr_dtor(&us->object);
-		ZVAL_UNDEF(&us->object);
-		zend_list_delete(us->wrapper->resource);
-		efree(us);
-	}
 	zval_ptr_dtor(&zretval);
-	zval_ptr_dtor(&zfuncname);
 	zval_ptr_dtor(&args[3]);
-	zval_ptr_dtor(&args[2]);
-	zval_ptr_dtor(&args[1]);
-	zval_ptr_dtor(&args[0]);
 
+end:
 	FG(user_stream_current_filename) = NULL;
-
 	PG(in_user_include) = old_in_user_include;
+	if (stream == NULL) {
+		zval_ptr_dtor(&us->object);
+		zend_list_delete(us->wrapper->resource);
+		efree(us);
+	}
 	return stream;
 }
 
@@ -396,7 +396,7 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char
 {
 	struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
 	php_userstream_data_t *us;
-	zval zretval, zfuncname;
+	zval zretval;
 	zval args[2];
 	php_stream *stream = NULL;
 
@@ -409,25 +409,34 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const
char
 
 	us = emalloc(sizeof(*us));
 	us->wrapper = uwrap;
-	/* call_method_if_exists() may unregister the stream wrapper. Hold on to it. */
+	/* zend_call_method_if_exists() may unregister the stream wrapper. Hold on to it. */
 	GC_ADDREF(us->wrapper->resource);
 
 	user_stream_create_object(uwrap, context, &us->object);
 	if (Z_TYPE(us->object) == IS_UNDEF) {
-		FG(user_stream_current_filename) = NULL;
-		efree(us);
-		return NULL;
+		goto end;
 	}
 
-	/* call it's dir_open method - set up params first */
+	/* call its dir_open method - set up params first */
 	ZVAL_STRING(&args[0], filename);
 	ZVAL_LONG(&args[1], options);
 
-	ZVAL_STRING(&zfuncname, USERSTREAM_DIR_OPEN);
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_DIR_OPEN, false);
+	zend_result call_result = zend_call_method_if_exists(Z_OBJ(us->object), func_name,
&zretval, 2, args);
+	zend_string_release_ex(func_name, false);
+	zval_ptr_dtor(&args[0]);
 
-	zend_result call_result = call_method_if_exists(&us->object, &zfuncname, &zretval,
2, args);
+	if (UNEXPECTED(call_result == FAILURE)) {
+		php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_DIR_OPEN
"\" is not implemented",
+			ZSTR_VAL(us->wrapper->ce->name));
+		goto end;
+	}
+	/* Exception occurred in call */
+	if (UNEXPECTED(Z_ISUNDEF(zretval))) {
+		goto end;
+	}
 
-	if (call_result == SUCCESS && Z_TYPE(zretval) != IS_UNDEF &&
zval_is_true(&zretval)) {
+	if (zval_is_true(&zretval)) {
 		/* the stream is now open! */
 		stream = php_stream_alloc_rel(&php_stream_userspace_dir_ops, us, 0, mode);
 
@@ -437,22 +446,15 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const
char
 		php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_DIR_OPEN
"\" call failed",
 			ZSTR_VAL(us->wrapper->ce->name));
 	}
+	zval_ptr_dtor(&zretval);
 
-	/* destroy everything else */
+end:
+	FG(user_stream_current_filename) = NULL;
 	if (stream == NULL) {
 		zval_ptr_dtor(&us->object);
-		ZVAL_UNDEF(&us->object);
 		zend_list_delete(us->wrapper->resource);
 		efree(us);
 	}
-	zval_ptr_dtor(&zretval);
-
-	zval_ptr_dtor(&zfuncname);
-	zval_ptr_dtor(&args[1]);
-	zval_ptr_dtor(&args[0]);
-
-	FG(user_stream_current_filename) = NULL;
-
 	return stream;
 }
 
@@ -561,7 +563,6 @@ PHP_FUNCTION(stream_wrapper_restore)
 
 static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_t count)
 {
-	zval func_name;
 	zval retval;
 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
 	zval args[1];
@@ -569,29 +570,27 @@ static ssize_t php_userstreamop_write(php_stream *stream, const char *buf,
size_
 
 	assert(us != NULL);
 
-	ZVAL_STRINGL(&func_name, USERSTREAM_WRITE, sizeof(USERSTREAM_WRITE)-1);
-
 	ZVAL_STRINGL(&args[0], (char*)buf, count);
 
-	zend_result call_result = call_method_if_exists(&us->object, &func_name, &retval,
1, args);
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_WRITE, false);
+	zend_result call_result = zend_call_method_if_exists(Z_OBJ(us->object), func_name, &retval,
1, args);
+	zend_string_release_ex(func_name, false);
 	zval_ptr_dtor(&args[0]);
-	zval_ptr_dtor(&func_name);
 
-	if (EG(exception)) {
+	if (UNEXPECTED(call_result == FAILURE)) {
+		php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_WRITE " is not
implemented!",
+				ZSTR_VAL(us->wrapper->ce->name));
+	}
+	/* Exception occurred */
+	if (Z_ISUNDEF(retval)) {
 		return -1;
 	}
 
-	if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
-		if (Z_TYPE(retval) == IS_FALSE) {
-			didwrite = -1;
-		} else {
-			convert_to_long(&retval);
-			didwrite = Z_LVAL(retval);
-		}
-	} else {
-		php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_WRITE " is not
implemented!",
-				ZSTR_VAL(us->wrapper->ce->name));
+	if (Z_TYPE(retval) == IS_FALSE) {
 		didwrite = -1;
+	} else {
+		convert_to_long(&retval);
+		didwrite = Z_LVAL(retval);
 	}
 
 	/* don't allow strange buffer overruns due to bogus return */
@@ -609,7 +608,6 @@ static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_
 
 static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count)
 {
-	zval func_name;
 	zval retval;
 	zval args[1];
 	size_t didread = 0;
@@ -617,20 +615,16 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t
count
 
 	assert(us != NULL);
 
-	ZVAL_STRINGL(&func_name, USERSTREAM_READ, sizeof(USERSTREAM_READ)-1);
-
 	ZVAL_LONG(&args[0], count);
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_READ, false);
+	zend_result call_result = zend_call_method_if_exists(Z_OBJ(us->object), func_name, &retval,
1, args);
+	zend_string_release_ex(func_name, false);
 
-	zend_result call_result = call_method_if_exists(&us->object, &func_name, &retval,
1, args);
-
-	zval_ptr_dtor(&args[0]);
-	zval_ptr_dtor(&func_name);
-
-	if (EG(exception)) {
+	if (UNEXPECTED(Z_ISUNDEF(retval))) {
 		return -1;
 	}
 
-	if (call_result == FAILURE) {
+	if (UNEXPECTED(call_result == FAILURE)) {
 		php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_READ " is not
implemented!",
 				ZSTR_VAL(us->wrapper->ce->name));
 		return -1;
@@ -660,25 +654,25 @@ 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_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
-	zval_ptr_dtor(&func_name);
+	func_name = ZSTR_INIT_LITERAL(USERSTREAM_EOF, false);
+	call_result = zend_call_method_if_exists(Z_OBJ(us->object), func_name, &retval, 0, NULL);
+	zend_string_release_ex(func_name, false);
 
-	if (EG(exception)) {
+	if (UNEXPECTED(call_result == FAILURE)) {
+		php_error_docref(NULL, E_WARNING,
+				"%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
+				ZSTR_VAL(us->wrapper->ce->name));
 		stream->eof = 1;
 		return -1;
 	}
-
-	if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF &&
zval_is_true(&retval)) {
+	if (UNEXPECTED(Z_ISUNDEF(retval))) {
 		stream->eof = 1;
-	} else if (call_result == FAILURE) {
-		php_error_docref(NULL, E_WARNING,
-				"%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
-				ZSTR_VAL(us->wrapper->ce->name));
+		return -1;
+	}
 
+	if (zval_is_true(&retval)) {
 		stream->eof = 1;
 	}
-
 	zval_ptr_dtor(&retval);
 
 	return didread;
@@ -686,18 +680,16 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t
count
 
 static int php_userstreamop_close(php_stream *stream, int close_handle)
 {
-	zval func_name;
 	zval retval;
 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
 
 	assert(us != NULL);
 
-	ZVAL_STRINGL(&func_name, USERSTREAM_CLOSE, sizeof(USERSTREAM_CLOSE)-1);
-
-	call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_CLOSE, false);
+	zend_call_method_if_exists(Z_OBJ(us->object), func_name, &retval, 0, NULL);
+	zend_string_release_ex(func_name, false);
 
 	zval_ptr_dtor(&retval);
-	zval_ptr_dtor(&func_name);
 
 	zval_ptr_dtor(&us->object);
 	ZVAL_UNDEF(&us->object);
@@ -709,30 +701,24 @@ static int php_userstreamop_close(php_stream *stream, int close_handle)
 
 static int php_userstreamop_flush(php_stream *stream)
 {
-	zval func_name;
 	zval retval;
 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
 
 	assert(us != NULL);
 
-	ZVAL_STRINGL(&func_name, USERSTREAM_FLUSH, sizeof(USERSTREAM_FLUSH)-1);
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_FLUSH, false);
+	zend_result call_result = zend_call_method_if_exists(Z_OBJ(us->object), func_name, &retval,
0, NULL);
+	zend_string_release_ex(func_name, false);
 
-	zend_result 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;
-	else
-		call_result = -1;
+	int ret = call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF &&
zval_is_true(&retval) ? 0 : -1;
 
 	zval_ptr_dtor(&retval);
-	zval_ptr_dtor(&func_name);
 
-	return call_result;
+	return ret;
 }
 
 static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t
*newoffs)
 {
-	zval func_name;
 	zval retval;
 	int ret;
 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
@@ -740,16 +726,15 @@ static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int
when
 
 	assert(us != NULL);
 
-	ZVAL_STRINGL(&func_name, USERSTREAM_SEEK, sizeof(USERSTREAM_SEEK)-1);
-
 	ZVAL_LONG(&args[0], offset);
 	ZVAL_LONG(&args[1], whence);
 
-	zend_result call_result = call_method_if_exists(&us->object, &func_name, &retval,
2, args);
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_SEEK, false);
+	zend_result call_result = zend_call_method_if_exists(Z_OBJ(us->object), func_name, &retval,
2, args);
+	zend_string_release_ex(func_name, false);
 
 	zval_ptr_dtor(&args[0]);
 	zval_ptr_dtor(&args[1]);
-	zval_ptr_dtor(&func_name);
 
 	if (call_result == FAILURE) {
 		/* stream_seek is not implemented, so disable seeks for this stream */
@@ -773,14 +758,14 @@ 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_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
+	func_name = ZSTR_INIT_LITERAL(USERSTREAM_TELL, false);
+	call_result = zend_call_method_if_exists(Z_OBJ(us->object), func_name, &retval, 0, NULL);
+	zend_string_release_ex(func_name, false);
 
 	if (call_result == SUCCESS && Z_TYPE(retval) == IS_LONG) {
 		*newoffs = Z_LVAL(retval);
 		ret = 0;
-	} else if (call_result == FAILURE) {
+	} else if (UNEXPECTED(call_result == FAILURE)) {
 		php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_TELL " is not
implemented!", ZSTR_VAL(us->wrapper->ce->name));
 		ret = -1;
 	} else {
@@ -788,7 +773,6 @@ static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int when
 	}
 
 	zval_ptr_dtor(&retval);
-	zval_ptr_dtor(&func_name);
 	return ret;
 }
 
@@ -832,202 +816,239 @@ static void statbuf_from_array(const HashTable *array, php_stream_statbuf
*ssb)
 
 static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb)
 {
-	zval func_name;
 	zval retval;
 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
 	int ret = -1;
 
-	ZVAL_STRINGL(&func_name, USERSTREAM_STAT, sizeof(USERSTREAM_STAT)-1);
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_STAT, false);
+	zend_result call_result = zend_call_method_if_exists(Z_OBJ(us->object), func_name, &retval,
0, NULL);
+	zend_string_release_ex(func_name, false);
 
-	zend_result call_result = call_method_if_exists(&us->object, &func_name, &retval,
0, NULL);
+	if (UNEXPECTED(call_result == FAILURE)) {
+		php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STAT " is not
implemented!",
+				ZSTR_VAL(us->wrapper->ce->name));
+		return -1;
+	}
+	if (UNEXPECTED(Z_ISUNDEF(retval))) {
+		return -1;
+	}
 
-	if (call_result == SUCCESS && Z_TYPE(retval) == IS_ARRAY) {
+	if (EXPECTED(Z_TYPE(retval) == IS_ARRAY)) {
 		statbuf_from_array(Z_ARR(retval), ssb);
 		ret = 0;
-	} else {
-		if (call_result == FAILURE) {
-			php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STAT " is not
implemented!",
-					ZSTR_VAL(us->wrapper->ce->name));
-		}
 	}
+	// TODO: Warning on incorrect return type?
 
 	zval_ptr_dtor(&retval);
-	zval_ptr_dtor(&func_name);
 
 	return ret;
 }
 
-
-static int php_userstreamop_set_option(php_stream *stream, int option, int value, void *ptrparam) {
-	zval func_name;
+static int user_stream_set_check_liveliness(const php_userstream_data_t *us)
+{
 	zval retval;
-	zend_result call_result;
-	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
-	int ret = PHP_STREAM_OPTION_RETURN_NOTIMPL;
-	zval args[3];
 
-	switch (option) {
-	case PHP_STREAM_OPTION_CHECK_LIVENESS:
-		ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1);
-		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 {
-			ret = PHP_STREAM_OPTION_RETURN_ERR;
-			php_error_docref(NULL, E_WARNING,
-					"%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
-					ZSTR_VAL(us->wrapper->ce->name));
-		}
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_EOF, false);
+	zend_result call_result = zend_call_method_if_exists(Z_OBJ(us->object), func_name, &retval,
0, NULL);
+	zend_string_release_ex(func_name, false);
+
+	if (UNEXPECTED(call_result == FAILURE)) {
+		php_error_docref(NULL, E_WARNING,
+				"%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
+				ZSTR_VAL(us->wrapper->ce->name));
+		return PHP_STREAM_OPTION_RETURN_ERR;
+	}
+	if (UNEXPECTED(Z_ISUNDEF(retval))) {
+		return PHP_STREAM_OPTION_RETURN_ERR;
+	}
+	if (EXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
+		return Z_TYPE(retval) == IS_TRUE ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
+	} else {
+		php_error_docref(NULL, E_WARNING,
+			"%s::" USERSTREAM_EOF " value must be of type bool, %s given",
+				ZSTR_VAL(us->wrapper->ce->name), zend_zval_value_name(&retval));
 		zval_ptr_dtor(&retval);
-		zval_ptr_dtor(&func_name);
-		break;
+		return PHP_STREAM_OPTION_RETURN_ERR;
+	}
+}
 
-	case PHP_STREAM_OPTION_LOCKING:
-		ZVAL_LONG(&args[0], 0);
+static int user_stream_set_locking(const php_userstream_data_t *us, int value)
+{
+	zval retval;
+	zval zlock;
+	zend_long lock = 0;
 
-		if (value & LOCK_NB) {
-			Z_LVAL_P(&args[0]) |= PHP_LOCK_NB;
-		}
-		switch(value & ~LOCK_NB) {
+	if (value & LOCK_NB) {
+		lock |= PHP_LOCK_NB;
+	}
+	switch (value & ~LOCK_NB) {
 		case LOCK_SH:
-			Z_LVAL_P(&args[0]) |= PHP_LOCK_SH;
+			lock |= PHP_LOCK_SH;
 			break;
 		case LOCK_EX:
-			Z_LVAL_P(&args[0]) |= PHP_LOCK_EX;
+			lock |= PHP_LOCK_EX;
 			break;
 		case LOCK_UN:
-			Z_LVAL_P(&args[0]) |= PHP_LOCK_UN;
+			lock |= PHP_LOCK_UN;
 			break;
-		}
-
-		/* TODO wouldblock */
-		ZVAL_STRINGL(&func_name, USERSTREAM_LOCK, sizeof(USERSTREAM_LOCK)-1);
-
-		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);
-		} else if (call_result == FAILURE) {
-			if (value == 0) {
-			   	/* lock support test (TODO: more check) */
-				ret = PHP_STREAM_OPTION_RETURN_OK;
-			} else {
-				php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_LOCK " is not
implemented!",
-								 ZSTR_VAL(us->wrapper->ce->name));
-				ret = PHP_STREAM_OPTION_RETURN_ERR;
-			}
-		}
-
-		zval_ptr_dtor(&retval);
-		zval_ptr_dtor(&func_name);
-		zval_ptr_dtor(&args[0]);
-		break;
-
-	case PHP_STREAM_OPTION_TRUNCATE_API:
-		ZVAL_STRINGL(&func_name, USERSTREAM_TRUNCATE, sizeof(USERSTREAM_TRUNCATE)-1);
+		default:
+			// TODO: Warn on invalid option value?
+			;
+	}
+	ZVAL_LONG(&zlock, lock);
 
-		switch (value) {
-		case PHP_STREAM_TRUNCATE_SUPPORTED:
-			if (zend_is_callable_ex(&func_name, Z_OBJ(us->object), IS_CALLABLE_SUPPRESS_DEPRECATIONS,
NULL, NULL, NULL))
-				ret = PHP_STREAM_OPTION_RETURN_OK;
-			else
-				ret = PHP_STREAM_OPTION_RETURN_ERR;
-			break;
+	/* TODO wouldblock */
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_LOCK, false);
+	zend_result call_result = zend_call_method_if_exists(Z_OBJ(us->object), func_name, &retval,
1, &zlock);
+	zend_string_release_ex(func_name, false);
 
-		case PHP_STREAM_TRUNCATE_SET_SIZE: {
-			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_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 :
-											   PHP_STREAM_OPTION_RETURN_ERR;
-					} else {
-						php_error_docref(NULL, E_WARNING,
-								"%s::" USERSTREAM_TRUNCATE " did not return a boolean!",
-								ZSTR_VAL(us->wrapper->ce->name));
-					}
-				} else {
-					php_error_docref(NULL, E_WARNING,
-							"%s::" USERSTREAM_TRUNCATE " is not implemented!",
-							ZSTR_VAL(us->wrapper->ce->name));
-				}
-				zval_ptr_dtor(&retval);
-				zval_ptr_dtor(&args[0]);
-			} else { /* bad new size */
-				ret = PHP_STREAM_OPTION_RETURN_ERR;
-			}
-			break;
-		}
+	if (UNEXPECTED(call_result == FAILURE)) {
+		if (value == 0) {
+			/* lock support test (TODO: more check) */
+			return PHP_STREAM_OPTION_RETURN_OK;
 		}
-		zval_ptr_dtor(&func_name);
-		break;
+		php_error_docref(NULL, E_WARNING,
+				"%s::" USERSTREAM_LOCK " is not implemented!",
+				ZSTR_VAL(us->wrapper->ce->name));
+		return PHP_STREAM_OPTION_RETURN_ERR;
+	}
+	if (UNEXPECTED(Z_ISUNDEF(retval))) {
+		return PHP_STREAM_OPTION_RETURN_ERR;
+	}
+	if (EXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
+		// This is somewhat confusing and relies on magic numbers.
+		return Z_TYPE(retval) == IS_FALSE;
+	}
+	// TODO: ext/standard/tests/file/userstreams_004.phpt returns null implicitly for function
+	// Should this warn or not? And should this be considered an error?
+	//php_error_docref(NULL, E_WARNING,
+	//	"%s::" USERSTREAM_LOCK " value must be of type bool, %s given",
+	//		ZSTR_VAL(us->wrapper->ce->name), zend_zval_value_name(&retval));
+	zval_ptr_dtor(&retval);
+	return PHP_STREAM_OPTION_RETURN_NOTIMPL;
+}
 
-	case PHP_STREAM_OPTION_READ_BUFFER:
-	case PHP_STREAM_OPTION_WRITE_BUFFER:
-	case PHP_STREAM_OPTION_READ_TIMEOUT:
-	case PHP_STREAM_OPTION_BLOCKING: {
+static int user_stream_set_truncation(const php_userstream_data_t *us, int value, void *ptrparam) {
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_TRUNCATE, false);
 
-		ZVAL_STRINGL(&func_name, USERSTREAM_SET_OPTION, sizeof(USERSTREAM_SET_OPTION)-1);
+	if (value == PHP_STREAM_TRUNCATE_SUPPORTED) {
+		zval zstr;
+		ZVAL_STR(&zstr, func_name);
+		bool is_callable = zend_is_callable_ex(&zstr, Z_OBJ(us->object),
IS_CALLABLE_SUPPRESS_DEPRECATIONS, NULL, NULL, NULL);
+		// Frees func_name
+		zval_ptr_dtor(&zstr);
+		return is_callable ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
+	}
+	ZEND_ASSERT(value == PHP_STREAM_TRUNCATE_SET_SIZE);
+	ptrdiff_t new_size = *(ptrdiff_t*) ptrparam;
 
-		ZVAL_LONG(&args[0], option);
-		ZVAL_NULL(&args[1]);
-		ZVAL_NULL(&args[2]);
+	if (UNEXPECTED(new_size < 0 || new_size > (ptrdiff_t)LONG_MAX)) {
+		/* bad new size */
+		zend_string_release_ex(func_name, false);
+		return PHP_STREAM_OPTION_RETURN_ERR;
+	}
 
-		switch(option) {
-		case PHP_STREAM_OPTION_READ_BUFFER:
-		case PHP_STREAM_OPTION_WRITE_BUFFER:
-			ZVAL_LONG(&args[1], value);
-			if (ptrparam) {
-				ZVAL_LONG(&args[2], *(long *)ptrparam);
-			} else {
-				ZVAL_LONG(&args[2], BUFSIZ);
-			}
-			break;
-		case PHP_STREAM_OPTION_READ_TIMEOUT: {
-			struct timeval tv = *(struct timeval*)ptrparam;
-			ZVAL_LONG(&args[1], tv.tv_sec);
-			ZVAL_LONG(&args[2], tv.tv_usec);
-			break;
-			}
-		case PHP_STREAM_OPTION_BLOCKING:
-			ZVAL_LONG(&args[1], value);
-			break;
-		default:
-			break;
-		}
+	zval retval;
+	zval size;
 
-		call_result = call_method_if_exists(&us->object, &func_name, &retval, 3, args);
+	ZVAL_LONG(&size, (zend_long)new_size);
+	zend_result call_result = zend_call_method_if_exists(Z_OBJ(us->object), func_name, &retval,
1, &size);
+	zend_string_release_ex(func_name, false);
 
-		if (call_result == FAILURE) {
-			php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_SET_OPTION " is not
implemented!",
-					ZSTR_VAL(us->wrapper->ce->name));
-			ret = PHP_STREAM_OPTION_RETURN_ERR;
-		} else if (zend_is_true(&retval)) {
-			ret = PHP_STREAM_OPTION_RETURN_OK;
+	if (UNEXPECTED(call_result == FAILURE)) {
+		php_error_docref(NULL, E_WARNING,
+				"%s::" USERSTREAM_TRUNCATE " is not implemented!",
+				ZSTR_VAL(us->wrapper->ce->name));
+		return PHP_STREAM_OPTION_RETURN_ERR;
+	}
+	if (UNEXPECTED(Z_ISUNDEF(retval))) {
+		return PHP_STREAM_OPTION_RETURN_ERR;
+	}
+	if (EXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
+		return Z_TYPE(retval) == IS_TRUE ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
+	} else {
+		php_error_docref(NULL, E_WARNING,
+			"%s::" USERSTREAM_TRUNCATE " value must be of type bool, %s given",
+				ZSTR_VAL(us->wrapper->ce->name), zend_zval_value_name(&retval));
+		zval_ptr_dtor(&retval);
+		return PHP_STREAM_OPTION_RETURN_ERR;
+	}
+}
+
+static int user_stream_set_option(const php_userstream_data_t *us, int option, int value, void
*ptrparam)
+{
+	zval args[3];
+	ZVAL_LONG(&args[0], option);
+	ZVAL_LONG(&args[1], value);
+	ZVAL_NULL(&args[2]);
+
+	if (option == PHP_STREAM_OPTION_READ_TIMEOUT) {
+		struct timeval tv = *(struct timeval*)ptrparam;
+		ZVAL_LONG(&args[1], tv.tv_sec);
+		ZVAL_LONG(&args[2], tv.tv_usec);
+	} else if (option == PHP_STREAM_OPTION_READ_BUFFER || option == PHP_STREAM_OPTION_WRITE_BUFFER) {
+		if (ptrparam) {
+			ZVAL_LONG(&args[2], *(long *)ptrparam);
 		} else {
-			ret = PHP_STREAM_OPTION_RETURN_ERR;
+			ZVAL_LONG(&args[2], BUFSIZ);
 		}
+	}
 
-		zval_ptr_dtor(&retval);
-		zval_ptr_dtor(&args[2]);
-		zval_ptr_dtor(&args[1]);
-		zval_ptr_dtor(&args[0]);
-		zval_ptr_dtor(&func_name);
+	zval retval;
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_SET_OPTION, false);
+	zend_result call_result = zend_call_method_if_exists(Z_OBJ(us->object), func_name, &retval,
3, args);
+	zend_string_release_ex(func_name, false);
 
-		break;
-		}
+	if (UNEXPECTED(call_result == FAILURE)) {
+		php_error_docref(NULL, E_WARNING,
+				"%s::" USERSTREAM_SET_OPTION " is not implemented!",
+				ZSTR_VAL(us->wrapper->ce->name));
+		return PHP_STREAM_OPTION_RETURN_ERR;
+	}
+	if (UNEXPECTED(Z_ISUNDEF(retval))) {
+		return PHP_STREAM_OPTION_RETURN_ERR;
 	}
 
+	int ret;
+	if (zend_is_true(&retval)) {
+		ret = PHP_STREAM_OPTION_RETURN_OK;
+	} else {
+		ret = PHP_STREAM_OPTION_RETURN_ERR;
+	}
+
+	zval_ptr_dtor(&retval);
 	return ret;
 }
 
+static int php_userstreamop_set_option(php_stream *stream, int option, int value, void *ptrparam) {
+	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
+
+	switch (option) {
+		case PHP_STREAM_OPTION_CHECK_LIVENESS:
+			return user_stream_set_check_liveliness(us);
+
+		case PHP_STREAM_OPTION_LOCKING:
+			return user_stream_set_locking(us, value);
+
+		case PHP_STREAM_OPTION_TRUNCATE_API:
+			return user_stream_set_truncation(us, value, ptrparam);
+
+		case PHP_STREAM_OPTION_READ_BUFFER:
+		case PHP_STREAM_OPTION_WRITE_BUFFER:
+		case PHP_STREAM_OPTION_READ_TIMEOUT:
+		case PHP_STREAM_OPTION_BLOCKING:
+			return user_stream_set_option(us, option, value, ptrparam);
+
+		default:
+			return PHP_STREAM_OPTION_RETURN_NOTIMPL;
+	}
+}
+
 
 static int user_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int options,
php_stream_context *context)
 {
 	struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
-	zval zfuncname, zretval;
+	zval zretval;
 	zval args[1];
 	zval object;
 	int ret = 0;
@@ -1041,22 +1062,21 @@ static int user_wrapper_unlink(php_stream_wrapper *wrapper, const char *url,
int
 	/* call the unlink method */
 	ZVAL_STRING(&args[0], url);
 
-	ZVAL_STRING(&zfuncname, USERSTREAM_UNLINK);
 
-	zend_result call_result = call_method_if_exists(&object, &zfuncname, &zretval, 1,
args);
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_UNLINK, false);
+	zend_result call_result = zend_call_method_if_exists(Z_OBJ(object), func_name, &zretval, 1,
args);
+	zend_string_release_ex(func_name, false);
+	zval_ptr_dtor(&args[0]);
+	zval_ptr_dtor(&object);
 
-	if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE))
{
-		ret = (Z_TYPE(zretval) == IS_TRUE);
-	} else if (call_result == FAILURE) {
+	if (UNEXPECTED(call_result == FAILURE)) {
 		php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_UNLINK " is not
implemented!", ZSTR_VAL(uwrap->ce->name));
+	} else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) {
+		ret = Z_TYPE(zretval) == IS_TRUE;
 	}
+	// TODO: Warn on invalid return type, or use zval_is_true()?
 
-	/* clean up */
-	zval_ptr_dtor(&object);
 	zval_ptr_dtor(&zretval);
-	zval_ptr_dtor(&zfuncname);
-
-	zval_ptr_dtor(&args[0]);
 
 	return ret;
 }
@@ -1065,7 +1085,7 @@ static int user_wrapper_rename(php_stream_wrapper *wrapper, const char
*url_from
 							   int options, php_stream_context *context)
 {
 	struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
-	zval zfuncname, zretval;
+	zval zretval;
 	zval args[2];
 	zval object;
 	int ret = 0;
@@ -1080,24 +1100,22 @@ static int user_wrapper_rename(php_stream_wrapper *wrapper, const char
*url_from
 	ZVAL_STRING(&args[0], url_from);
 	ZVAL_STRING(&args[1], url_to);
 
-	ZVAL_STRING(&zfuncname, USERSTREAM_RENAME);
-
-	zend_result call_result = call_method_if_exists(&object, &zfuncname, &zretval, 2,
args);
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_RENAME, false);
+	zend_result call_result = zend_call_method_if_exists(Z_OBJ(object), func_name, &zretval, 2,
args);
+	zend_string_release_ex(func_name, false);
+	zval_ptr_dtor(&args[1]);
+	zval_ptr_dtor(&args[0]);
+	zval_ptr_dtor(&object);
 
-	if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE))
{
-		ret = (Z_TYPE(zretval) == IS_TRUE);
-	} else if (call_result == FAILURE) {
+	if (UNEXPECTED(call_result == FAILURE)) {
 		php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_RENAME " is not
implemented!", ZSTR_VAL(uwrap->ce->name));
+	} else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) {
+		ret = Z_TYPE(zretval) == IS_TRUE;
 	}
+	// TODO: Warn on invalid return type, or use zval_is_true()?
 
-	/* clean up */
-	zval_ptr_dtor(&object);
 	zval_ptr_dtor(&zretval);
 
-	zval_ptr_dtor(&zfuncname);
-	zval_ptr_dtor(&args[1]);
-	zval_ptr_dtor(&args[0]);
-
 	return ret;
 }
 
@@ -1105,7 +1123,7 @@ static int user_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url,
int
 							  int options, php_stream_context *context)
 {
 	struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
-	zval zfuncname, zretval;
+	zval zretval;
 	zval args[3];
 	zval object;
 	int ret = 0;
@@ -1121,25 +1139,21 @@ static int user_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url,
int
 	ZVAL_LONG(&args[1], mode);
 	ZVAL_LONG(&args[2], options);
 
-	ZVAL_STRING(&zfuncname, USERSTREAM_MKDIR);
-
-	zend_result call_result = call_method_if_exists(&object, &zfuncname, &zretval, 3,
args);
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_MKDIR, false);
+	zend_result call_result = zend_call_method_if_exists(Z_OBJ(object), func_name, &zretval, 3,
args);
+	zend_string_release_ex(func_name, false);
+	zval_ptr_dtor(&args[0]);
+	zval_ptr_dtor(&object);
 
-	if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE))
{
-		ret = (Z_TYPE(zretval) == IS_TRUE);
-	} else if (call_result == FAILURE) {
+	if (UNEXPECTED(call_result == FAILURE)) {
 		php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_MKDIR " is not
implemented!", ZSTR_VAL(uwrap->ce->name));
+	} else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) {
+		ret = Z_TYPE(zretval) == IS_TRUE;
 	}
+	// TODO: Warn on invalid return type, or use zval_is_true()?
 
-	/* clean up */
-	zval_ptr_dtor(&object);
 	zval_ptr_dtor(&zretval);
 
-	zval_ptr_dtor(&zfuncname);
-	zval_ptr_dtor(&args[2]);
-	zval_ptr_dtor(&args[1]);
-	zval_ptr_dtor(&args[0]);
-
 	return ret;
 }
 
@@ -1147,7 +1161,7 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url,
 							  int options, php_stream_context *context)
 {
 	struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
-	zval zfuncname, zretval;
+	zval zretval;
 	zval args[2];
 	zval object;
 	int ret = 0;
@@ -1162,24 +1176,21 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url,
 	ZVAL_STRING(&args[0], url);
 	ZVAL_LONG(&args[1], options);
 
-	ZVAL_STRING(&zfuncname, USERSTREAM_RMDIR);
-
-	zend_result call_result = call_method_if_exists(&object, &zfuncname, &zretval, 2,
args);
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_RMDIR, false);
+	zend_result call_result = zend_call_method_if_exists(Z_OBJ(object), func_name, &zretval, 2,
args);
+	zend_string_release_ex(func_name, false);
+	zval_ptr_dtor(&args[0]);
+	zval_ptr_dtor(&object);
 
-	if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE))
{
-		ret = (Z_TYPE(zretval) == IS_TRUE);
-	} else if (call_result == FAILURE) {
+	if (UNEXPECTED(call_result == FAILURE)) {
 		php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_RMDIR " is not
implemented!", ZSTR_VAL(uwrap->ce->name));
+	} else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) {
+		ret = Z_TYPE(zretval) == IS_TRUE;
 	}
+	// TODO: Warn on invalid return type, or use zval_is_true()?
 
-	/* clean up */
-	zval_ptr_dtor(&object);
 	zval_ptr_dtor(&zretval);
 
-	zval_ptr_dtor(&zfuncname);
-	zval_ptr_dtor(&args[1]);
-	zval_ptr_dtor(&args[0]);
-
 	return ret;
 }
 
@@ -1187,7 +1198,7 @@ static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url,
i
 								 void *value, php_stream_context *context)
 {
 	struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
-	zval zfuncname, zretval;
+	zval zretval;
 	zval args[3];
 	zval object;
 	int ret = 0;
@@ -1227,25 +1238,22 @@ static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char
*url, i
 	ZVAL_STRING(&args[0], url);
 	ZVAL_LONG(&args[1], option);
 
-	ZVAL_STRING(&zfuncname, USERSTREAM_METADATA);
-
-	zend_result call_result = call_method_if_exists(&object, &zfuncname, &zretval, 3,
args);
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_METADATA, false);
+	zend_result call_result = zend_call_method_if_exists(Z_OBJ(object), func_name, &zretval, 3,
args);
+	zend_string_release_ex(func_name, false);
+	zval_ptr_dtor(&args[2]);
+	zval_ptr_dtor(&args[0]);
+	zval_ptr_dtor(&object);
 
-	if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE))
{
-		ret = Z_TYPE(zretval) == IS_TRUE;
-	} else if (call_result == FAILURE) {
+	if (UNEXPECTED(call_result == FAILURE)) {
 		php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_METADATA " is not
implemented!", ZSTR_VAL(uwrap->ce->name));
+	} else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) {
+		ret = Z_TYPE(zretval) == IS_TRUE;
 	}
+	// TODO: Warn on invalid return type, or use zval_is_true()?
 
-	/* clean up */
-	zval_ptr_dtor(&object);
 	zval_ptr_dtor(&zretval);
 
-	zval_ptr_dtor(&zfuncname);
-	zval_ptr_dtor(&args[0]);
-	zval_ptr_dtor(&args[1]);
-	zval_ptr_dtor(&args[2]);
-
 	return ret;
 }
 
@@ -1254,7 +1262,7 @@ static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char *url,
i
 								 php_stream_statbuf *ssb, php_stream_context *context)
 {
 	struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
-	zval zfuncname, zretval;
+	zval zretval;
 	zval args[2];
 	zval object;
 	int ret = -1;
@@ -1262,90 +1270,97 @@ static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char
*url, i
 	/* create an instance of our class */
 	user_stream_create_object(uwrap, context, &object);
 	if (Z_TYPE(object) == IS_UNDEF) {
-		return ret;
+		return -1;
 	}
 
 	/* call it's stat_url method - set up params first */
 	ZVAL_STRING(&args[0], url);
 	ZVAL_LONG(&args[1], flags);
 
-	ZVAL_STRING(&zfuncname, USERSTREAM_STATURL);
-
-	zend_result call_result = call_method_if_exists(&object, &zfuncname, &zretval, 2,
args);
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_STATURL, false);
+	zend_result call_result = zend_call_method_if_exists(Z_OBJ(object), func_name, &zretval, 2,
args);
+	zend_string_release_ex(func_name, false);
+	zval_ptr_dtor(&args[0]);
+	zval_ptr_dtor(&object);
 
-	if (call_result == SUCCESS && Z_TYPE(zretval) == IS_ARRAY) {
-		/* We got the info we needed */
+	if (UNEXPECTED(call_result == FAILURE)) {
+		php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STATURL " is not
implemented!",
+			ZSTR_VAL(uwrap->ce->name));
+		return -1;
+	}
+	if (UNEXPECTED(Z_ISUNDEF(zretval))) {
+		return -1;
+	}
+	if (EXPECTED(Z_TYPE(zretval) == IS_ARRAY)) {
 		statbuf_from_array(Z_ARR(zretval), ssb);
 		ret = 0;
-	} else {
-		if (call_result == FAILURE) {
-			php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STATURL " is not
implemented!",
-					ZSTR_VAL(uwrap->ce->name));
-		}
 	}
+	// TODO: Warning on incorrect return type?
 
-	/* clean up */
-	zval_ptr_dtor(&object);
 	zval_ptr_dtor(&zretval);
 
-	zval_ptr_dtor(&zfuncname);
-	zval_ptr_dtor(&args[1]);
-	zval_ptr_dtor(&args[0]);
-
 	return ret;
 
 }
 
 static ssize_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t count)
 {
-	zval func_name;
 	zval retval;
 	size_t didread = 0;
 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
 	php_stream_dirent *ent = (php_stream_dirent*)buf;
 
 	/* avoid problems if someone mis-uses the stream */
-	if (count != sizeof(php_stream_dirent))
+	if (count != sizeof(php_stream_dirent)) {
 		return -1;
+	}
 
-	ZVAL_STRINGL(&func_name, USERSTREAM_DIR_READ, sizeof(USERSTREAM_DIR_READ)-1);
-
-	zend_result call_result = call_method_if_exists(&us->object, &func_name, &retval,
0, NULL);
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_DIR_READ, false);
+	zend_result call_result = zend_call_method_if_exists(Z_OBJ(us->object), func_name, &retval,
0, NULL);
+	zend_string_release_ex(func_name, false);
 
-	if (call_result == SUCCESS && Z_TYPE(retval) != IS_FALSE && Z_TYPE(retval) !=
IS_TRUE) {
+	if (UNEXPECTED(call_result == FAILURE)) {
+		php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_DIR_READ " is not
implemented!",
+				ZSTR_VAL(us->wrapper->ce->name));
+		return -1;
+	}
+	if (UNEXPECTED(Z_ISUNDEF(retval))) {
+		return -1;
+	}
+	// TODO: Warn/TypeError for invalid returns?
+	if (Z_TYPE(retval) != IS_FALSE && Z_TYPE(retval) != IS_TRUE) {
+		zend_string *str = zval_try_get_string(&retval);
+		if (UNEXPECTED(str == NULL)) {
+			zval_ptr_dtor(&retval);
+			return -1;
+		}
 		convert_to_string(&retval);
-		PHP_STRLCPY(ent->d_name, Z_STRVAL(retval), sizeof(ent->d_name), Z_STRLEN(retval));
+		PHP_STRLCPY(ent->d_name, ZSTR_VAL(str), sizeof(ent->d_name), ZSTR_LEN(str));
+		zend_string_release(str);
 		ent->d_type = DT_UNKNOWN;
 
 		didread = sizeof(php_stream_dirent);
-	} else if (call_result == FAILURE) {
-		php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_DIR_READ " is not
implemented!",
-				ZSTR_VAL(us->wrapper->ce->name));
 	}
 
 	zval_ptr_dtor(&retval);
-	zval_ptr_dtor(&func_name);
 
 	return didread;
 }
 
 static int php_userstreamop_closedir(php_stream *stream, int close_handle)
 {
-	zval func_name;
 	zval retval;
 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
 
 	assert(us != NULL);
 
-	ZVAL_STRINGL(&func_name, USERSTREAM_DIR_CLOSE, sizeof(USERSTREAM_DIR_CLOSE)-1);
-
-	call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_DIR_CLOSE, false);
+	zend_call_method_if_exists(Z_OBJ(us->object), func_name, &retval, 0, NULL);
+	zend_string_release_ex(func_name, false);
 
 	zval_ptr_dtor(&retval);
-	zval_ptr_dtor(&func_name);
 	zval_ptr_dtor(&us->object);
 	ZVAL_UNDEF(&us->object);
-
 	efree(us);
 
 	return 0;
@@ -1353,16 +1368,14 @@ static int php_userstreamop_closedir(php_stream *stream, int close_handle)
 
 static int php_userstreamop_rewinddir(php_stream *stream, zend_off_t offset, int whence, zend_off_t
*newoffs)
 {
-	zval func_name;
 	zval retval;
 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
 
-	ZVAL_STRINGL(&func_name, USERSTREAM_DIR_REWIND, sizeof(USERSTREAM_DIR_REWIND)-1);
-
-	call_method_if_exists(&us->object, &func_name, &retval, 0, NULL);
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_DIR_REWIND, false);
+	zend_call_method_if_exists(Z_OBJ(us->object), func_name, &retval, 0, NULL);
+	zend_string_release_ex(func_name, false);
 
 	zval_ptr_dtor(&retval);
-	zval_ptr_dtor(&func_name);
 
 	return 0;
 
@@ -1371,7 +1384,6 @@ static int php_userstreamop_rewinddir(php_stream *stream, zend_off_t offset,
int
 static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr)
 {
 	php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
-	zval func_name;
 	zval retval;
 	zval args[1];
 	php_stream * intstream = NULL;
@@ -1379,8 +1391,6 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void
**retptr)
 	/* If we are checking if the stream can cast, no return pointer is provided, so do not emit errors
*/
 	bool report_errors = retptr;
 
-	ZVAL_STRINGL(&func_name, USERSTREAM_CAST, sizeof(USERSTREAM_CAST)-1);
-
 	switch(castas) {
 	case PHP_STREAM_AS_FD_FOR_SELECT:
 		ZVAL_LONG(&args[0], PHP_STREAM_AS_FD_FOR_SELECT);
@@ -1390,19 +1400,23 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void
**retptr)
 		break;
 	}
 
-	zend_result call_result = call_method_if_exists(&us->object, &func_name, &retval,
1, args);
+	zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_CAST, false);
+	zend_result call_result = zend_call_method_if_exists(Z_OBJ(us->object), func_name, &retval,
1, args);
+	zend_string_release_ex(func_name, false);
 
-	do {
-		if (call_result == FAILURE) {
-			if (report_errors) {
-				php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " is not
implemented!",
-						ZSTR_VAL(us->wrapper->ce->name));
-			}
-			break;
+	if (UNEXPECTED(call_result == FAILURE)) {
+		if (report_errors) {
+			php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " is not
implemented!",
+					ZSTR_VAL(us->wrapper->ce->name));
 		}
+		return FAILURE;
+	}
+
+	do {
 		if (!zend_is_true(&retval)) {
 			break;
 		}
+		// TODO: Can this emit an exception even with no error reporting?
 		php_stream_from_zval_no_verify(intstream, &retval);
 		if (!intstream) {
 			if (report_errors) {
@@ -1423,8 +1437,6 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void
**retptr)
 	} while (0);
 
 	zval_ptr_dtor(&retval);
-	zval_ptr_dtor(&func_name);
-	zval_ptr_dtor(&args[0]);
 
 	return ret;
 }


Thread (1 message)

  • Gina Peter Banyard
« previous php.cvs (#135293) next »