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;
}