From 972d7c711bc4cdc009e018ac50a5cfc70fda7fec Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 30 Sep 2025 20:29:14 +0200 Subject: [PATCH 01/37] stream: add initial stream errors API --- configure.ac | 1 + ext/standard/basic_functions.c | 1 + ext/standard/file.h | 4 +- main/php_streams.h | 3 + main/streams/php_stream_errors.h | 167 ++++++++ main/streams/stream_errors.c | 553 +++++++++++++++++++++++++++ main/streams/stream_errors.stub.php | 16 + main/streams/stream_errors_arginfo.h | 39 ++ main/streams/streams.c | 149 +------- win32/build/config.w32 | 2 +- 10 files changed, 794 insertions(+), 141 deletions(-) create mode 100644 main/streams/php_stream_errors.h create mode 100644 main/streams/stream_errors.c create mode 100644 main/streams/stream_errors.stub.php create mode 100644 main/streams/stream_errors_arginfo.h diff --git a/configure.ac b/configure.ac index 77fc8c89cdf40..a515c34f2394b 100644 --- a/configure.ac +++ b/configure.ac @@ -1695,6 +1695,7 @@ PHP_ADD_SOURCES([main/streams], m4_normalize([ memory.c mmap.c plain_wrapper.c + stream_errors.c streams.c transports.c userspace.c diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index e0c4230dae27c..20781ddddd433 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -336,6 +336,7 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */ #endif BASIC_MINIT_SUBMODULE(exec) + BASIC_MINIT_SUBMODULE(stream_errors) BASIC_MINIT_SUBMODULE(user_streams) php_register_url_stream_wrapper("php", &php_stream_php_wrapper); diff --git a/ext/standard/file.h b/ext/standard/file.h index f8faebd028293..a3c84791cc1ee 100644 --- a/ext/standard/file.h +++ b/ext/standard/file.h @@ -34,6 +34,7 @@ PHPAPI PHP_FUNCTION(ftell); PHPAPI PHP_FUNCTION(fseek); PHPAPI PHP_FUNCTION(fpassthru); +PHP_MINIT_FUNCTION(stream_errors); PHP_MINIT_FUNCTION(user_streams); PHPAPI zend_result php_copy_file(const char *src, const char *dest); @@ -100,7 +101,8 @@ typedef struct { php_stream_context *default_context; HashTable *stream_wrappers; /* per-request copy of url_stream_wrappers_hash */ HashTable *stream_filters; /* per-request copy of stream_filters_hash */ - HashTable *wrapper_errors; /* key: wrapper address; value: linked list of char* */ + HashTable *wrapper_logged_errors; /* key: wrapper address; value: linked list of error entries */ + HashTable *wrapper_stored_errors; /* key: wrapper address; value: linked list of error entries */ int pclose_wait; #ifdef HAVE_GETHOSTBYNAME_R struct hostent tmp_host_info; diff --git a/main/php_streams.h b/main/php_streams.h index 1c52539cfcaee..33af6c3c91c30 100644 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -248,6 +248,8 @@ struct _php_stream { #endif struct _php_stream *enclosing_stream; /* this is a private stream owned by enclosing_stream */ + + zend_llist *error_list; }; /* php_stream */ #define PHP_STREAM_CONTEXT(stream) \ @@ -539,6 +541,7 @@ PHPAPI ssize_t _php_stream_passthru(php_stream * src STREAMS_DC); #define php_stream_passthru(stream) _php_stream_passthru((stream) STREAMS_CC) END_EXTERN_C() +#include "streams/php_stream_errors.h" #include "streams/php_stream_transport.h" #include "streams/php_stream_plain_wrapper.h" #include "streams/php_stream_glob_wrapper.h" diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h new file mode 100644 index 0000000000000..08998a87111cf --- /dev/null +++ b/main/streams/php_stream_errors.h @@ -0,0 +1,167 @@ +#ifndef PHP_STREAM_ERRORS_H +#define PHP_STREAM_ERRORS_H + +#include "php.h" +#include "php_streams.h" + +BEGIN_EXTERN_C() + +/* Error mode context options */ +#define PHP_STREAM_ERROR_MODE_ERROR 0 +#define PHP_STREAM_ERROR_MODE_EXCEPTION 1 +#define PHP_STREAM_ERROR_MODE_SILENT 2 + +/* Error store context options */ +#define PHP_STREAM_ERROR_STORE_AUTO 0 +#define PHP_STREAM_ERROR_STORE_NONE 1 +#define PHP_STREAM_ERROR_STORE_NON_TERM 2 +#define PHP_STREAM_ERROR_STORE_TERMINAL 3 +#define PHP_STREAM_ERROR_STORE_ALL 4 + +/* Error code definition for registration */ +typedef struct { + int code; + const char *name; +} php_stream_error_code_def; + +/* Stored error entry */ +typedef struct { + zend_string *message; + int code; + const char *wrapper_name; /* Points to wrapper->wops->label, no need to duplicate */ + const char *param; /* Points to passed string, caller manages lifetime for storage */ + int severity; + bool terminal; +} php_stream_error_entry; + +/* Sentinel for error code array termination */ +#define PHP_STREAM_ERROR_CODE_END {0, NULL} + +/* Error code registration */ +PHPAPI void php_stream_wrapper_register_error_codes( + php_stream_wrapper *wrapper, + const php_stream_error_code_def *codes +); + +PHPAPI const char *php_stream_wrapper_get_error_name( + php_stream_wrapper *wrapper, + int code +); + +/* Main error reporting functions */ +PHPAPI void php_stream_wrapper_error( + php_stream_wrapper *wrapper, + php_stream_context *context, + int options, + int severity, + bool terminal, + int code, + const char *fmt, + ... +) ZEND_ATTRIBUTE_FORMAT(printf, 7, 8); + +PHPAPI void php_stream_wrapper_error_param( + php_stream_wrapper *wrapper, + php_stream_context *context, + int options, + int severity, + bool terminal, + int code, + const char *param, + const char *fmt, + ... +) ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); + +PHPAPI void php_stream_wrapper_error_param2( + php_stream_wrapper *wrapper, + php_stream_context *context, + int options, + int severity, + bool terminal, + int code, + const char *param1, + const char *param2, + const char *fmt, + ... +) ZEND_ATTRIBUTE_FORMAT(printf, 9, 10); + +PHPAPI void php_stream_error( + php_stream *stream, + int severity, + bool terminal, + int code, + const char *fmt, + ... +) ZEND_ATTRIBUTE_FORMAT(printf, 5, 6); + +/* Legacy wrapper error log - updated API */ +PHPAPI void php_stream_wrapper_log_error( + const php_stream_wrapper *wrapper, + int options, + int severity, + bool terminal, + int code, + const char *fmt, + ... +) ZEND_ATTRIBUTE_FORMAT(printf, 6, 7); + +PHPAPI void php_stream_wrapper_log_error_param( + const php_stream_wrapper *wrapper, + int options, + int severity, + bool terminal, + int code, + const char *param, + const char *fmt, + ... +) ZEND_ATTRIBUTE_FORMAT(printf, 7, 8); + +PHPAPI void php_stream_display_wrapper_errors( + php_stream_wrapper *wrapper, + const char *path, + const char *caption +); + +PHPAPI void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper); + +void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption); +void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper); + +/* Convenience macros */ +#define php_stream_wrapper_warn(wrapper, context, options, code, ...) \ + php_stream_wrapper_error(wrapper, context, options, E_WARNING, true, code, __VA_ARGS__) + +#define php_stream_wrapper_warn_nt(wrapper, context, options, code, ...) \ + php_stream_wrapper_error(wrapper, context, options, E_WARNING, false, code, __VA_ARGS__) + +#define php_stream_wrapper_notice(wrapper, context, options, code, ...) \ + php_stream_wrapper_error(wrapper, context, options, E_NOTICE, false, code, __VA_ARGS__) + +#define php_stream_wrapper_warn_param(wrapper, context, options, code, param, ...) \ + php_stream_wrapper_error_param(wrapper, context, options, E_WARNING, true, code, param, __VA_ARGS__) + +#define php_stream_warn(stream, code, ...) \ + php_stream_error(stream, E_WARNING, true, code, __VA_ARGS__) + +#define php_stream_warn_nt(stream, code, ...) \ + php_stream_error(stream, E_WARNING, false, code, __VA_ARGS__) + +#define php_stream_notice(stream, code, ...) \ + php_stream_error(stream, E_NOTICE, false, code, __VA_ARGS__) + +#define php_stream_fatal(stream, code, ...) \ + php_stream_error(stream, E_ERROR, true, code, __VA_ARGS__) + +/* Legacy log variants */ +#define php_stream_wrapper_log_warn(wrapper, options, code, ...) \ + php_stream_wrapper_log_error(wrapper, options, E_WARNING, true, code, __VA_ARGS__) + +#define php_stream_wrapper_log_warn_nt(wrapper, options, code, ...) \ + php_stream_wrapper_log_error(wrapper, options, E_WARNING, false, code, __VA_ARGS__) + +#define php_stream_wrapper_log_notice(wrapper, options, code, ...) \ + php_stream_wrapper_log_error(wrapper, options, E_NOTICE, false, code, __VA_ARGS__) + +END_EXTERN_C() + +#endif /* PHP_STREAM_ERRORS_H */ diff --git a/main/streams/stream_errors.c b/main/streams/stream_errors.c new file mode 100644 index 0000000000000..b308521ca33c3 --- /dev/null +++ b/main/streams/stream_errors.c @@ -0,0 +1,553 @@ +#include "php.h" +#include "php_globals.h" +#include "php_streams.h" +#include "php_stream_errors.h" +#include "stream_errors_arginfo.h" +#include "zend_exceptions.h" +#include "ext/standard/file.h" + +/* StreamException class entry */ +static zend_class_entry *php_ce_stream_exception; + +/* Error code registry */ +static HashTable *php_stream_wrapper_error_codes = NULL; + +static void php_stream_error_entry_dtor(void *error) +{ + php_stream_error_entry *entry = *(php_stream_error_entry **) error; + zend_string_release(entry->message); + efree(entry); +} + +static void php_stream_error_list_dtor(zval *item) +{ + zend_llist *list = (zend_llist *) Z_PTR_P(item); + zend_llist_destroy(list); + efree(list); +} + +/* Error code registration */ + +PHPAPI void php_stream_wrapper_register_error_codes( + php_stream_wrapper *wrapper, const php_stream_error_code_def *codes) +{ + if (!php_stream_wrapper_error_codes) { + php_stream_wrapper_error_codes = zend_new_array(8); + } + + zval code_table_zv, *code_table; + code_table = zend_hash_index_find(php_stream_wrapper_error_codes, (zend_ulong) wrapper); + + if (!code_table) { + code_table = &code_table_zv; + ZVAL_ARR(code_table, zend_new_array(8)); + zend_hash_index_update(php_stream_wrapper_error_codes, (zend_ulong) wrapper, code_table); + } + + for (const php_stream_error_code_def *def = codes; def->name != NULL; def++) { + zend_string *name = zend_string_init(def->name, strlen(def->name), 1); + zval zv; + ZVAL_STR(&zv, name); + zend_hash_index_update(code_table, def->code, &zv); + } +} + +PHPAPI const char *php_stream_wrapper_get_error_name(php_stream_wrapper *wrapper, int code) +{ + if (!php_stream_wrapper_error_codes) { + return NULL; + } + + zval *code_table = zend_hash_index_find(php_stream_wrapper_error_codes, (zend_ulong) wrapper); + + if (!code_table) { + return NULL; + } + + zval *error_name = zend_hash_index_find(Z_ARR_P(code_table), code); + return error_name ? Z_STRVAL_P(error_name) : NULL; +} + +/* Context option helpers */ + +static int php_stream_auto_decide_error_store_mode(int error_mode) +{ + switch (error_mode) { + case PHP_STREAM_ERROR_MODE_ERROR: + return PHP_STREAM_ERROR_STORE_NONE; + case PHP_STREAM_ERROR_MODE_EXCEPTION: + return PHP_STREAM_ERROR_STORE_NON_TERM; + case PHP_STREAM_ERROR_MODE_SILENT: + return PHP_STREAM_ERROR_STORE_ALL; + default: + return PHP_STREAM_ERROR_STORE_NONE; + } +} + +static int php_stream_get_error_mode(php_stream_context *context) +{ + if (!context) { + return PHP_STREAM_ERROR_MODE_ERROR; + } + + zval *option = php_stream_context_get_option(context, "stream", "error_mode"); + if (option && Z_TYPE_P(option) == IS_LONG) { + return Z_LVAL_P(option); + } + + return PHP_STREAM_ERROR_MODE_ERROR; +} + +static int php_stream_get_error_store_mode(php_stream_context *context, int error_mode) +{ + if (!context) { + return php_stream_auto_decide_error_store_mode(error_mode); + } + + zval *option = php_stream_context_get_option(context, "stream", "error_store"); + if (option && Z_TYPE_P(option) == IS_LONG) { + int store_mode = Z_LVAL_P(option); + + if (store_mode == PHP_STREAM_ERROR_STORE_AUTO) { + return php_stream_auto_decide_error_store_mode(error_mode); + } + + return store_mode; + } + + return php_stream_auto_decide_error_store_mode(error_mode); +} + +static bool php_stream_should_store_error(int store_mode, bool terminal) +{ + switch (store_mode) { + case PHP_STREAM_ERROR_STORE_NONE: + return false; + case PHP_STREAM_ERROR_STORE_NON_TERM: + return !terminal; + case PHP_STREAM_ERROR_STORE_TERMINAL: + return terminal; + case PHP_STREAM_ERROR_STORE_ALL: + return true; + default: + return false; + } +} + +/* StreamException methods */ + +static void php_stream_throw_exception( + const char *message, int code, const char *wrapper_name, const char *param) +{ + zval ex; + + object_init_ex(&ex, php_ce_stream_exception); + + zend_update_property_string(php_ce_stream_exception, Z_OBJ(ex), ZEND_STRL("message"), message); + zend_update_property_long(php_ce_stream_exception, Z_OBJ(ex), ZEND_STRL("code"), code); + zend_update_property_string( + php_ce_stream_exception, Z_OBJ(ex), ZEND_STRL("wrapperName"), wrapper_name); + + if (param) { + zend_update_property_string(php_ce_stream_exception, Z_OBJ(ex), ZEND_STRL("param"), param); + } + + zend_throw_exception_object(&ex); +} + +/* Core error processing */ + +static void php_stream_process_error(php_stream_context *context, const char *wrapper_name, + php_stream *stream, int code, const char *message, const char *param, int severity, + bool terminal) +{ + int error_mode = php_stream_get_error_mode(context); + + /* Handle error based on error_mode */ + switch (error_mode) { + case PHP_STREAM_ERROR_MODE_ERROR: + if (param) { + php_error_docref1(NULL, param, severity, "%s", message); + } else { + php_error_docref(NULL, severity, "%s", message); + } + break; + + case PHP_STREAM_ERROR_MODE_EXCEPTION: + if (terminal) { + php_stream_throw_exception(message, code, wrapper_name, param); + } + break; + + case PHP_STREAM_ERROR_MODE_SILENT: + break; + } + + /* Call user error handler if set */ + if (context) { + zval *handler = php_stream_context_get_option(context, "stream", "error_handler"); + if (handler && Z_TYPE_P(handler) == IS_CALLABLE) { + zval retval; + zval args[5]; + + /* Arg 0: wrapper name */ + ZVAL_STRING(&args[0], wrapper_name); + + /* Arg 1: stream resource or null */ + if (stream && stream->res) { + ZVAL_RES(&args[1], stream->res); + GC_ADDREF(stream->res); + } else { + ZVAL_NULL(&args[1]); + } + + /* Arg 2: error code */ + ZVAL_LONG(&args[2], code); + + /* Arg 3: message */ + ZVAL_STRING(&args[3], message); + + /* Arg 4: param (or null) */ + if (param) { + ZVAL_STRING(&args[4], param); + } else { + ZVAL_NULL(&args[4]); + } + + call_user_function(NULL, NULL, handler, &retval, 5, args); + + zval_ptr_dtor(&retval); + zval_ptr_dtor(&args[0]); + zval_ptr_dtor(&args[1]); + zval_ptr_dtor(&args[3]); + zval_ptr_dtor(&args[4]); + } + } +} + +/* Helper to create error entry */ +static php_stream_error_entry *php_stream_create_error_entry(zend_string *message, int code, + const char *wrapper_name, const char *param, int severity, bool terminal) +{ + php_stream_error_entry *entry = emalloc(sizeof(php_stream_error_entry)); + entry->message = message; /* Takes ownership */ + entry->code = code; + entry->wrapper_name = wrapper_name; + entry->param = param; + entry->severity = severity; + entry->terminal = terminal; + return entry; +} + +/* Common storage function*/ +static void php_stream_store_error_common(php_stream_context *context, php_stream *stream, + php_stream_wrapper *wrapper, zend_string *message, int code, const char *wrapper_name, + const char *param, int severity, bool terminal) +{ + int error_mode = php_stream_get_error_mode(context); + int store_mode = php_stream_get_error_store_mode(context, error_mode); + + if (!php_stream_should_store_error(store_mode, terminal)) { + return; + } + + php_stream_error_entry *entry + = php_stream_create_error_entry(message, code, wrapper_name, param, severity, terminal); + zend_string_addref(message); /* Storage keeps a reference */ + + zend_llist *list; + + if (stream) { + /* Store in stream's error list */ + if (!stream->error_list) { + stream->error_list = emalloc(sizeof(zend_llist)); + zend_llist_init(stream->error_list, sizeof(php_stream_error_entry *), + php_stream_error_entry_dtor, 0); + } + list = stream->error_list; + } else { + /* Store in FG(stream_errors) for wrapper errors */ + if (!FG(wrapper_stored_errors)) { + ALLOC_HASHTABLE(FG(wrapper_stored_errors)); + zend_hash_init(FG(wrapper_stored_errors), 8, NULL, php_stream_error_list_dtor, 0); + list = NULL; + } else { + list = zend_hash_str_find_ptr( + FG(wrapper_stored_errors), (const char *) &wrapper, sizeof(wrapper)); + } + + if (!list) { + zend_llist new_list; + zend_llist_init( + &new_list, sizeof(php_stream_error_entry *), php_stream_error_entry_dtor, 0); + list = zend_hash_str_update_mem(FG(wrapper_stored_errors), (const char *) &wrapper, + sizeof(wrapper), &new_list, sizeof(new_list)); + } + } + + zend_llist_add_element(list, &entry); +} + +/* Wrapper error reporting - stores in FG(wrapper_stored_errors) */ + +static void php_stream_wrapper_error_internal(php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminal, int code, + const char *param, const char *fmt, va_list args) +{ + zend_string *message = vstrpprintf(0, fmt, args); + const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown"; + + if (options & REPORT_ERRORS) { + php_stream_process_error( + context, wrapper_name, NULL, code, ZSTR_VAL(message), param, severity, terminal); + } + + php_stream_store_error_common( + context, NULL, wrapper, message, code, wrapper_name, param, severity, terminal); + + zend_string_release(message); +} + +PHPAPI void php_stream_wrapper_error(php_stream_wrapper *wrapper, php_stream_context *context, + int options, int severity, bool terminal, int code, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + php_stream_wrapper_error_internal( + wrapper, context, options, severity, terminal, code, NULL, fmt, args); + va_end(args); +} + +PHPAPI void php_stream_wrapper_error_param(php_stream_wrapper *wrapper, php_stream_context *context, + int options, int severity, bool terminal, int code, const char *param, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + php_stream_wrapper_error_internal( + wrapper, context, options, severity, terminal, code, param, fmt, args); + va_end(args); +} + +PHPAPI void php_stream_wrapper_error_param2(php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminal, int code, + const char *param1, const char *param2, const char *fmt, ...) +{ + char *combined_param; + spprintf(&combined_param, 0, "%s,%s", param1, param2); + + va_list args; + va_start(args, fmt); + php_stream_wrapper_error_internal( + wrapper, context, options, severity, terminal, code, combined_param, fmt, args); + va_end(args); + + efree(combined_param); +} + +/* Wrapper error logging - stores in FG(wrapper_logged_errors) */ + +static void php_stream_wrapper_log_store_error(const php_stream_wrapper *wrapper, + zend_string *message, int code, const char *wrapper_name, const char *param, int severity, + bool terminal) +{ + php_stream_error_entry *entry + = php_stream_create_error_entry(message, code, wrapper_name, param, severity, terminal); + zend_string_addref(message); + + if (!FG(wrapper_logged_errors)) { + ALLOC_HASHTABLE(FG(wrapper_logged_errors)); + zend_hash_init(FG(wrapper_logged_errors), 8, NULL, php_stream_error_list_dtor, 0); + } + + zend_llist *list = zend_hash_str_find_ptr( + FG(wrapper_logged_errors), (const char *) &wrapper, sizeof(wrapper)); + + if (!list) { + zend_llist new_list; + zend_llist_init( + &new_list, sizeof(php_stream_error_entry *), php_stream_error_entry_dtor, 0); + list = zend_hash_str_update_mem(FG(wrapper_logged_errors), (const char *) &wrapper, + sizeof(wrapper), &new_list, sizeof(new_list)); + } + + zend_llist_add_element(list, &entry); +} + +static void php_stream_wrapper_log_error_internal(const php_stream_wrapper *wrapper, int options, + int severity, bool terminal, int code, const char *param, va_list args) +{ + zend_string *message = vstrpprintf(0, args, args); + const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown"; + + if (options & REPORT_ERRORS) { + /* Report immediately using standard error functions */ + if (param) { + php_error_docref1(NULL, param, severity, "%s", ZSTR_VAL(message)); + } else { + php_error_docref(NULL, severity, "%s", ZSTR_VAL(message)); + } + zend_string_release(message); + } else { + /* Store for later display in FG(wrapper_logged_errors) */ + php_stream_wrapper_log_store_error( + wrapper, message, code, wrapper_name, param, severity, terminal); + zend_string_release(message); + } +} + +PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, int options, + int severity, bool terminal, int code, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + php_stream_wrapper_log_error_internal(wrapper, options, severity, terminal, code, NULL, args); + va_end(args); +} + +PHPAPI void php_stream_wrapper_log_error_param(const php_stream_wrapper *wrapper, int options, + int severity, bool terminal, int code, const char *param, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + php_stream_wrapper_log_error_internal(wrapper, options, severity, terminal, code, param, args); + va_end(args); +} + +static zend_llist *php_stream_get_wrapper_errors_list(php_stream_wrapper *wrapper) +{ + if (!FG(wrapper_logged_errors)) { + return NULL; + } else { + return (zend_llist*) zend_hash_str_find_ptr(FG(wrapper_logged_errors), (const char*)&wrapper, sizeof(wrapper)); + } +} + +/* {{{ wrapper error reporting */ +static void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption) +{ + char *tmp; + char *msg; + char errstr[256]; + int free_msg = 0; + + if (EG(exception)) { + /* Don't emit additional warnings if an exception has already been thrown. */ + return; + } + + tmp = estrdup(path); + if (wrapper) { + zend_llist *err_list = php_stream_get_wrapper_errors_list(wrapper); + if (err_list) { + size_t l = 0; + int brlen; + int i; + int count = (int) zend_llist_count(err_list); + const char *br; + php_stream_error_entry **err_entry_p; + zend_llist_position pos; + + if (PG(html_errors)) { + brlen = 7; + br = "
\n"; + } else { + brlen = 1; + br = "\n"; + } + + for (err_entry_p = zend_llist_get_first_ex(err_list, &pos), i = 0; err_entry_p; + err_entry_p = zend_llist_get_next_ex(err_list, &pos), i++) { + strcat(msg, ZSTR_VAL((*err_entry_p)->message)); + if (i < count - 1) { + l += brlen; + } + } + msg = emalloc(l + 1); + msg[0] = '\0'; + for (err_entry_p = zend_llist_get_first_ex(err_list, &pos), i = 0; err_entry_p; + err_entry_p = zend_llist_get_next_ex(err_list, &pos), i++) { + strcat(msg, ZSTR_VAL((*err_entry_p)->message)); + if (i < count - 1) { + strcat(msg, br); + } + } + + free_msg = 1; + } else { + if (wrapper == &php_plain_files_wrapper) { + msg = php_socket_strerror_s(errno, errstr, sizeof(errstr)); + } else { + msg = "operation failed"; + } + } + } else { + msg = "no suitable wrapper could be found"; + } + + php_strip_url_passwd(tmp); + php_error_docref1(NULL, tmp, E_WARNING, "%s: %s", caption, msg); + efree(tmp); + if (free_msg) { + efree(msg); + } +} + +void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper) +{ + if (wrapper && FG(wrapper_logged_errors)) { + zend_hash_str_del(FG(wrapper_logged_errors), (const char *) &wrapper, sizeof(wrapper)); + } +} + +/* Stream error reporting */ + +PHPAPI void php_stream_error( + php_stream *stream, int severity, bool terminal, int code, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + zend_string *message = vstrpprintf(0, fmt, args); + va_end(args); + + php_stream_wrapper *wrapper = stream->wrapper; + const char *wrapper_name = wrapper ? wrapper->wops->label : "stream"; + + php_stream_context *context = PHP_STREAM_CONTEXT(stream); + + /* Process the error - always report for stream errors */ + php_stream_process_error( + context, wrapper_name, stream, code, ZSTR_VAL(message), NULL, severity, terminal); + + /* Store error */ + php_stream_store_error_common( + context, stream, wrapper, message, code, wrapper_name, NULL, severity, terminal); + + zend_string_release(message); +} + +/* StreamException class registration */ + +PHP_MINIT_FUNCTION(stream_errors) +{ + php_ce_stream_exception = register_class_StreamException(zend_ce_exception); + + return SUCCESS; +} + +PHP_METHOD(StreamException, getParam) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zval *param = zend_read_property( + php_ce_stream_exception, Z_OBJ_P(getThis()), ZEND_STRL("param"), 1, NULL); + RETURN_COPY(param); +} + +PHP_METHOD(StreamException, getWrapperName) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zval *wrapper_name = zend_read_property( + php_ce_stream_exception, Z_OBJ_P(getThis()), ZEND_STRL("wrapperName"), 1, NULL); + RETURN_COPY(wrapper_name); +} diff --git a/main/streams/stream_errors.stub.php b/main/streams/stream_errors.stub.php new file mode 100644 index 0000000000000..387c2c692703f --- /dev/null +++ b/main/streams/stream_errors.stub.php @@ -0,0 +1,16 @@ +\n"; - } else { - brlen = 1; - br = "\n"; - } - - for (err_buf_p = zend_llist_get_first_ex(err_list, &pos), i = 0; - err_buf_p; - err_buf_p = zend_llist_get_next_ex(err_list, &pos), i++) { - l += strlen(*err_buf_p); - if (i < count - 1) { - l += brlen; - } - } - msg = emalloc(l + 1); - msg[0] = '\0'; - for (err_buf_p = zend_llist_get_first_ex(err_list, &pos), i = 0; - err_buf_p; - err_buf_p = zend_llist_get_next_ex(err_list, &pos), i++) { - strcat(msg, *err_buf_p); - if (i < count - 1) { - strcat(msg, br); - } - } - - free_msg = 1; - } else { - if (wrapper == &php_plain_files_wrapper) { - msg = php_socket_strerror_s(errno, errstr, sizeof(errstr)); - } else { - msg = "operation failed"; - } - } - } else { - msg = "no suitable wrapper could be found"; - } - - php_strip_url_passwd(tmp); - php_error_docref1(NULL, tmp, E_WARNING, "%s: %s", caption, msg); - efree(tmp); - if (free_msg) { - efree(msg); - } -} - -static void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper) -{ - if (wrapper && FG(wrapper_errors)) { - zend_hash_str_del(FG(wrapper_errors), (const char*)&wrapper, sizeof(wrapper)); - } -} - -static void wrapper_error_dtor(void *error) -{ - efree(*(char**)error); -} - -static void wrapper_list_dtor(zval *item) { - zend_llist *list = (zend_llist*)Z_PTR_P(item); - zend_llist_destroy(list); - efree(list); -} - -PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, int options, const char *fmt, ...) -{ - va_list args; - char *buffer = NULL; - - va_start(args, fmt); - vspprintf(&buffer, 0, fmt, args); - va_end(args); - - if ((options & REPORT_ERRORS) || wrapper == NULL) { - php_error_docref(NULL, E_WARNING, "%s", buffer); - efree(buffer); - } else { - zend_llist *list = NULL; - if (!FG(wrapper_errors)) { - ALLOC_HASHTABLE(FG(wrapper_errors)); - zend_hash_init(FG(wrapper_errors), 8, NULL, wrapper_list_dtor, 0); - } else { - list = zend_hash_str_find_ptr(FG(wrapper_errors), (const char*)&wrapper, sizeof(wrapper)); - } - - if (!list) { - zend_llist new_list; - zend_llist_init(&new_list, sizeof(buffer), wrapper_error_dtor, 0); - list = zend_hash_str_update_mem(FG(wrapper_errors), (const char*)&wrapper, - sizeof(wrapper), &new_list, sizeof(new_list)); - } - - /* append to linked list */ - zend_llist_add_element(list, &buffer); - } -} - - /* }}} */ /* allocate a new stream for a particular ops */ @@ -1877,10 +1742,16 @@ void php_shutdown_stream_hashes(void) FG(stream_filters) = NULL; } - if (FG(wrapper_errors)) { - zend_hash_destroy(FG(wrapper_errors)); - efree(FG(wrapper_errors)); - FG(wrapper_errors) = NULL; + if (FG(wrapper_logged_errors)) { + zend_hash_destroy(FG(wrapper_logged_errors)); + efree(FG(wrapper_logged_errors)); + FG(wrapper_logged_errors) = NULL; + } + + if (FG(wrapper_stored_errors)) { + zend_hash_destroy(FG(wrapper_stored_errors)); + efree(FG(wrapper_stored_errors)); + FG(wrapper_stored_errors) = NULL; } } diff --git a/win32/build/config.w32 b/win32/build/config.w32 index 403f0aa6efbfe..6e02ae5c07341 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -298,7 +298,7 @@ AC_DEFINE('HAVE_STRNLEN', 1); AC_DEFINE('ZEND_CHECK_STACK_LIMIT', 1) -ADD_SOURCES("main/streams", "streams.c cast.c memory.c filter.c plain_wrapper.c \ +ADD_SOURCES("main/streams", "streams.c stream_errors.c cast.c memory.c filter.c plain_wrapper.c \ userspace.c transports.c xp_socket.c mmap.c glob_wrapper.c"); ADD_FLAG("CFLAGS_BD_MAIN_STREAMS", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); From e705e04a749fb3e0adb14790a337b779345b6384 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 16 Nov 2025 15:53:30 +0100 Subject: [PATCH 02/37] stream: add error codes and handler check --- main/php_streams.h | 4 - main/streams/php_stream_errors.h | 105 ++++++++-- main/streams/stream_errors.c | 76 +++---- main/streams/stream_errors.stub.php | 294 ++++++++++++++++++++++++++- main/streams/stream_errors_arginfo.h | 71 ++++++- 5 files changed, 472 insertions(+), 78 deletions(-) diff --git a/main/php_streams.h b/main/php_streams.h index 33af6c3c91c30..601223bd9a5fd 100644 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -645,10 +645,6 @@ PHPAPI const char *php_stream_locate_eol(php_stream *stream, zend_string *buf); #define php_stream_open_wrapper(path, mode, options, opened) _php_stream_open_wrapper_ex((path), (mode), (options), (opened), NULL STREAMS_CC) #define php_stream_open_wrapper_ex(path, mode, options, opened, context) _php_stream_open_wrapper_ex((path), (mode), (options), (opened), (context) STREAMS_CC) - -/* pushes an error message onto the stack for a wrapper instance */ -PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, int options, const char *fmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 3, 4); - typedef enum { PHP_STREAM_UNCHANGED = 0, /* orig stream was seekable anyway */ PHP_STREAM_RELEASED = 1, /* newstream should be used; origstream is no longer valid */ diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h index 08998a87111cf..5331000dfe149 100644 --- a/main/streams/php_stream_errors.h +++ b/main/streams/php_stream_errors.h @@ -1,3 +1,19 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jakub Zelenka | + +----------------------------------------------------------------------+ + */ + #ifndef PHP_STREAM_ERRORS_H #define PHP_STREAM_ERRORS_H @@ -18,11 +34,76 @@ BEGIN_EXTERN_C() #define PHP_STREAM_ERROR_STORE_TERMINAL 3 #define PHP_STREAM_ERROR_STORE_ALL 4 -/* Error code definition for registration */ -typedef struct { - int code; - const char *name; -} php_stream_error_code_def; +/* Stream Error Codes*/ +/* No error */ +#define STREAM_ERROR_CODE_NONE 0 +/* Generic unspecified error */ +#define STREAM_ERROR_CODE_GENERIC 1 +/* Stream I/O operations */ +#define STREAM_ERROR_CODE_READ_FAILED 10 +#define STREAM_ERROR_CODE_WRITE_FAILED 11 +#define STREAM_ERROR_CODE_SEEK_FAILED 12 +#define STREAM_ERROR_CODE_SEEK_NOT_SUPPORTED 13 +#define STREAM_ERROR_CODE_FLUSH_FAILED 14 +#define STREAM_ERROR_CODE_TRUNCATE_FAILED 15 +#define STREAM_ERROR_CODE_NOT_WRITABLE 16 +#define STREAM_ERROR_CODE_NOT_READABLE 17 +/* File system operations */ +#define STREAM_ERROR_CODE_NOT_FOUND 30 +#define STREAM_ERROR_CODE_PERMISSION_DENIED 31 +#define STREAM_ERROR_CODE_ALREADY_EXISTS 32 +#define STREAM_ERROR_CODE_INVALID_PATH 33 +#define STREAM_ERROR_CODE_PATH_TOO_LONG 34 +#define STREAM_ERROR_CODE_UNLINK_FAILED 35 +#define STREAM_ERROR_CODE_RENAME_FAILED 36 +#define STREAM_ERROR_CODE_MKDIR_FAILED 37 +#define STREAM_ERROR_CODE_RMDIR_FAILED 38 +#define STREAM_ERROR_CODE_STAT_FAILED 39 +#define STREAM_ERROR_CODE_CHMOD_FAILED 40 +#define STREAM_ERROR_CODE_CHOWN_FAILED 41 +#define STREAM_ERROR_CODE_TOUCH_FAILED 42 +#define STREAM_ERROR_CODE_INVALID_MODE 43 +#define STREAM_ERROR_CODE_MODE_NOT_SUPPORTED 44 +#define STREAM_ERROR_CODE_READONLY 45 +#define STREAM_ERROR_CODE_RECURSION_DETECTED 46 +/* Wrapper/protocol operations */ +#define STREAM_ERROR_CODE_WRAPPER_NOT_FOUND 60 +#define STREAM_ERROR_CODE_WRAPPER_DISABLED 61 +#define STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED 62 +#define STREAM_ERROR_CODE_WRAPPER_REGISTRATION_FAILED 63 +#define STREAM_ERROR_CODE_WRAPPER_UNREGISTRATION_FAILED 64 +#define STREAM_ERROR_CODE_WRAPPER_RESTORATION_FAILED 65 +/* Filter operations */ +#define STREAM_ERROR_CODE_FILTER_NOT_FOUND 80 +#define STREAM_ERROR_CODE_FILTER_FAILED 81 +/* Cast/conversion operations */ +#define STREAM_ERROR_CODE_CAST_FAILED 90 +#define STREAM_ERROR_CODE_CAST_NOT_SUPPORTED 91 +#define STREAM_ERROR_CODE_MAKE_SEEKABLE_FAILED 92 +#define STREAM_ERROR_CODE_BUFFERED_DATA_LOST 93 +/* Network/socket operations */ +#define STREAM_ERROR_CODE_NETWORK_SEND_FAILED 100 +#define STREAM_ERROR_CODE_NETWORK_RECV_FAILED 101 +#define STREAM_ERROR_CODE_SSL_NOT_SUPPORTED 102 +#define STREAM_ERROR_CODE_SOCKET_PATH_TOO_LONG 103 +#define STREAM_ERROR_CODE_OOB_NOT_SUPPORTED 104 +#define STREAM_ERROR_CODE_PROTOCOL_ERROR 105 +#define STREAM_ERROR_CODE_INVALID_URL 106 +#define STREAM_ERROR_CODE_INVALID_RESPONSE 107 +#define STREAM_ERROR_CODE_REDIRECT_LIMIT 108 +#define STREAM_ERROR_CODE_AUTH_FAILED 109 +/* Encoding/decoding operations */ +#define STREAM_ERROR_CODE_ENCODING_FAILED 120 +#define STREAM_ERROR_CODE_INVALID_FORMAT 121 +/* Resource/allocation operations */ +#define STREAM_ERROR_CODE_ALLOCATION_FAILED 130 +#define STREAM_ERROR_CODE_TEMPORARY_FILE_FAILED 131 +/* Locking operations */ +#define STREAM_ERROR_CODE_LOCK_FAILED 140 +#define STREAM_ERROR_CODE_LOCK_NOT_SUPPORTED 141 +/* Userspace stream operations */ +#define STREAM_ERROR_CODE_USERSPACE_NOT_IMPLEMENTED 150 +#define STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN 151 /* Stored error entry */ typedef struct { @@ -34,20 +115,6 @@ typedef struct { bool terminal; } php_stream_error_entry; -/* Sentinel for error code array termination */ -#define PHP_STREAM_ERROR_CODE_END {0, NULL} - -/* Error code registration */ -PHPAPI void php_stream_wrapper_register_error_codes( - php_stream_wrapper *wrapper, - const php_stream_error_code_def *codes -); - -PHPAPI const char *php_stream_wrapper_get_error_name( - php_stream_wrapper *wrapper, - int code -); - /* Main error reporting functions */ PHPAPI void php_stream_wrapper_error( php_stream_wrapper *wrapper, diff --git a/main/streams/stream_errors.c b/main/streams/stream_errors.c index b308521ca33c3..52428e9065f34 100644 --- a/main/streams/stream_errors.c +++ b/main/streams/stream_errors.c @@ -1,3 +1,19 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jakub Zelenka | + +----------------------------------------------------------------------+ + */ + #include "php.h" #include "php_globals.h" #include "php_streams.h" @@ -26,48 +42,6 @@ static void php_stream_error_list_dtor(zval *item) efree(list); } -/* Error code registration */ - -PHPAPI void php_stream_wrapper_register_error_codes( - php_stream_wrapper *wrapper, const php_stream_error_code_def *codes) -{ - if (!php_stream_wrapper_error_codes) { - php_stream_wrapper_error_codes = zend_new_array(8); - } - - zval code_table_zv, *code_table; - code_table = zend_hash_index_find(php_stream_wrapper_error_codes, (zend_ulong) wrapper); - - if (!code_table) { - code_table = &code_table_zv; - ZVAL_ARR(code_table, zend_new_array(8)); - zend_hash_index_update(php_stream_wrapper_error_codes, (zend_ulong) wrapper, code_table); - } - - for (const php_stream_error_code_def *def = codes; def->name != NULL; def++) { - zend_string *name = zend_string_init(def->name, strlen(def->name), 1); - zval zv; - ZVAL_STR(&zv, name); - zend_hash_index_update(code_table, def->code, &zv); - } -} - -PHPAPI const char *php_stream_wrapper_get_error_name(php_stream_wrapper *wrapper, int code) -{ - if (!php_stream_wrapper_error_codes) { - return NULL; - } - - zval *code_table = zend_hash_index_find(php_stream_wrapper_error_codes, (zend_ulong) wrapper); - - if (!code_table) { - return NULL; - } - - zval *error_name = zend_hash_index_find(Z_ARR_P(code_table), code); - return error_name ? Z_STRVAL_P(error_name) : NULL; -} - /* Context option helpers */ static int php_stream_auto_decide_error_store_mode(int error_mode) @@ -186,10 +160,22 @@ static void php_stream_process_error(php_stream_context *context, const char *wr /* Call user error handler if set */ if (context) { zval *handler = php_stream_context_get_option(context, "stream", "error_handler"); - if (handler && Z_TYPE_P(handler) == IS_CALLABLE) { + if (handler) { + zend_fcall_info_cache fcc; + char *is_callable_error = NULL; zval retval; zval args[5]; + if (!zend_is_callable_ex(handler, NULL, 0, NULL, &fcc, &is_callable_error)) { + if (is_callable_error) { + zend_type_error("stream error handler must be a valid callback, %s", is_callable_error); + efree(is_callable_error); + } else { + zend_type_error("stream error must be a valid callback"); + } + return; + } + /* Arg 0: wrapper name */ ZVAL_STRING(&args[0], wrapper_name); @@ -525,10 +511,12 @@ PHPAPI void php_stream_error( zend_string_release(message); } -/* StreamException class registration */ +/* StreamException class and error constants registration */ PHP_MINIT_FUNCTION(stream_errors) { + register_stream_errors_symbols(module_number); + php_ce_stream_exception = register_class_StreamException(zend_ce_exception); return SUCCESS; diff --git a/main/streams/stream_errors.stub.php b/main/streams/stream_errors.stub.php index 387c2c692703f..fb660ad6e56c5 100644 --- a/main/streams/stream_errors.stub.php +++ b/main/streams/stream_errors.stub.php @@ -1,16 +1,298 @@ Date: Sun, 16 Nov 2025 16:46:12 +0100 Subject: [PATCH 03/37] stream: convert phar errors --- ext/phar/dirstream.c | 92 ++++++++++++++++++++++--------- ext/phar/stream.c | 128 +++++++++++++++++++++++++++++-------------- 2 files changed, 152 insertions(+), 68 deletions(-) diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c index f37599e7db117..7faeddba25d7d 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -254,25 +254,30 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, const char *path, phar_archive_data *phar; if ((resource = phar_parse_url(wrapper, path, mode, options)) == NULL) { - php_stream_wrapper_log_error(wrapper, options, "phar url \"%s\" is unknown", path); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, + "phar url \"%s\" is unknown", path); return NULL; } /* we must have at the very least phar://alias.phar/ */ if (!resource->scheme || !resource->host || !resource->path) { if (resource->host && !resource->path) { - php_stream_wrapper_log_error(wrapper, options, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", path, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_PATH, + "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", + path, ZSTR_VAL(resource->host)); php_url_free(resource); return NULL; } php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\", must have at least phar://%s/", path, path); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: invalid url \"%s\", must have at least phar://%s/", path, path); return NULL; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar url \"%s\"", path); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + "phar error: not a phar url \"%s\"", path); return NULL; } @@ -280,10 +285,11 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, const char *path, if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error)) { if (error) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NOT_FOUND, "%s", error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "phar file \"%s\" is unknown", ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NOT_FOUND, + "phar file \"%s\" is unknown", ZSTR_VAL(resource->host)); } php_url_free(resource); return NULL; @@ -353,7 +359,8 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo /* pre-readonly check, we need to know if this is a data phar */ if (FAILURE == phar_split_fname(url_from, strlen(url_from), &arch, &arch_len, NULL, NULL, 2, 2)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\", no phar archive specified", url_from); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NOT_FOUND, + "phar error: cannot create directory \"%s\", no phar archive specified", url_from); return 0; } @@ -364,7 +371,8 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo efree(arch); if (PHAR_G(readonly) && (!phar || !phar->is_data)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\", write operations disabled", url_from); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_READONLY, + "phar error: cannot create directory \"%s\", write operations disabled", url_from); return 0; } @@ -375,18 +383,22 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", url_from); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: invalid url \"%s\"", url_from); return 0; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", url_from); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + "phar error: not a phar stream url \"%s\"", url_from); return 0; } if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", error retrieving phar information: %s", ZSTR_VAL(resource->path) + 1, ZSTR_VAL(resource->host), error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_MKDIR_FAILED, + "phar error: cannot create directory \"%s\" in phar \"%s\", error retrieving phar information: %s", + ZSTR_VAL(resource->path) + 1, ZSTR_VAL(resource->host), error); efree(error); php_url_free(resource); return 0; @@ -398,13 +410,17 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo zend_string_efree(e->filename); efree(e); } - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", directory already exists", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_ALREADY_EXISTS, + "phar error: cannot create directory \"%s\" in phar \"%s\", directory already exists", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); php_url_free(resource); return 0; } if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_MKDIR_FAILED, + "phar error: cannot create directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); php_url_free(resource); return 0; @@ -412,13 +428,17 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo if (phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, ZSTR_LEN(resource->path) - 1, 0, &error, true)) { /* entry exists as a file */ - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", file already exists", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_ALREADY_EXISTS, + "phar error: cannot create directory \"%s\" in phar \"%s\", file already exists", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); php_url_free(resource); return 0; } if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_MKDIR_FAILED, + "phar error: cannot create directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); php_url_free(resource); return 0; @@ -447,7 +467,9 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo entry.old_flags = PHAR_ENT_PERM_DEF_DIR; if (NULL == zend_hash_add_mem(&phar->manifest, entry.filename, &entry, sizeof(phar_entry_info))) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", adding to manifest failed", ZSTR_VAL(entry.filename), phar->fname); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_MKDIR_FAILED, + "phar error: cannot create directory \"%s\" in phar \"%s\", adding to manifest failed", + ZSTR_VAL(entry.filename), phar->fname); zend_string_efree(entry.filename); return 0; } @@ -455,7 +477,9 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo phar_flush(phar, &error); if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(entry.filename), phar->fname, error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_MKDIR_FAILED, + "phar error: cannot create directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(entry.filename), phar->fname, error); zend_hash_del(&phar->manifest, entry.filename); efree(error); return 0; @@ -479,7 +503,8 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options /* pre-readonly check, we need to know if this is a data phar */ if (FAILURE == phar_split_fname(url, strlen(url), &arch, &arch_len, NULL, NULL, 2, 2)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NOT_FOUND, + "phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url); return 0; } @@ -490,7 +515,8 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options efree(arch); if (PHAR_G(readonly) && (!phar || !phar->is_data)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot rmdir directory \"%s\", write operations disabled", url); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_READONLY, + "phar error: cannot rmdir directory \"%s\", write operations disabled", url); return 0; } @@ -501,18 +527,22 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", url); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: invalid url \"%s\"", url); return 0; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", url); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + "phar error: not a phar stream url \"%s\"", url); return 0; } if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", error retrieving phar information: %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_RMDIR_FAILED, + "phar error: cannot remove directory \"%s\" in phar \"%s\", error retrieving phar information: %s", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); php_url_free(resource); return 0; @@ -522,10 +552,14 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options if (!(entry = phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, path_len, 2, &error, true))) { if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_RMDIR_FAILED, + "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", directory does not exist", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NOT_FOUND, + "phar error: cannot remove directory \"%s\" in phar \"%s\", directory does not exist", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); } php_url_free(resource); return 0; @@ -539,7 +573,8 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options zend_string_starts_with_cstr(str_key, ZSTR_VAL(resource->path)+1, path_len) && IS_SLASH(ZSTR_VAL(str_key)[path_len]) ) { - php_stream_wrapper_log_error(wrapper, options, "phar error: Directory not empty"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_RMDIR_FAILED, + "phar error: Directory not empty"); if (entry->is_temp_dir) { zend_string_efree(entry->filename); efree(entry); @@ -555,7 +590,8 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options zend_string_starts_with_cstr(str_key, ZSTR_VAL(resource->path)+1, path_len) && IS_SLASH(ZSTR_VAL(str_key)[path_len]) ) { - php_stream_wrapper_log_error(wrapper, options, "phar error: Directory not empty"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_RMDIR_FAILED, + "phar error: Directory not empty"); if (entry->is_temp_dir) { zend_string_efree(entry->filename); efree(entry); @@ -576,7 +612,9 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options phar_flush(phar, &error); if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(entry->filename), phar->fname, error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_RMDIR_FAILED, + "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(entry->filename), phar->fname, error); php_url_free(resource); efree(error); return 0; diff --git a/ext/phar/stream.c b/ext/phar/stream.c index d77dbdcffc74f..2dfb8097ac1c6 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -68,17 +68,21 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const } if (mode[0] == 'a') { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: open mode append not supported"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, + "phar error: open mode append not supported"); } return NULL; } if (phar_split_fname(filename, strlen(filename), &arch, &arch_len, &entry, &entry_len, 2, (mode[0] == 'w' ? 2 : 0)) == FAILURE) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { if (arch && !entry) { - php_stream_wrapper_log_error(wrapper, options, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", filename, arch); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_PATH, + "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", + filename, arch); arch = NULL; } else { - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url or non-existent phar \"%s\"", filename); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: invalid url or non-existent phar \"%s\"", filename); } } return NULL; @@ -111,7 +115,8 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const } if (PHAR_G(readonly) && (!pphar || !pphar->is_data)) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: write operations disabled by the php.ini setting phar.readonly"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_READONLY, + "phar error: write operations disabled by the php.ini setting phar.readonly"); } php_url_free(resource); return NULL; @@ -120,7 +125,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const { if (error) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_GENERIC, "%s", error); } efree(error); } @@ -131,7 +136,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const if (error) { spprintf(&error, 0, "Cannot open cached phar '%s' as writeable, copy on write failed", ZSTR_VAL(resource->host)); if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_GENERIC, "%s", error); } efree(error); } @@ -143,7 +148,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const { if (error) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NOT_FOUND, "%s", error); } efree(error); } @@ -176,13 +181,15 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", path); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: invalid url \"%s\"", path); return NULL; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", path); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + "phar error: not a phar stream url \"%s\"", path); return NULL; } @@ -193,10 +200,11 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) { if (NULL == (idata = phar_get_or_create_entry_data(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), internal_file, strlen(internal_file), mode, 0, &error, true))) { if (error) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_GENERIC, "%s", error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "phar error: file \"%s\" could not be created in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_GENERIC, + "phar error: file \"%s\" could not be created in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); } efree(internal_file); php_url_free(resource); @@ -236,7 +244,8 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha if (!*internal_file && (options & STREAM_OPEN_FOR_INCLUDE)) { /* retrieve the stub */ if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, NULL)) { - php_stream_wrapper_log_error(wrapper, options, "file %s is not a valid phar archive", ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_FORMAT, + "file %s is not a valid phar archive", ZSTR_VAL(resource->host)); efree(internal_file); php_url_free(resource); return NULL; @@ -255,7 +264,8 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha php_stream *stream = phar_get_pharfp(phar); if (stream == NULL) { if (UNEXPECTED(FAILURE == phar_open_archive_fp(phar))) { - php_stream_wrapper_log_error(wrapper, options, "phar error: could not reopen phar \"%s\"", ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_GENERIC, + "phar error: could not reopen phar \"%s\"", ZSTR_VAL(resource->host)); efree(internal_file); php_url_free(resource); return NULL; @@ -293,10 +303,11 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha if ((FAILURE == phar_get_entry_data(&idata, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), internal_file, strlen(internal_file), "r", 0, &error, false)) || !idata) { idata_error: if (error) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NOT_FOUND, "%s", error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NOT_FOUND, + "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); } efree(internal_file); php_url_free(resource); @@ -314,7 +325,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha /* check length, crc32 */ if (!idata->internal_file->is_crc_checked && phar_postprocess_file(idata, idata->internal_file->crc32, &error, 2) != SUCCESS) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_GENERIC, "%s", error); efree(error); phar_entry_delref(idata); efree(internal_file); @@ -445,7 +456,9 @@ static ssize_t phar_stream_write(php_stream *stream, const char *buf, size_t cou php_stream_seek(data->fp, data->position + data->zero, SEEK_SET); if (count != php_stream_write(data->fp, buf, count)) { - php_stream_wrapper_log_error(stream->wrapper, stream->flags, "phar error: Could not write %zu characters to \"%s\" in phar \"%s\"", count, ZSTR_VAL(data->internal_file->filename), data->phar->fname); + php_stream_wrapper_log_warn(stream->wrapper, stream->flags, STREAM_ERROR_CODE_WRITE_FAILED, + "phar error: Could not write %zu characters to \"%s\" in phar \"%s\"", + count, ZSTR_VAL(data->internal_file->filename), data->phar->fname); return -1; } data->position = php_stream_tell(data->fp) - data->zero; @@ -472,7 +485,7 @@ static int phar_stream_flush(php_stream *stream) /* {{{ */ data->internal_file->timestamp = time(0); ret = phar_flush(data->phar, &error); if (error) { - php_stream_wrapper_log_error(stream->wrapper, REPORT_ERRORS, "%s", error); + php_stream_wrapper_log_warn(stream->wrapper, REPORT_ERRORS, STREAM_ERROR_CODE_FLUSH_FAILED, "%s", error); efree(error); } return ret; @@ -663,20 +676,23 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int phar_archive_data *pphar; if ((resource = phar_parse_url(wrapper, url, "rb", options)) == NULL) { - php_stream_wrapper_log_error(wrapper, options, "phar error: unlink failed"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_UNLINK_FAILED, + "phar error: unlink failed"); return 0; } /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", url); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: invalid url \"%s\"", url); return 0; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", url); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + "phar error: not a phar stream url \"%s\"", url); return 0; } @@ -685,7 +701,8 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int pphar = zend_hash_find_ptr(&(PHAR_G(phar_fname_map)), resource->host); if (PHAR_G(readonly) && (!pphar || !pphar->is_data)) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: write operations disabled by the php.ini setting phar.readonly"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_READONLY, + "phar error: write operations disabled by the php.ini setting phar.readonly"); return 0; } @@ -695,10 +712,12 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int if (FAILURE == phar_get_entry_data(&idata, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), internal_file, internal_file_len, "r", 0, &error, true)) { /* constraints of fp refcount were not met */ if (error) { - php_stream_wrapper_log_error(wrapper, options, "unlink of \"%s\" failed: %s", url, error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_UNLINK_FAILED, + "unlink of \"%s\" failed: %s", url, error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "unlink of \"%s\" failed, file does not exist", url); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NOT_FOUND, + "unlink of \"%s\" failed, file does not exist", url); } efree(internal_file); php_url_free(resource); @@ -709,7 +728,9 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int } if (idata->internal_file->fp_refcount > 1) { /* more than just our fp resource is open for this file */ - php_stream_wrapper_log_error(wrapper, options, "phar error: \"%s\" in phar \"%s\", has open file pointers, cannot unlink", internal_file, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_UNLINK_FAILED, + "phar error: \"%s\" in phar \"%s\", has open file pointers, cannot unlink", + internal_file, ZSTR_VAL(resource->host)); efree(internal_file); php_url_free(resource); phar_entry_delref(idata); @@ -719,7 +740,7 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int efree(internal_file); phar_entry_remove(idata, &error); if (error) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_UNLINK_FAILED, "%s", error); efree(error); } return 1; @@ -738,7 +759,9 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from error = NULL; if ((resource_from = phar_parse_url(wrapper, url_from, "wb", options|PHP_STREAM_URL_STAT_QUIET)) == NULL) { - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", url_from, url_to, url_from); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", + url_from, url_to, url_from); return 0; } if (SUCCESS != phar_get_archive(&pfrom, ZSTR_VAL(resource_from->host), ZSTR_LEN(resource_from->host), NULL, 0, &error)) { @@ -749,13 +772,16 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from } if (PHAR_G(readonly) && (!pfrom || !pfrom->is_data)) { php_url_free(resource_from); - php_error_docref(NULL, E_WARNING, "phar error: Write operations disabled by the php.ini setting phar.readonly"); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_READONLY, + "phar error: Write operations disabled by the php.ini setting phar.readonly"); return 0; } if ((resource_to = phar_parse_url(wrapper, url_to, "wb", options|PHP_STREAM_URL_STAT_QUIET)) == NULL) { php_url_free(resource_from); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", url_from, url_to, url_to); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", + url_from, url_to, url_to); return 0; } if (SUCCESS != phar_get_archive(&pto, ZSTR_VAL(resource_to->host), ZSTR_LEN(resource_to->host), NULL, 0, &error)) { @@ -767,14 +793,17 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (PHAR_G(readonly) && (!pto || !pto->is_data)) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: Write operations disabled by the php.ini setting phar.readonly"); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_READONLY, + "phar error: Write operations disabled by the php.ini setting phar.readonly"); return 0; } if (!zend_string_equals(resource_from->host, resource_to->host)) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\", not within the same phar archive", url_from, url_to); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_RENAME_FAILED, + "phar error: cannot rename \"%s\" to \"%s\", not within the same phar archive", + url_from, url_to); return 0; } @@ -782,35 +811,44 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (!resource_from->scheme || !resource_from->host || !resource_from->path) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", url_from, url_to, url_from); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", + url_from, url_to, url_from); return 0; } if (!resource_to->scheme || !resource_to->host || !resource_to->path) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", url_from, url_to, url_to); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, + "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", + url_from, url_to, url_to); return 0; } if (!zend_string_equals_literal_ci(resource_from->scheme, "phar")) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", url_from, url_to, url_from); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", + url_from, url_to, url_from); return 0; } if (!zend_string_equals_literal_ci(resource_to->scheme, "phar")) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", url_from, url_to, url_to); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", + url_from, url_to, url_to); return 0; } if (SUCCESS != phar_get_archive(&phar, ZSTR_VAL(resource_from->host), ZSTR_LEN(resource_from->host), NULL, 0, &error)) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_RENAME_FAILED, + "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); efree(error); return 0; } @@ -818,7 +856,9 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (phar->is_persistent && FAILURE == phar_copy_on_write(&phar)) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": could not make cached phar writeable", url_from, url_to); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_RENAME_FAILED, + "phar error: cannot rename \"%s\" to \"%s\": could not make cached phar writeable", + url_from, url_to); return 0; } @@ -829,7 +869,9 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (entry->is_deleted) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source has been deleted", url_from, url_to); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, + "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source has been deleted", + url_from, url_to); return 0; } /* transfer all data over to the new entry */ @@ -849,7 +891,8 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (FAILURE == phar_copy_entry_fp(source, entry, &error)) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_RENAME_FAILED, + "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); efree(error); zend_hash_del(&phar->manifest, entry->filename); return 0; @@ -863,7 +906,9 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from /* file does not exist */ php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source does not exist", url_from, url_to); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, + "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source does not exist", + url_from, url_to); return 0; } @@ -942,7 +987,8 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (error) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_RENAME_FAILED, + "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); efree(error); return 0; } From a54de16571a0ca0c873f508b22c4c80f3d11987f Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 16 Nov 2025 16:55:22 +0100 Subject: [PATCH 04/37] stream: convert ftp wrapper errors --- ext/standard/ftp_fopen_wrapper.c | 78 +++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/ext/standard/ftp_fopen_wrapper.c b/ext/standard/ftp_fopen_wrapper.c index d1bb3aeeccd68..9c95214ab3b0f 100644 --- a/ext/standard/ftp_fopen_wrapper.c +++ b/ext/standard/ftp_fopen_wrapper.c @@ -109,7 +109,9 @@ static int php_stream_ftp_stream_close(php_stream_wrapper *wrapper, php_stream * /* For write modes close data stream first to signal EOF to server */ result = GET_FTP_RESULT(controlstream); if (result != 226 && result != 250) { - php_error_docref(NULL, E_WARNING, "FTP server error %d:%s", result, tmp_line); + php_stream_wrapper_warn(wrapper, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + STREAM_ERROR_CODE_PROTOCOL_ERROR, + "FTP server error %d:%s", result, tmp_line); ret = EOF; } } @@ -187,7 +189,8 @@ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char /* get the response */ result = GET_FTP_RESULT(stream); if (result != 334) { - php_stream_wrapper_log_error(wrapper, options, "Server doesn't support FTPS."); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + "Server doesn't support FTPS."); goto connect_errexit; } else { /* we must reuse the old SSL session id */ @@ -206,7 +209,8 @@ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(stream, 1) < 0) { - php_stream_wrapper_log_error(wrapper, options, "Unable to activate SSL mode"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + "Unable to activate SSL mode"); php_stream_close(stream); stream = NULL; goto connect_errexit; @@ -237,7 +241,7 @@ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char unsigned char *s = (unsigned char *) val, *e = (unsigned char *) s + val_len; \ while (s < e) { \ if (iscntrl(*s)) { \ - php_stream_wrapper_log_error(wrapper, options, err_msg, val); \ + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_AUTH_FAILED, err_msg, val); \ goto connect_errexit; \ } \ s++; \ @@ -434,7 +438,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa } if (strpbrk(mode, "wa+")) { if (read_write) { - php_stream_wrapper_log_error(wrapper, options, "FTP does not support simultaneous read/write connections"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, + "FTP does not support simultaneous read/write connections"); return NULL; } if (strchr(mode, 'a')) { @@ -445,7 +450,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa } if (!read_write) { /* No mode specified? */ - php_stream_wrapper_log_error(wrapper, options, "Unknown file open mode"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_MODE, + "Unknown file open mode"); return NULL; } @@ -456,7 +462,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa return php_stream_url_wrap_http(wrapper, path, mode, options, opened_path, context STREAMS_CC); } else { /* ftp proxy is read-only */ - php_stream_wrapper_log_error(wrapper, options, "FTP proxy may only be used in read mode"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, + "FTP proxy may only be used in read mode"); return NULL; } } @@ -508,7 +515,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa goto errexit; } } else { - php_stream_wrapper_log_error(wrapper, options, "Remote file already exists and overwrite context option not specified"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_ALREADY_EXISTS, + "Remote file already exists and overwrite context option not specified"); errno = EEXIST; goto errexit; } @@ -532,7 +540,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa php_stream_printf(stream, "REST " ZEND_LONG_FMT "\r\n", Z_LVAL_P(tmpzval)); result = GET_FTP_RESULT(stream); if (result < 300 || result > 399) { - php_stream_wrapper_log_error(wrapper, options, "Unable to resume from offset " ZEND_LONG_FMT, Z_LVAL_P(tmpzval)); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_GENERIC, + "Unable to resume from offset " ZEND_LONG_FMT, Z_LVAL_P(tmpzval)); goto errexit; } } @@ -577,7 +586,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(datastream, 1) < 0)) { - php_stream_wrapper_log_error(wrapper, options, "Unable to activate SSL mode"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + "Unable to activate SSL mode"); php_stream_close(datastream); datastream = NULL; tmp_line[0]='\0'; @@ -599,10 +609,12 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa php_stream_close(stream); } if (tmp_line[0] != '\0') - php_stream_wrapper_log_error(wrapper, options, "FTP server reports %s", tmp_line); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, + "FTP server reports %s", tmp_line); if (error_message) { - php_stream_wrapper_log_error(wrapper, options, "Failed to set up data channel: %s", ZSTR_VAL(error_message)); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NETWORK_SEND_FAILED, + "Failed to set up data channel: %s", ZSTR_VAL(error_message)); zend_string_release(error_message); } return NULL; @@ -747,7 +759,8 @@ static php_stream * php_stream_ftp_opendir(php_stream_wrapper *wrapper, const ch STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(datastream, 1) < 0)) { - php_stream_wrapper_log_error(wrapper, options, "Unable to activate SSL mode"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + "Unable to activate SSL mode"); php_stream_close(datastream); datastream = NULL; goto opendir_errexit; @@ -771,7 +784,8 @@ static php_stream * php_stream_ftp_opendir(php_stream_wrapper *wrapper, const ch php_stream_close(stream); } if (tmp_line[0] != '\0') { - php_stream_wrapper_log_error(wrapper, options, "FTP server reports %s", tmp_line); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, + "FTP server reports %s", tmp_line); } return NULL; } @@ -910,14 +924,16 @@ static int php_stream_ftp_unlink(php_stream_wrapper *wrapper, const char *url, i stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, context, NULL, &resource, NULL, NULL); if (!stream) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Unable to connect to %s", url); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_AUTH_FAILED, + "Unable to connect to %s", url); } goto unlink_errexit; } if (resource->path == NULL) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Invalid path provided in %s", url); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_PATH, + "Invalid path provided in %s", url); } goto unlink_errexit; } @@ -928,7 +944,8 @@ static int php_stream_ftp_unlink(php_stream_wrapper *wrapper, const char *url, i result = GET_FTP_RESULT(stream); if (result < 200 || result > 299) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Error Deleting file: %s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_UNLINK_FAILED, + "Error Deleting file: %s", tmp_line); } goto unlink_errexit; } @@ -992,7 +1009,8 @@ static int php_stream_ftp_rename(php_stream_wrapper *wrapper, const char *url_fr stream = php_ftp_fopen_connect(wrapper, url_from, "r", 0, NULL, context, NULL, NULL, NULL, NULL); if (!stream) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Unable to connect to %s", ZSTR_VAL(resource_from->host)); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_AUTH_FAILED, + "Unable to connect to %s", ZSTR_VAL(resource_from->host)); } goto rename_errexit; } @@ -1003,7 +1021,8 @@ static int php_stream_ftp_rename(php_stream_wrapper *wrapper, const char *url_fr result = GET_FTP_RESULT(stream); if (result < 300 || result > 399) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Error Renaming file: %s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_RENAME_FAILED, + "Error Renaming file: %s", tmp_line); } goto rename_errexit; } @@ -1014,7 +1033,8 @@ static int php_stream_ftp_rename(php_stream_wrapper *wrapper, const char *url_fr result = GET_FTP_RESULT(stream); if (result < 200 || result > 299) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Error Renaming file: %s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_RENAME_FAILED, + "Error Renaming file: %s", tmp_line); } goto rename_errexit; } @@ -1047,14 +1067,16 @@ static int php_stream_ftp_mkdir(php_stream_wrapper *wrapper, const char *url, in stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, context, NULL, &resource, NULL, NULL); if (!stream) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Unable to connect to %s", url); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_AUTH_FAILED, + "Unable to connect to %s", url); } goto mkdir_errexit; } if (resource->path == NULL) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Invalid path provided in %s", url); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_PATH, + "Invalid path provided in %s", url); } goto mkdir_errexit; } @@ -1095,7 +1117,8 @@ static int php_stream_ftp_mkdir(php_stream_wrapper *wrapper, const char *url, in result = GET_FTP_RESULT(stream); if (result < 200 || result > 299) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_MKDIR_FAILED, + "%s", tmp_line); } break; } @@ -1139,14 +1162,16 @@ static int php_stream_ftp_rmdir(php_stream_wrapper *wrapper, const char *url, in stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, context, NULL, &resource, NULL, NULL); if (!stream) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Unable to connect to %s", url); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_AUTH_FAILED, + "Unable to connect to %s", url); } goto rmdir_errexit; } if (resource->path == NULL) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Invalid path provided in %s", url); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_PATH, + "Invalid path provided in %s", url); } goto rmdir_errexit; } @@ -1156,7 +1181,8 @@ static int php_stream_ftp_rmdir(php_stream_wrapper *wrapper, const char *url, in if (result < 200 || result > 299) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_RMDIR_FAILED, + "%s", tmp_line); } goto rmdir_errexit; } From f8ecb9b7ad0fff57704b036815fe748d4af50252 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 16 Nov 2025 17:44:50 +0100 Subject: [PATCH 05/37] stream: add few more errors to replace generic errors in phar and ftp --- ext/phar/stream.c | 12 +++---- ext/standard/ftp_fopen_wrapper.c | 2 +- main/streams/php_stream_errors.h | 48 +++++++++++++++------------- main/streams/stream_errors.stub.php | 20 ++++++++++++ main/streams/stream_errors_arginfo.h | 6 +++- 5 files changed, 58 insertions(+), 30 deletions(-) diff --git a/ext/phar/stream.c b/ext/phar/stream.c index 2dfb8097ac1c6..faae9b3d325a8 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -125,7 +125,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const { if (error) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_GENERIC, "%s", error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_OPEN_FAILED, "%s", error); } efree(error); } @@ -136,7 +136,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const if (error) { spprintf(&error, 0, "Cannot open cached phar '%s' as writeable, copy on write failed", ZSTR_VAL(resource->host)); if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_GENERIC, "%s", error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_OPEN_FAILED, "%s", error); } efree(error); } @@ -200,10 +200,10 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) { if (NULL == (idata = phar_get_or_create_entry_data(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), internal_file, strlen(internal_file), mode, 0, &error, true))) { if (error) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_GENERIC, "%s", error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_CREATE_FAILED, "%s", error); efree(error); } else { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_GENERIC, + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_CREATE_FAILED, "phar error: file \"%s\" could not be created in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); } efree(internal_file); @@ -264,7 +264,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha php_stream *stream = phar_get_pharfp(phar); if (stream == NULL) { if (UNEXPECTED(FAILURE == phar_open_archive_fp(phar))) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_GENERIC, + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_OPEN_FAILED, "phar error: could not reopen phar \"%s\"", ZSTR_VAL(resource->host)); efree(internal_file); php_url_free(resource); @@ -325,7 +325,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha /* check length, crc32 */ if (!idata->internal_file->is_crc_checked && phar_postprocess_file(idata, idata->internal_file->crc32, &error, 2) != SUCCESS) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_GENERIC, "%s", error); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_ARCHIVING_FAILED, "%s", error); efree(error); phar_entry_delref(idata); efree(internal_file); diff --git a/ext/standard/ftp_fopen_wrapper.c b/ext/standard/ftp_fopen_wrapper.c index 9c95214ab3b0f..6f6550b6ac8f7 100644 --- a/ext/standard/ftp_fopen_wrapper.c +++ b/ext/standard/ftp_fopen_wrapper.c @@ -540,7 +540,7 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa php_stream_printf(stream, "REST " ZEND_LONG_FMT "\r\n", Z_LVAL_P(tmpzval)); result = GET_FTP_RESULT(stream); if (result < 300 || result > 399) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_GENERIC, + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_RESUMPTION_FAILED, "Unable to resume from offset " ZEND_LONG_FMT, Z_LVAL_P(tmpzval)); goto errexit; } diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h index 5331000dfe149..f36ece142bc06 100644 --- a/main/streams/php_stream_errors.h +++ b/main/streams/php_stream_errors.h @@ -54,18 +54,20 @@ BEGIN_EXTERN_C() #define STREAM_ERROR_CODE_ALREADY_EXISTS 32 #define STREAM_ERROR_CODE_INVALID_PATH 33 #define STREAM_ERROR_CODE_PATH_TOO_LONG 34 -#define STREAM_ERROR_CODE_UNLINK_FAILED 35 -#define STREAM_ERROR_CODE_RENAME_FAILED 36 -#define STREAM_ERROR_CODE_MKDIR_FAILED 37 -#define STREAM_ERROR_CODE_RMDIR_FAILED 38 -#define STREAM_ERROR_CODE_STAT_FAILED 39 -#define STREAM_ERROR_CODE_CHMOD_FAILED 40 -#define STREAM_ERROR_CODE_CHOWN_FAILED 41 -#define STREAM_ERROR_CODE_TOUCH_FAILED 42 -#define STREAM_ERROR_CODE_INVALID_MODE 43 -#define STREAM_ERROR_CODE_MODE_NOT_SUPPORTED 44 -#define STREAM_ERROR_CODE_READONLY 45 -#define STREAM_ERROR_CODE_RECURSION_DETECTED 46 +#define STREAM_ERROR_CODE_OPEN_FAILED 35 +#define STREAM_ERROR_CODE_CREATE_FAILED 36 +#define STREAM_ERROR_CODE_UNLINK_FAILED 37 +#define STREAM_ERROR_CODE_RENAME_FAILED 38 +#define STREAM_ERROR_CODE_MKDIR_FAILED 39 +#define STREAM_ERROR_CODE_RMDIR_FAILED 40 +#define STREAM_ERROR_CODE_STAT_FAILED 41 +#define STREAM_ERROR_CODE_CHMOD_FAILED 42 +#define STREAM_ERROR_CODE_CHOWN_FAILED 43 +#define STREAM_ERROR_CODE_TOUCH_FAILED 44 +#define STREAM_ERROR_CODE_INVALID_MODE 45 +#define STREAM_ERROR_CODE_MODE_NOT_SUPPORTED 46 +#define STREAM_ERROR_CODE_READONLY 47 +#define STREAM_ERROR_CODE_RECURSION_DETECTED 48 /* Wrapper/protocol operations */ #define STREAM_ERROR_CODE_WRAPPER_NOT_FOUND 60 #define STREAM_ERROR_CODE_WRAPPER_DISABLED 61 @@ -85,16 +87,18 @@ BEGIN_EXTERN_C() #define STREAM_ERROR_CODE_NETWORK_SEND_FAILED 100 #define STREAM_ERROR_CODE_NETWORK_RECV_FAILED 101 #define STREAM_ERROR_CODE_SSL_NOT_SUPPORTED 102 -#define STREAM_ERROR_CODE_SOCKET_PATH_TOO_LONG 103 -#define STREAM_ERROR_CODE_OOB_NOT_SUPPORTED 104 -#define STREAM_ERROR_CODE_PROTOCOL_ERROR 105 -#define STREAM_ERROR_CODE_INVALID_URL 106 -#define STREAM_ERROR_CODE_INVALID_RESPONSE 107 -#define STREAM_ERROR_CODE_REDIRECT_LIMIT 108 -#define STREAM_ERROR_CODE_AUTH_FAILED 109 -/* Encoding/decoding operations */ -#define STREAM_ERROR_CODE_ENCODING_FAILED 120 -#define STREAM_ERROR_CODE_INVALID_FORMAT 121 +#define STREAM_ERROR_CODE_RESUMPTION_FAILED 103 +#define STREAM_ERROR_CODE_SOCKET_PATH_TOO_LONG 104 +#define STREAM_ERROR_CODE_OOB_NOT_SUPPORTED 105 +#define STREAM_ERROR_CODE_PROTOCOL_ERROR 106 +#define STREAM_ERROR_CODE_INVALID_URL 107 +#define STREAM_ERROR_CODE_INVALID_RESPONSE 108 +#define STREAM_ERROR_CODE_REDIRECT_LIMIT 109 +#define STREAM_ERROR_CODE_AUTH_FAILED 110 +/* Encoding/decoding/archiving operations */ +#define STREAM_ERROR_CODE_ARCHIVING_FAILED 120 +#define STREAM_ERROR_CODE_ENCODING_FAILED 121 +#define STREAM_ERROR_CODE_INVALID_FORMAT 122 /* Resource/allocation operations */ #define STREAM_ERROR_CODE_ALLOCATION_FAILED 130 #define STREAM_ERROR_CODE_TEMPORARY_FILE_FAILED 131 diff --git a/main/streams/stream_errors.stub.php b/main/streams/stream_errors.stub.php index fb660ad6e56c5..a9d090858aff6 100644 --- a/main/streams/stream_errors.stub.php +++ b/main/streams/stream_errors.stub.php @@ -86,6 +86,16 @@ public function getWrapperName(): string {} * @cvalue STREAM_ERROR_CODE_PATH_TOO_LONG */ const STREAM_ERROR_CODE_PATH_TOO_LONG = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_CREATE_FAILED + */ +const STREAM_ERROR_CODE_CREATE_FAILED = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_OPEN_FAILED + */ +const STREAM_ERROR_CODE_OPEN_FAILED = UNKNOWN; /** * @var int * @cvalue STREAM_ERROR_CODE_UNLINK_FAILED @@ -221,6 +231,11 @@ public function getWrapperName(): string {} * @cvalue STREAM_ERROR_CODE_SSL_NOT_SUPPORTED */ const STREAM_ERROR_CODE_SSL_NOT_SUPPORTED = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_RESUMPTION_FAILED + */ +const STREAM_ERROR_CODE_RESUMPTION_FAILED = UNKNOWN; /** * @var int * @cvalue STREAM_ERROR_CODE_SOCKET_PATH_TOO_LONG @@ -256,6 +271,11 @@ public function getWrapperName(): string {} * @cvalue STREAM_ERROR_CODE_AUTH_FAILED */ const STREAM_ERROR_CODE_AUTH_FAILED = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_ARCHIVING_FAILED + */ +const STREAM_ERROR_CODE_ARCHIVING_FAILED = UNKNOWN; /** * @var int * @cvalue STREAM_ERROR_CODE_ENCODING_FAILED diff --git a/main/streams/stream_errors_arginfo.h b/main/streams/stream_errors_arginfo.h index fde8805efd8e8..ff8da9dab548e 100644 --- a/main/streams/stream_errors_arginfo.h +++ b/main/streams/stream_errors_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 33e2f6f12083668388d80dbd77d97fd3f1efbe39 */ + * Stub hash: e3d25328018715348be75f6cb31ae6684afe0a56 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_StreamException_getParam, 0, 0, IS_STRING, 1) ZEND_END_ARG_INFO() @@ -33,6 +33,8 @@ static void register_stream_errors_symbols(int module_number) REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_ALREADY_EXISTS", STREAM_ERROR_CODE_ALREADY_EXISTS, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_INVALID_PATH", STREAM_ERROR_CODE_INVALID_PATH, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_PATH_TOO_LONG", STREAM_ERROR_CODE_PATH_TOO_LONG, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_CREATE_FAILED", STREAM_ERROR_CODE_CREATE_FAILED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_OPEN_FAILED", STREAM_ERROR_CODE_OPEN_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_UNLINK_FAILED", STREAM_ERROR_CODE_UNLINK_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_RENAME_FAILED", STREAM_ERROR_CODE_RENAME_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_MKDIR_FAILED", STREAM_ERROR_CODE_MKDIR_FAILED, CONST_PERSISTENT); @@ -60,6 +62,7 @@ static void register_stream_errors_symbols(int module_number) REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_NETWORK_SEND_FAILED", STREAM_ERROR_CODE_NETWORK_SEND_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_NETWORK_RECV_FAILED", STREAM_ERROR_CODE_NETWORK_RECV_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_SSL_NOT_SUPPORTED", STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_RESUMPTION_FAILED", STREAM_ERROR_CODE_RESUMPTION_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_SOCKET_PATH_TOO_LONG", STREAM_ERROR_CODE_SOCKET_PATH_TOO_LONG, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_OOB_NOT_SUPPORTED", STREAM_ERROR_CODE_OOB_NOT_SUPPORTED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_PROTOCOL_ERROR", STREAM_ERROR_CODE_PROTOCOL_ERROR, CONST_PERSISTENT); @@ -67,6 +70,7 @@ static void register_stream_errors_symbols(int module_number) REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_INVALID_RESPONSE", STREAM_ERROR_CODE_INVALID_RESPONSE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_REDIRECT_LIMIT", STREAM_ERROR_CODE_REDIRECT_LIMIT, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_AUTH_FAILED", STREAM_ERROR_CODE_AUTH_FAILED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_ARCHIVING_FAILED", STREAM_ERROR_CODE_ARCHIVING_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_ENCODING_FAILED", STREAM_ERROR_CODE_ENCODING_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_INVALID_FORMAT", STREAM_ERROR_CODE_INVALID_FORMAT, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_ALLOCATION_FAILED", STREAM_ERROR_CODE_ALLOCATION_FAILED, CONST_PERSISTENT); From ccd02f1f72a28cb169da5b9315ca3ff8fa5b3dcc Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 16 Nov 2025 18:24:22 +0100 Subject: [PATCH 06/37] stream: convert http wrapper errors --- ext/standard/http_fopen_wrapper.c | 49 ++++++++++++++++++---------- main/streams/php_stream_errors.h | 6 ++-- main/streams/stream_errors.stub.php | 10 ++++++ main/streams/stream_errors_arginfo.h | 4 ++- 4 files changed, 48 insertions(+), 21 deletions(-) diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index da150381f43f3..9f206a02c272d 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -246,7 +246,7 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w while (last_header_name < last_header_value) { if (*last_header_name == ' ' || *last_header_name == '\t') { header_info->error = true; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_RESPONSE, "HTTP invalid response format (space in header name)!"); zend_string_efree(last_header_line_str); return NULL; @@ -264,7 +264,7 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w } else { /* There is no colon which means invalid response so error. */ header_info->error = true; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_RESPONSE, "HTTP invalid response format (no colon in header line)!"); zend_string_efree(last_header_line_str); return NULL; @@ -288,7 +288,7 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w size_t last_header_value_len = strlen(last_header_value); if (last_header_value_len > HTTP_HEADER_MAX_LOCATION_SIZE) { header_info->error = true; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_RESPONSE, "HTTP Location header size is over the limit of %d bytes", HTTP_HEADER_MAX_LOCATION_SIZE); zend_string_efree(last_header_line_str); @@ -389,7 +389,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, tmp_line[0] = '\0'; if (redirect_max < 1) { - php_stream_wrapper_log_error(wrapper, options, "Redirection limit reached, aborting"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_REDIRECT_LIMIT, + "Redirection limit reached, aborting"); return NULL; } @@ -422,7 +423,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, /* Normal http request (possibly with proxy) */ if (strpbrk(mode, "awx+")) { - php_stream_wrapper_log_error(wrapper, options, "HTTP wrapper does not support writeable connections"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, + "HTTP wrapper does not support writeable connections"); php_uri_struct_free(resource); return NULL; } @@ -451,7 +453,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } if (request_fulluri && (strchr(path, '\n') != NULL || strchr(path, '\r') != NULL)) { - php_stream_wrapper_log_error(wrapper, options, "HTTP wrapper full URI path does not allow CR or LF characters"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, + "HTTP wrapper full URI path does not allow CR or LF characters"); php_uri_struct_free(resource); zend_string_release(transport_string); return NULL; @@ -466,7 +469,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, #endif if (d > timeoutmax) { - php_stream_wrapper_log_error(wrapper, options, "timeout must be lower than " ZEND_ULONG_FMT, (zend_ulong)timeoutmax); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_PARAM, + "timeout must be lower than " ZEND_ULONG_FMT, (zend_ulong)timeoutmax); zend_string_release(transport_string); php_uri_struct_free(resource); return NULL; @@ -496,7 +500,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } if (errstr) { - php_stream_wrapper_log_error(wrapper, options, "%s", ZSTR_VAL(errstr)); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, + "%s", ZSTR_VAL(errstr)); zend_string_release_ex(errstr, 0); errstr = NULL; } @@ -547,7 +552,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, smart_str_appendl(&header, "\r\n", sizeof("\r\n")-1); if (php_stream_write(stream, ZSTR_VAL(header.s), ZSTR_LEN(header.s)) != ZSTR_LEN(header.s)) { - php_stream_wrapper_log_error(wrapper, options, "Cannot connect to HTTPS server through proxy"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, + "Cannot connect to HTTPS server through proxy"); php_stream_close(stream); stream = NULL; } @@ -570,7 +576,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (stream) { if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(stream, 1) < 0) { - php_stream_wrapper_log_error(wrapper, options, "Cannot connect to HTTPS server through proxy"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + "Cannot connect to HTTPS server through proxy"); php_stream_close(stream); stream = NULL; } @@ -827,7 +834,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, ua[ua_len] = 0; smart_str_appendl(&req_buf, ua, ua_len); } else { - php_error_docref(NULL, E_WARNING, "Cannot construct User-agent header"); + php_stream_wrapper_warn_nt(wrapper, context, options, STREAM_ERROR_CODE_INVALID_HEADER, + "Cannot construct User-agent header"); } efree(ua); } @@ -866,7 +874,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } if (!(have_header & HTTP_HEADER_TYPE)) { smart_str_appends(&req_buf, "Content-Type: application/x-www-form-urlencoded\r\n"); - php_error_docref(NULL, E_NOTICE, "Content-type not specified assuming application/x-www-form-urlencoded"); + php_stream_wrapper_notice(wrapper, context, options, STREAM_ERROR_CODE_INVALID_HEADER, + "Content-type not specified assuming application/x-www-form-urlencoded"); } smart_str_appends(&req_buf, "\r\n"); smart_str_appendl(&req_buf, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval)); @@ -953,7 +962,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } else { php_stream_close(stream); stream = NULL; - php_stream_wrapper_log_error(wrapper, options, "HTTP request failed!"); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, + "HTTP request failed!"); goto out; } } @@ -971,7 +981,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (http_header_line[1] != '\n') { php_stream_close(stream); stream = NULL; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_RESPONSE, "HTTP invalid header name (cannot start with CR character)!"); goto out; } @@ -1002,7 +1012,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (*http_header_line == ' ' || *http_header_line == '\t') { php_stream_close(stream); stream = NULL; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_RESPONSE, "HTTP invalid response format (folding header at the start)!"); goto out; } @@ -1098,7 +1108,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, php_uri_struct_free(resource); /* check for invalid redirection URLs */ if ((resource = php_uri_parse_to_struct(uri_parser, new_path, strlen(new_path), PHP_URI_COMPONENT_READ_MODE_RAW, true)) == NULL) { - php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, + "Invalid redirect URL! %s", new_path); efree(new_path); goto out; } @@ -1110,7 +1121,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, s = (unsigned char*)ZSTR_VAL(val); e = s + ZSTR_LEN(val); \ while (s < e) { \ if (iscntrl(*s)) { \ - php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path); \ + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, \ + "Invalid redirect URL! %s", new_path); \ efree(new_path); \ goto out; \ } \ @@ -1136,7 +1148,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, --redirect_max, new_flags, response_header STREAMS_CC); efree(new_path); } else { - php_stream_wrapper_log_error(wrapper, options, "HTTP request failed! %s", tmp_line); + php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, + "HTTP request failed! %s", tmp_line); } } out: diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h index f36ece142bc06..2d77c17864db5 100644 --- a/main/streams/php_stream_errors.h +++ b/main/streams/php_stream_errors.h @@ -93,8 +93,10 @@ BEGIN_EXTERN_C() #define STREAM_ERROR_CODE_PROTOCOL_ERROR 106 #define STREAM_ERROR_CODE_INVALID_URL 107 #define STREAM_ERROR_CODE_INVALID_RESPONSE 108 -#define STREAM_ERROR_CODE_REDIRECT_LIMIT 109 -#define STREAM_ERROR_CODE_AUTH_FAILED 110 +#define STREAM_ERROR_CODE_INVALID_HEADER 109 +#define STREAM_ERROR_CODE_INVALID_PARAM 110 +#define STREAM_ERROR_CODE_REDIRECT_LIMIT 111 +#define STREAM_ERROR_CODE_AUTH_FAILED 112 /* Encoding/decoding/archiving operations */ #define STREAM_ERROR_CODE_ARCHIVING_FAILED 120 #define STREAM_ERROR_CODE_ENCODING_FAILED 121 diff --git a/main/streams/stream_errors.stub.php b/main/streams/stream_errors.stub.php index a9d090858aff6..7e76865b02a62 100644 --- a/main/streams/stream_errors.stub.php +++ b/main/streams/stream_errors.stub.php @@ -261,6 +261,16 @@ public function getWrapperName(): string {} * @cvalue STREAM_ERROR_CODE_INVALID_RESPONSE */ const STREAM_ERROR_CODE_INVALID_RESPONSE = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_INVALID_HEADER + */ +const STREAM_ERROR_CODE_INVALID_HEADER = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_INVALID_PARAM + */ +const STREAM_ERROR_CODE_INVALID_PARAM = UNKNOWN; /** * @var int * @cvalue STREAM_ERROR_CODE_REDIRECT_LIMIT diff --git a/main/streams/stream_errors_arginfo.h b/main/streams/stream_errors_arginfo.h index ff8da9dab548e..dd2051c0e10df 100644 --- a/main/streams/stream_errors_arginfo.h +++ b/main/streams/stream_errors_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: e3d25328018715348be75f6cb31ae6684afe0a56 */ + * Stub hash: c40a28ed8224ab9fc3673ca4dec0d5376f9acd7e */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_StreamException_getParam, 0, 0, IS_STRING, 1) ZEND_END_ARG_INFO() @@ -68,6 +68,8 @@ static void register_stream_errors_symbols(int module_number) REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_PROTOCOL_ERROR", STREAM_ERROR_CODE_PROTOCOL_ERROR, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_INVALID_URL", STREAM_ERROR_CODE_INVALID_URL, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_INVALID_RESPONSE", STREAM_ERROR_CODE_INVALID_RESPONSE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_INVALID_HEADER", STREAM_ERROR_CODE_INVALID_HEADER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_INVALID_PARAM", STREAM_ERROR_CODE_INVALID_PARAM, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_REDIRECT_LIMIT", STREAM_ERROR_CODE_REDIRECT_LIMIT, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_AUTH_FAILED", STREAM_ERROR_CODE_AUTH_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_ARCHIVING_FAILED", STREAM_ERROR_CODE_ARCHIVING_FAILED, CONST_PERSISTENT); From 53fa92ea755d539942ebc6b4b78756ed103f97b9 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 16 Nov 2025 18:46:22 +0100 Subject: [PATCH 07/37] stream: add context param to php_stream_wrapper_log_* variants --- ext/phar/dirstream.c | 54 +++++++++++++++---------------- ext/phar/stream.c | 48 +++++++++++++-------------- ext/standard/ftp_fopen_wrapper.c | 26 +++++++-------- ext/standard/http_fopen_wrapper.c | 20 ++++++------ main/streams/php_stream_errors.h | 18 ++++++----- main/streams/stream_errors.c | 22 ++++++------- 6 files changed, 93 insertions(+), 95 deletions(-) diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c index 7faeddba25d7d..6ea3f7ba2fb06 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -254,7 +254,7 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, const char *path, phar_archive_data *phar; if ((resource = phar_parse_url(wrapper, path, mode, options)) == NULL) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, "phar url \"%s\" is unknown", path); return NULL; } @@ -262,21 +262,21 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, const char *path, /* we must have at the very least phar://alias.phar/ */ if (!resource->scheme || !resource->host || !resource->path) { if (resource->host && !resource->path) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_PATH, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_PATH, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", path, ZSTR_VAL(resource->host)); php_url_free(resource); return NULL; } php_url_free(resource); - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, "phar error: invalid url \"%s\", must have at least phar://%s/", path, path); return NULL; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, "phar error: not a phar url \"%s\"", path); return NULL; } @@ -285,10 +285,10 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, const char *path, if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error)) { if (error) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NOT_FOUND, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, "%s", error); efree(error); } else { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NOT_FOUND, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, "phar file \"%s\" is unknown", ZSTR_VAL(resource->host)); } php_url_free(resource); @@ -359,7 +359,7 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo /* pre-readonly check, we need to know if this is a data phar */ if (FAILURE == phar_split_fname(url_from, strlen(url_from), &arch, &arch_len, NULL, NULL, 2, 2)) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NOT_FOUND, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, "phar error: cannot create directory \"%s\", no phar archive specified", url_from); return 0; } @@ -371,7 +371,7 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo efree(arch); if (PHAR_G(readonly) && (!phar || !phar->is_data)) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_READONLY, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_READONLY, "phar error: cannot create directory \"%s\", write operations disabled", url_from); return 0; } @@ -383,20 +383,20 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, "phar error: invalid url \"%s\"", url_from); return 0; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, "phar error: not a phar stream url \"%s\"", url_from); return 0; } if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error)) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_MKDIR_FAILED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_MKDIR_FAILED, "phar error: cannot create directory \"%s\" in phar \"%s\", error retrieving phar information: %s", ZSTR_VAL(resource->path) + 1, ZSTR_VAL(resource->host), error); efree(error); @@ -410,7 +410,7 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo zend_string_efree(e->filename); efree(e); } - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_ALREADY_EXISTS, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_ALREADY_EXISTS, "phar error: cannot create directory \"%s\" in phar \"%s\", directory already exists", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); php_url_free(resource); @@ -418,7 +418,7 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo } if (error) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_MKDIR_FAILED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_MKDIR_FAILED, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); @@ -428,7 +428,7 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo if (phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, ZSTR_LEN(resource->path) - 1, 0, &error, true)) { /* entry exists as a file */ - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_ALREADY_EXISTS, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_ALREADY_EXISTS, "phar error: cannot create directory \"%s\" in phar \"%s\", file already exists", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); php_url_free(resource); @@ -436,7 +436,7 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo } if (error) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_MKDIR_FAILED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_MKDIR_FAILED, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); @@ -467,7 +467,7 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo entry.old_flags = PHAR_ENT_PERM_DEF_DIR; if (NULL == zend_hash_add_mem(&phar->manifest, entry.filename, &entry, sizeof(phar_entry_info))) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_MKDIR_FAILED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_MKDIR_FAILED, "phar error: cannot create directory \"%s\" in phar \"%s\", adding to manifest failed", ZSTR_VAL(entry.filename), phar->fname); zend_string_efree(entry.filename); @@ -477,7 +477,7 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo phar_flush(phar, &error); if (error) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_MKDIR_FAILED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_MKDIR_FAILED, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(entry.filename), phar->fname, error); zend_hash_del(&phar->manifest, entry.filename); @@ -503,7 +503,7 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options /* pre-readonly check, we need to know if this is a data phar */ if (FAILURE == phar_split_fname(url, strlen(url), &arch, &arch_len, NULL, NULL, 2, 2)) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NOT_FOUND, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, "phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url); return 0; } @@ -515,7 +515,7 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options efree(arch); if (PHAR_G(readonly) && (!phar || !phar->is_data)) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_READONLY, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_READONLY, "phar error: cannot rmdir directory \"%s\", write operations disabled", url); return 0; } @@ -527,20 +527,20 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, "phar error: invalid url \"%s\"", url); return 0; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, "phar error: not a phar stream url \"%s\"", url); return 0; } if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error)) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_RMDIR_FAILED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_RMDIR_FAILED, "phar error: cannot remove directory \"%s\" in phar \"%s\", error retrieving phar information: %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); @@ -552,12 +552,12 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options if (!(entry = phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, path_len, 2, &error, true))) { if (error) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_RMDIR_FAILED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_RMDIR_FAILED, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); } else { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NOT_FOUND, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, "phar error: cannot remove directory \"%s\" in phar \"%s\", directory does not exist", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); } @@ -573,7 +573,7 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options zend_string_starts_with_cstr(str_key, ZSTR_VAL(resource->path)+1, path_len) && IS_SLASH(ZSTR_VAL(str_key)[path_len]) ) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_RMDIR_FAILED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_RMDIR_FAILED, "phar error: Directory not empty"); if (entry->is_temp_dir) { zend_string_efree(entry->filename); @@ -590,7 +590,7 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options zend_string_starts_with_cstr(str_key, ZSTR_VAL(resource->path)+1, path_len) && IS_SLASH(ZSTR_VAL(str_key)[path_len]) ) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_RMDIR_FAILED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_RMDIR_FAILED, "phar error: Directory not empty"); if (entry->is_temp_dir) { zend_string_efree(entry->filename); @@ -612,7 +612,7 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options phar_flush(phar, &error); if (error) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_RMDIR_FAILED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_RMDIR_FAILED, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(entry->filename), phar->fname, error); php_url_free(resource); diff --git a/ext/phar/stream.c b/ext/phar/stream.c index faae9b3d325a8..3d11d3ad76bc8 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -68,7 +68,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const } if (mode[0] == 'a') { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, + php_stream_wrapper_log_warn(wrapper, NULL, options, STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, "phar error: open mode append not supported"); } return NULL; @@ -76,12 +76,12 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const if (phar_split_fname(filename, strlen(filename), &arch, &arch_len, &entry, &entry_len, 2, (mode[0] == 'w' ? 2 : 0)) == FAILURE) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { if (arch && !entry) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_PATH, + php_stream_wrapper_log_warn(wrapper, NULL, options, STREAM_ERROR_CODE_INVALID_PATH, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", filename, arch); arch = NULL; } else { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, + php_stream_wrapper_log_warn(wrapper, NULL, options, STREAM_ERROR_CODE_INVALID_URL, "phar error: invalid url or non-existent phar \"%s\"", filename); } } @@ -115,7 +115,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const } if (PHAR_G(readonly) && (!pphar || !pphar->is_data)) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_READONLY, + php_stream_wrapper_log_warn(wrapper, NULL, options, STREAM_ERROR_CODE_READONLY, "phar error: write operations disabled by the php.ini setting phar.readonly"); } php_url_free(resource); @@ -125,7 +125,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const { if (error) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_OPEN_FAILED, "%s", error); + php_stream_wrapper_log_warn(wrapper, NULL, options, STREAM_ERROR_CODE_OPEN_FAILED, "%s", error); } efree(error); } @@ -136,7 +136,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const if (error) { spprintf(&error, 0, "Cannot open cached phar '%s' as writeable, copy on write failed", ZSTR_VAL(resource->host)); if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_OPEN_FAILED, "%s", error); + php_stream_wrapper_log_warn(wrapper, NULL, options, STREAM_ERROR_CODE_OPEN_FAILED, "%s", error); } efree(error); } @@ -148,7 +148,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const { if (error) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NOT_FOUND, "%s", error); + php_stream_wrapper_log_warn(wrapper, NULL, options, STREAM_ERROR_CODE_NOT_FOUND, "%s", error); } efree(error); } @@ -181,14 +181,14 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, "phar error: invalid url \"%s\"", path); return NULL; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, "phar error: not a phar stream url \"%s\"", path); return NULL; } @@ -200,10 +200,10 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) { if (NULL == (idata = phar_get_or_create_entry_data(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), internal_file, strlen(internal_file), mode, 0, &error, true))) { if (error) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_CREATE_FAILED, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_CREATE_FAILED, "%s", error); efree(error); } else { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_CREATE_FAILED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_CREATE_FAILED, "phar error: file \"%s\" could not be created in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); } efree(internal_file); @@ -244,7 +244,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha if (!*internal_file && (options & STREAM_OPEN_FOR_INCLUDE)) { /* retrieve the stub */ if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, NULL)) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_FORMAT, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_FORMAT, "file %s is not a valid phar archive", ZSTR_VAL(resource->host)); efree(internal_file); php_url_free(resource); @@ -264,7 +264,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha php_stream *stream = phar_get_pharfp(phar); if (stream == NULL) { if (UNEXPECTED(FAILURE == phar_open_archive_fp(phar))) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_OPEN_FAILED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_OPEN_FAILED, "phar error: could not reopen phar \"%s\"", ZSTR_VAL(resource->host)); efree(internal_file); php_url_free(resource); @@ -303,10 +303,10 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha if ((FAILURE == phar_get_entry_data(&idata, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), internal_file, strlen(internal_file), "r", 0, &error, false)) || !idata) { idata_error: if (error) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NOT_FOUND, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, "%s", error); efree(error); } else { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NOT_FOUND, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); } efree(internal_file); @@ -325,7 +325,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha /* check length, crc32 */ if (!idata->internal_file->is_crc_checked && phar_postprocess_file(idata, idata->internal_file->crc32, &error, 2) != SUCCESS) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_ARCHIVING_FAILED, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_ARCHIVING_FAILED, "%s", error); efree(error); phar_entry_delref(idata); efree(internal_file); @@ -676,7 +676,7 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int phar_archive_data *pphar; if ((resource = phar_parse_url(wrapper, url, "rb", options)) == NULL) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_UNLINK_FAILED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_UNLINK_FAILED, "phar error: unlink failed"); return 0; } @@ -684,14 +684,14 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, "phar error: invalid url \"%s\"", url); return 0; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, "phar error: not a phar stream url \"%s\"", url); return 0; } @@ -701,7 +701,7 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int pphar = zend_hash_find_ptr(&(PHAR_G(phar_fname_map)), resource->host); if (PHAR_G(readonly) && (!pphar || !pphar->is_data)) { php_url_free(resource); - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_READONLY, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_READONLY, "phar error: write operations disabled by the php.ini setting phar.readonly"); return 0; } @@ -712,11 +712,11 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int if (FAILURE == phar_get_entry_data(&idata, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), internal_file, internal_file_len, "r", 0, &error, true)) { /* constraints of fp refcount were not met */ if (error) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_UNLINK_FAILED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_UNLINK_FAILED, "unlink of \"%s\" failed: %s", url, error); efree(error); } else { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NOT_FOUND, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, "unlink of \"%s\" failed, file does not exist", url); } efree(internal_file); @@ -728,7 +728,7 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int } if (idata->internal_file->fp_refcount > 1) { /* more than just our fp resource is open for this file */ - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_UNLINK_FAILED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_UNLINK_FAILED, "phar error: \"%s\" in phar \"%s\", has open file pointers, cannot unlink", internal_file, ZSTR_VAL(resource->host)); efree(internal_file); @@ -740,7 +740,7 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int efree(internal_file); phar_entry_remove(idata, &error); if (error) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_UNLINK_FAILED, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_UNLINK_FAILED, "%s", error); efree(error); } return 1; diff --git a/ext/standard/ftp_fopen_wrapper.c b/ext/standard/ftp_fopen_wrapper.c index 6f6550b6ac8f7..3e3b74bb2a2e7 100644 --- a/ext/standard/ftp_fopen_wrapper.c +++ b/ext/standard/ftp_fopen_wrapper.c @@ -189,7 +189,7 @@ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char /* get the response */ result = GET_FTP_RESULT(stream); if (result != 334) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, "Server doesn't support FTPS."); goto connect_errexit; } else { @@ -209,7 +209,7 @@ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(stream, 1) < 0) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, "Unable to activate SSL mode"); php_stream_close(stream); stream = NULL; @@ -241,7 +241,7 @@ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char unsigned char *s = (unsigned char *) val, *e = (unsigned char *) s + val_len; \ while (s < e) { \ if (iscntrl(*s)) { \ - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_AUTH_FAILED, err_msg, val); \ + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_AUTH_FAILED, err_msg, val); \ goto connect_errexit; \ } \ s++; \ @@ -438,7 +438,7 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa } if (strpbrk(mode, "wa+")) { if (read_write) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, "FTP does not support simultaneous read/write connections"); return NULL; } @@ -450,7 +450,7 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa } if (!read_write) { /* No mode specified? */ - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_MODE, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_MODE, "Unknown file open mode"); return NULL; } @@ -462,7 +462,7 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa return php_stream_url_wrap_http(wrapper, path, mode, options, opened_path, context STREAMS_CC); } else { /* ftp proxy is read-only */ - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, "FTP proxy may only be used in read mode"); return NULL; } @@ -515,7 +515,7 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa goto errexit; } } else { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_ALREADY_EXISTS, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_ALREADY_EXISTS, "Remote file already exists and overwrite context option not specified"); errno = EEXIST; goto errexit; @@ -540,7 +540,7 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa php_stream_printf(stream, "REST " ZEND_LONG_FMT "\r\n", Z_LVAL_P(tmpzval)); result = GET_FTP_RESULT(stream); if (result < 300 || result > 399) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_RESUMPTION_FAILED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_RESUMPTION_FAILED, "Unable to resume from offset " ZEND_LONG_FMT, Z_LVAL_P(tmpzval)); goto errexit; } @@ -586,7 +586,7 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(datastream, 1) < 0)) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, "Unable to activate SSL mode"); php_stream_close(datastream); datastream = NULL; @@ -609,11 +609,11 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa php_stream_close(stream); } if (tmp_line[0] != '\0') - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, "FTP server reports %s", tmp_line); if (error_message) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_NETWORK_SEND_FAILED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NETWORK_SEND_FAILED, "Failed to set up data channel: %s", ZSTR_VAL(error_message)); zend_string_release(error_message); } @@ -759,7 +759,7 @@ static php_stream * php_stream_ftp_opendir(php_stream_wrapper *wrapper, const ch STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(datastream, 1) < 0)) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, "Unable to activate SSL mode"); php_stream_close(datastream); datastream = NULL; @@ -784,7 +784,7 @@ static php_stream * php_stream_ftp_opendir(php_stream_wrapper *wrapper, const ch php_stream_close(stream); } if (tmp_line[0] != '\0') { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, "FTP server reports %s", tmp_line); } return NULL; diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index 9f206a02c272d..b772fb0364a6f 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -246,7 +246,7 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w while (last_header_name < last_header_value) { if (*last_header_name == ' ' || *last_header_name == '\t') { header_info->error = true; - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_RESPONSE, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_RESPONSE, "HTTP invalid response format (space in header name)!"); zend_string_efree(last_header_line_str); return NULL; @@ -264,7 +264,7 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w } else { /* There is no colon which means invalid response so error. */ header_info->error = true; - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_RESPONSE, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_RESPONSE, "HTTP invalid response format (no colon in header line)!"); zend_string_efree(last_header_line_str); return NULL; @@ -389,7 +389,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, tmp_line[0] = '\0'; if (redirect_max < 1) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_REDIRECT_LIMIT, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_REDIRECT_LIMIT, "Redirection limit reached, aborting"); return NULL; } @@ -423,7 +423,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, /* Normal http request (possibly with proxy) */ if (strpbrk(mode, "awx+")) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, "HTTP wrapper does not support writeable connections"); php_uri_struct_free(resource); return NULL; @@ -453,7 +453,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } if (request_fulluri && (strchr(path, '\n') != NULL || strchr(path, '\r') != NULL)) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, "HTTP wrapper full URI path does not allow CR or LF characters"); php_uri_struct_free(resource); zend_string_release(transport_string); @@ -552,7 +552,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, smart_str_appendl(&header, "\r\n", sizeof("\r\n")-1); if (php_stream_write(stream, ZSTR_VAL(header.s), ZSTR_LEN(header.s)) != ZSTR_LEN(header.s)) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, "Cannot connect to HTTPS server through proxy"); php_stream_close(stream); stream = NULL; @@ -576,7 +576,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (stream) { if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(stream, 1) < 0) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, "Cannot connect to HTTPS server through proxy"); php_stream_close(stream); stream = NULL; @@ -962,7 +962,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } else { php_stream_close(stream); stream = NULL; - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, "HTTP request failed!"); goto out; } @@ -981,7 +981,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (http_header_line[1] != '\n') { php_stream_close(stream); stream = NULL; - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_RESPONSE, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_RESPONSE, "HTTP invalid header name (cannot start with CR character)!"); goto out; } @@ -1012,7 +1012,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (*http_header_line == ' ' || *http_header_line == '\t') { php_stream_close(stream); stream = NULL; - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_RESPONSE, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_RESPONSE, "HTTP invalid response format (folding header at the start)!"); goto out; } diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h index 2d77c17864db5..1ed4257bf47e8 100644 --- a/main/streams/php_stream_errors.h +++ b/main/streams/php_stream_errors.h @@ -170,16 +170,18 @@ PHPAPI void php_stream_error( /* Legacy wrapper error log - updated API */ PHPAPI void php_stream_wrapper_log_error( const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminal, int code, const char *fmt, ... -) ZEND_ATTRIBUTE_FORMAT(printf, 6, 7); +) ZEND_ATTRIBUTE_FORMAT(printf, 7, 8); PHPAPI void php_stream_wrapper_log_error_param( const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminal, @@ -187,7 +189,7 @@ PHPAPI void php_stream_wrapper_log_error_param( const char *param, const char *fmt, ... -) ZEND_ATTRIBUTE_FORMAT(printf, 7, 8); +) ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); PHPAPI void php_stream_display_wrapper_errors( php_stream_wrapper *wrapper, @@ -226,14 +228,14 @@ void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper); php_stream_error(stream, E_ERROR, true, code, __VA_ARGS__) /* Legacy log variants */ -#define php_stream_wrapper_log_warn(wrapper, options, code, ...) \ - php_stream_wrapper_log_error(wrapper, options, E_WARNING, true, code, __VA_ARGS__) +#define php_stream_wrapper_log_warn(wrapper, context, options, code, ...) \ + php_stream_wrapper_log_error(wrapper, context, options, E_WARNING, true, code, __VA_ARGS__) -#define php_stream_wrapper_log_warn_nt(wrapper, options, code, ...) \ - php_stream_wrapper_log_error(wrapper, options, E_WARNING, false, code, __VA_ARGS__) +#define php_stream_wrapper_log_warn_nt(wrapper, context, options, code, ...) \ + php_stream_wrapper_log_error(wrapper, context, options, E_WARNING, false, code, __VA_ARGS__) -#define php_stream_wrapper_log_notice(wrapper, options, code, ...) \ - php_stream_wrapper_log_error(wrapper, options, E_NOTICE, false, code, __VA_ARGS__) +#define php_stream_wrapper_log_notice(wrapper, context, options, code, ...) \ + php_stream_wrapper_log_error(wrapper, context, options, E_NOTICE, false, code, __VA_ARGS__) END_EXTERN_C() diff --git a/main/streams/stream_errors.c b/main/streams/stream_errors.c index 52428e9065f34..74e3af9a7a1c0 100644 --- a/main/streams/stream_errors.c +++ b/main/streams/stream_errors.c @@ -359,19 +359,15 @@ static void php_stream_wrapper_log_store_error(const php_stream_wrapper *wrapper zend_llist_add_element(list, &entry); } -static void php_stream_wrapper_log_error_internal(const php_stream_wrapper *wrapper, int options, - int severity, bool terminal, int code, const char *param, va_list args) +static void php_stream_wrapper_log_error_internal(const php_stream_wrapper *wrapper, php_stream_context *context, + int options, int severity, bool terminal, int code, const char *param, const char *fmt, va_list args) { zend_string *message = vstrpprintf(0, args, args); const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown"; if (options & REPORT_ERRORS) { /* Report immediately using standard error functions */ - if (param) { - php_error_docref1(NULL, param, severity, "%s", ZSTR_VAL(message)); - } else { - php_error_docref(NULL, severity, "%s", ZSTR_VAL(message)); - } + php_stream_wrapper_error_internal(wrapper, context, options, severity, terminal, code, param, fmt, args); zend_string_release(message); } else { /* Store for later display in FG(wrapper_logged_errors) */ @@ -381,21 +377,21 @@ static void php_stream_wrapper_log_error_internal(const php_stream_wrapper *wrap } } -PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, int options, - int severity, bool terminal, int code, const char *fmt, ...) +PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, php_stream_context *context, + int options, int severity, bool terminal, int code, const char *fmt, ...) { va_list args; va_start(args, fmt); - php_stream_wrapper_log_error_internal(wrapper, options, severity, terminal, code, NULL, args); + php_stream_wrapper_log_error_internal(wrapper, context, options, severity, terminal, code, NULL, fmt, args); va_end(args); } -PHPAPI void php_stream_wrapper_log_error_param(const php_stream_wrapper *wrapper, int options, - int severity, bool terminal, int code, const char *param, const char *fmt, ...) +PHPAPI void php_stream_wrapper_log_error_param(const php_stream_wrapper *wrapper, php_stream_context *context, + int options, int severity, bool terminal, int code, const char *param, const char *fmt, ...) { va_list args; va_start(args, fmt); - php_stream_wrapper_log_error_internal(wrapper, options, severity, terminal, code, param, args); + php_stream_wrapper_log_error_internal(wrapper, context, options, severity, terminal, code, param, fmt, args); va_end(args); } From 0f261b2bbaf5f61bc8db7b61ab10c0504e9e81e1 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 16 Nov 2025 19:06:55 +0100 Subject: [PATCH 08/37] stream: convert php fopen wrapper --- ext/standard/php_fopen_wrapper.c | 37 +++++++--- main/streams/php_stream_errors.h | 106 ++++++++++++++------------- main/streams/stream_errors.stub.php | 10 +++ main/streams/stream_errors_arginfo.h | 4 +- 4 files changed, 94 insertions(+), 63 deletions(-) diff --git a/ext/standard/php_fopen_wrapper.c b/ext/standard/php_fopen_wrapper.c index ea33ba4904346..b13f18e08bf78 100644 --- a/ext/standard/php_fopen_wrapper.c +++ b/ext/standard/php_fopen_wrapper.c @@ -158,14 +158,18 @@ static void php_stream_apply_filter_list(php_stream *stream, char *filterlist, i if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream)))) { php_stream_filter_append(&stream->readfilters, temp_filter); } else { - php_error_docref(NULL, E_WARNING, "Unable to create filter (%s)", p); + php_stream_wrapper_warn_nt(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + STREAM_ERROR_CODE_CREATE_FAILED, + "Unable to create filter (%s)", p); } } if (write_chain) { if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream)))) { php_stream_filter_append(&stream->writefilters, temp_filter); } else { - php_error_docref(NULL, E_WARNING, "Unable to create filter (%s)", p); + php_stream_wrapper_warn_nt(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + STREAM_ERROR_CODE_CREATE_FAILED, + "Unable to create filter (%s)", p); } } p = php_strtok_r(NULL, "|", &token); @@ -219,7 +223,9 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "URL file-access is disabled in the server configuration"); + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_DISABLED, + "URL file-access is disabled in the server configuration"); } return NULL; } @@ -238,7 +244,9 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c if (!strcasecmp(path, "stdin")) { if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "URL file-access is disabled in the server configuration"); + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_DISABLED, + "URL file-access is disabled in the server configuration"); } return NULL; } @@ -297,14 +305,18 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c if (strcmp(sapi_module.name, "cli")) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Direct access to file descriptors is only available from command-line PHP"); + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_DISABLED, + "Direct access to file descriptors is only available from command-line PHP"); } return NULL; } if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "URL file-access is disabled in the server configuration"); + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_DISABLED, + "URL file-access is disabled in the server configuration"); } return NULL; } @@ -312,7 +324,8 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c start = &path[3]; fildes_ori = ZEND_STRTOL(start, &end, 10); if (end == start || *end != '\0') { - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_INVALID_URL, "php://fd/ stream must be specified in the form php://fd/"); return NULL; } @@ -324,14 +337,16 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c #endif if (fildes_ori < 0 || fildes_ori >= dtablesize) { - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_INVALID_PARAM, "The file descriptors must be non-negative numbers smaller than %d", dtablesize); return NULL; } fd = dup((int)fildes_ori); if (fd == -1) { - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_DUP_FAILED, "Error duping file descriptor " ZEND_LONG_FMT "; possibly it doesn't exist: " "[%d]: %s", fildes_ori, errno, strerror(errno)); return NULL; @@ -380,7 +395,9 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c return stream; } else { /* invalid php://thingy */ - php_error_docref(NULL, E_WARNING, "Invalid php:// URL specified"); + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_INVALID_URL, + "Invalid php:// URL specified"); return NULL; } diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h index 1ed4257bf47e8..1841ad5ceaf7a 100644 --- a/main/streams/php_stream_errors.h +++ b/main/streams/php_stream_errors.h @@ -49,67 +49,69 @@ BEGIN_EXTERN_C() #define STREAM_ERROR_CODE_NOT_WRITABLE 16 #define STREAM_ERROR_CODE_NOT_READABLE 17 /* File system operations */ -#define STREAM_ERROR_CODE_NOT_FOUND 30 -#define STREAM_ERROR_CODE_PERMISSION_DENIED 31 -#define STREAM_ERROR_CODE_ALREADY_EXISTS 32 -#define STREAM_ERROR_CODE_INVALID_PATH 33 -#define STREAM_ERROR_CODE_PATH_TOO_LONG 34 -#define STREAM_ERROR_CODE_OPEN_FAILED 35 -#define STREAM_ERROR_CODE_CREATE_FAILED 36 -#define STREAM_ERROR_CODE_UNLINK_FAILED 37 -#define STREAM_ERROR_CODE_RENAME_FAILED 38 -#define STREAM_ERROR_CODE_MKDIR_FAILED 39 -#define STREAM_ERROR_CODE_RMDIR_FAILED 40 -#define STREAM_ERROR_CODE_STAT_FAILED 41 -#define STREAM_ERROR_CODE_CHMOD_FAILED 42 -#define STREAM_ERROR_CODE_CHOWN_FAILED 43 -#define STREAM_ERROR_CODE_TOUCH_FAILED 44 -#define STREAM_ERROR_CODE_INVALID_MODE 45 -#define STREAM_ERROR_CODE_MODE_NOT_SUPPORTED 46 -#define STREAM_ERROR_CODE_READONLY 47 -#define STREAM_ERROR_CODE_RECURSION_DETECTED 48 +#define STREAM_ERROR_CODE_DISABLED 30 +#define STREAM_ERROR_CODE_NOT_FOUND 31 +#define STREAM_ERROR_CODE_PERMISSION_DENIED 32 +#define STREAM_ERROR_CODE_ALREADY_EXISTS 33 +#define STREAM_ERROR_CODE_INVALID_PATH 34 +#define STREAM_ERROR_CODE_PATH_TOO_LONG 35 +#define STREAM_ERROR_CODE_OPEN_FAILED 36 +#define STREAM_ERROR_CODE_CREATE_FAILED 37 +#define STREAM_ERROR_CODE_DUP_FAILED 38 +#define STREAM_ERROR_CODE_UNLINK_FAILED 39 +#define STREAM_ERROR_CODE_RENAME_FAILED 40 +#define STREAM_ERROR_CODE_MKDIR_FAILED 41 +#define STREAM_ERROR_CODE_RMDIR_FAILED 42 +#define STREAM_ERROR_CODE_STAT_FAILED 43 +#define STREAM_ERROR_CODE_CHMOD_FAILED 44 +#define STREAM_ERROR_CODE_CHOWN_FAILED 45 +#define STREAM_ERROR_CODE_TOUCH_FAILED 46 +#define STREAM_ERROR_CODE_INVALID_MODE 47 +#define STREAM_ERROR_CODE_MODE_NOT_SUPPORTED 48 +#define STREAM_ERROR_CODE_READONLY 49 +#define STREAM_ERROR_CODE_RECURSION_DETECTED 50 /* Wrapper/protocol operations */ -#define STREAM_ERROR_CODE_WRAPPER_NOT_FOUND 60 -#define STREAM_ERROR_CODE_WRAPPER_DISABLED 61 -#define STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED 62 -#define STREAM_ERROR_CODE_WRAPPER_REGISTRATION_FAILED 63 -#define STREAM_ERROR_CODE_WRAPPER_UNREGISTRATION_FAILED 64 -#define STREAM_ERROR_CODE_WRAPPER_RESTORATION_FAILED 65 +#define STREAM_ERROR_CODE_WRAPPER_NOT_FOUND 70 +#define STREAM_ERROR_CODE_WRAPPER_DISABLED 71 +#define STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED 72 +#define STREAM_ERROR_CODE_WRAPPER_REGISTRATION_FAILED 73 +#define STREAM_ERROR_CODE_WRAPPER_UNREGISTRATION_FAILED 74 +#define STREAM_ERROR_CODE_WRAPPER_RESTORATION_FAILED 75 /* Filter operations */ -#define STREAM_ERROR_CODE_FILTER_NOT_FOUND 80 -#define STREAM_ERROR_CODE_FILTER_FAILED 81 +#define STREAM_ERROR_CODE_FILTER_NOT_FOUND 90 +#define STREAM_ERROR_CODE_FILTER_FAILED 91 /* Cast/conversion operations */ -#define STREAM_ERROR_CODE_CAST_FAILED 90 -#define STREAM_ERROR_CODE_CAST_NOT_SUPPORTED 91 -#define STREAM_ERROR_CODE_MAKE_SEEKABLE_FAILED 92 -#define STREAM_ERROR_CODE_BUFFERED_DATA_LOST 93 +#define STREAM_ERROR_CODE_CAST_FAILED 100 +#define STREAM_ERROR_CODE_CAST_NOT_SUPPORTED 101 +#define STREAM_ERROR_CODE_MAKE_SEEKABLE_FAILED 102 +#define STREAM_ERROR_CODE_BUFFERED_DATA_LOST 103 /* Network/socket operations */ -#define STREAM_ERROR_CODE_NETWORK_SEND_FAILED 100 -#define STREAM_ERROR_CODE_NETWORK_RECV_FAILED 101 -#define STREAM_ERROR_CODE_SSL_NOT_SUPPORTED 102 -#define STREAM_ERROR_CODE_RESUMPTION_FAILED 103 -#define STREAM_ERROR_CODE_SOCKET_PATH_TOO_LONG 104 -#define STREAM_ERROR_CODE_OOB_NOT_SUPPORTED 105 -#define STREAM_ERROR_CODE_PROTOCOL_ERROR 106 -#define STREAM_ERROR_CODE_INVALID_URL 107 -#define STREAM_ERROR_CODE_INVALID_RESPONSE 108 -#define STREAM_ERROR_CODE_INVALID_HEADER 109 +#define STREAM_ERROR_CODE_NETWORK_SEND_FAILED 110 +#define STREAM_ERROR_CODE_NETWORK_RECV_FAILED 111 +#define STREAM_ERROR_CODE_SSL_NOT_SUPPORTED 112 +#define STREAM_ERROR_CODE_RESUMPTION_FAILED 113 +#define STREAM_ERROR_CODE_SOCKET_PATH_TOO_LONG 114 +#define STREAM_ERROR_CODE_OOB_NOT_SUPPORTED 115 +#define STREAM_ERROR_CODE_PROTOCOL_ERROR 116 +#define STREAM_ERROR_CODE_INVALID_URL 117 +#define STREAM_ERROR_CODE_INVALID_RESPONSE 118 +#define STREAM_ERROR_CODE_INVALID_HEADER 119 #define STREAM_ERROR_CODE_INVALID_PARAM 110 -#define STREAM_ERROR_CODE_REDIRECT_LIMIT 111 -#define STREAM_ERROR_CODE_AUTH_FAILED 112 +#define STREAM_ERROR_CODE_REDIRECT_LIMIT 121 +#define STREAM_ERROR_CODE_AUTH_FAILED 122 /* Encoding/decoding/archiving operations */ -#define STREAM_ERROR_CODE_ARCHIVING_FAILED 120 -#define STREAM_ERROR_CODE_ENCODING_FAILED 121 -#define STREAM_ERROR_CODE_INVALID_FORMAT 122 +#define STREAM_ERROR_CODE_ARCHIVING_FAILED 130 +#define STREAM_ERROR_CODE_ENCODING_FAILED 131 +#define STREAM_ERROR_CODE_INVALID_FORMAT 132 /* Resource/allocation operations */ -#define STREAM_ERROR_CODE_ALLOCATION_FAILED 130 -#define STREAM_ERROR_CODE_TEMPORARY_FILE_FAILED 131 +#define STREAM_ERROR_CODE_ALLOCATION_FAILED 140 +#define STREAM_ERROR_CODE_TEMPORARY_FILE_FAILED 141 /* Locking operations */ -#define STREAM_ERROR_CODE_LOCK_FAILED 140 -#define STREAM_ERROR_CODE_LOCK_NOT_SUPPORTED 141 +#define STREAM_ERROR_CODE_LOCK_FAILED 150 +#define STREAM_ERROR_CODE_LOCK_NOT_SUPPORTED 151 /* Userspace stream operations */ -#define STREAM_ERROR_CODE_USERSPACE_NOT_IMPLEMENTED 150 -#define STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN 151 +#define STREAM_ERROR_CODE_USERSPACE_NOT_IMPLEMENTED 160 +#define STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN 161 /* Stored error entry */ typedef struct { diff --git a/main/streams/stream_errors.stub.php b/main/streams/stream_errors.stub.php index 7e76865b02a62..7bec3e68db235 100644 --- a/main/streams/stream_errors.stub.php +++ b/main/streams/stream_errors.stub.php @@ -61,6 +61,11 @@ public function getWrapperName(): string {} * @cvalue STREAM_ERROR_CODE_NOT_READABLE */ const STREAM_ERROR_CODE_NOT_READABLE = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_DISABLED + */ +const STREAM_ERROR_CODE_DISABLED = UNKNOWN; /** * @var int * @cvalue STREAM_ERROR_CODE_NOT_FOUND @@ -91,6 +96,11 @@ public function getWrapperName(): string {} * @cvalue STREAM_ERROR_CODE_CREATE_FAILED */ const STREAM_ERROR_CODE_CREATE_FAILED = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_DUP_FAILED + */ +const STREAM_ERROR_CODE_DUP_FAILED = UNKNOWN; /** * @var int * @cvalue STREAM_ERROR_CODE_OPEN_FAILED diff --git a/main/streams/stream_errors_arginfo.h b/main/streams/stream_errors_arginfo.h index dd2051c0e10df..dca72e8b8375c 100644 --- a/main/streams/stream_errors_arginfo.h +++ b/main/streams/stream_errors_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: c40a28ed8224ab9fc3673ca4dec0d5376f9acd7e */ + * Stub hash: ea73ead3bd777bd305d626af87d75dfc21140f97 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_StreamException_getParam, 0, 0, IS_STRING, 1) ZEND_END_ARG_INFO() @@ -28,12 +28,14 @@ static void register_stream_errors_symbols(int module_number) REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_TRUNCATE_FAILED", STREAM_ERROR_CODE_TRUNCATE_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_NOT_WRITABLE", STREAM_ERROR_CODE_NOT_WRITABLE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_NOT_READABLE", STREAM_ERROR_CODE_NOT_READABLE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_DISABLED", STREAM_ERROR_CODE_DISABLED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_NOT_FOUND", STREAM_ERROR_CODE_NOT_FOUND, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_PERMISSION_DENIED", STREAM_ERROR_CODE_PERMISSION_DENIED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_ALREADY_EXISTS", STREAM_ERROR_CODE_ALREADY_EXISTS, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_INVALID_PATH", STREAM_ERROR_CODE_INVALID_PATH, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_PATH_TOO_LONG", STREAM_ERROR_CODE_PATH_TOO_LONG, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_CREATE_FAILED", STREAM_ERROR_CODE_CREATE_FAILED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_DUP_FAILED", STREAM_ERROR_CODE_DUP_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_OPEN_FAILED", STREAM_ERROR_CODE_OPEN_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_UNLINK_FAILED", STREAM_ERROR_CODE_UNLINK_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_RENAME_FAILED", STREAM_ERROR_CODE_RENAME_FAILED, CONST_PERSISTENT); From d57eb7d1633fbf6faf6c1cc76d62ffe34a487281 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 16 Nov 2025 19:22:05 +0100 Subject: [PATCH 09/37] stream: convert memory wrappers errors --- main/streams/memory.c | 25 +++++++++++++++++-------- main/streams/php_stream_errors.h | 3 ++- main/streams/stream_errors.stub.php | 5 +++++ main/streams/stream_errors_arginfo.h | 3 ++- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/main/streams/memory.c b/main/streams/memory.c index 785109db6582c..1b0bad3410857 100644 --- a/main/streams/memory.c +++ b/main/streams/memory.c @@ -362,7 +362,9 @@ static ssize_t php_stream_temp_write(php_stream *stream, const char *buf, size_t zend_string *membuf = php_stream_memory_get_buffer(ts->innerstream); php_stream *file = php_stream_fopen_temporary_file(ts->tmpdir, "php", NULL); if (file == NULL) { - php_error_docref(NULL, E_WARNING, "Unable to create temporary file, Check permissions in temporary files directory."); + php_stream_wrapper_warn(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + STREAM_ERROR_CODE_PERMISSION_DENIED, + "Unable to create temporary file, Check permissions in temporary files directory."); return 0; } php_stream_write(file, ZSTR_VAL(membuf), ZSTR_LEN(membuf)); @@ -489,7 +491,8 @@ static int php_stream_temp_cast(php_stream *stream, int castas, void **ret) file = php_stream_fopen_tmpfile(); if (file == NULL) { - php_error_docref(NULL, E_WARNING, "Unable to create temporary file."); + php_stream_wrapper_warn(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + STREAM_ERROR_CODE_PERMISSION_DENIED, "Unable to create temporary file."); return FAILURE; } @@ -638,7 +641,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con } if ((comma = memchr(path, ',', dlen)) == NULL) { - php_stream_wrapper_log_error(wrapper, options, "rfc2397: no comma in URL"); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_INVALID_URL, "rfc2397: no comma in URL"); return NULL; } @@ -650,7 +654,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con sep = memchr(path, '/', mlen); if (!semi && !sep) { - php_stream_wrapper_log_error(wrapper, options, "rfc2397: illegal media type"); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_INVALID_URL, "rfc2397: illegal media type"); return NULL; } @@ -665,7 +670,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con path += plen; } else if (semi != path || mlen != sizeof(";base64")-1 || memcmp(path, ";base64", sizeof(";base64")-1)) { /* must be error since parameters are only allowed after mediatype */ zval_ptr_dtor(&meta); - php_stream_wrapper_log_error(wrapper, options, "rfc2397: illegal media type"); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_INVALID_URL, "rfc2397: illegal media type"); return NULL; } /* get parameters and potentially ';base64' */ @@ -678,7 +684,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con if (mlen != sizeof("base64")-1 || memcmp(path, "base64", sizeof("base64")-1)) { /* must be error since parameters are only allowed after mediatype and we have no '=' sign */ zval_ptr_dtor(&meta); - php_stream_wrapper_log_error(wrapper, options, "rfc2397: illegal parameter"); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_INVALID_PARAM, "rfc2397: illegal parameter"); return NULL; } base64 = 1; @@ -698,7 +705,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con } if (mlen) { zval_ptr_dtor(&meta); - php_stream_wrapper_log_error(wrapper, options, "rfc2397: illegal URL"); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_INVALID_URL, "rfc2397: illegal URL"); return NULL; } } else { @@ -714,7 +722,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con base64_comma = php_base64_decode_ex((const unsigned char *)comma, dlen, 1); if (!base64_comma) { zval_ptr_dtor(&meta); - php_stream_wrapper_log_error(wrapper, options, "rfc2397: unable to decode"); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_DECODING_FAILED, "rfc2397: unable to decode"); return NULL; } comma = ZSTR_VAL(base64_comma); diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h index 1841ad5ceaf7a..c88f4b5113002 100644 --- a/main/streams/php_stream_errors.h +++ b/main/streams/php_stream_errors.h @@ -102,7 +102,8 @@ BEGIN_EXTERN_C() /* Encoding/decoding/archiving operations */ #define STREAM_ERROR_CODE_ARCHIVING_FAILED 130 #define STREAM_ERROR_CODE_ENCODING_FAILED 131 -#define STREAM_ERROR_CODE_INVALID_FORMAT 132 +#define STREAM_ERROR_CODE_DECODING_FAILED 132 +#define STREAM_ERROR_CODE_INVALID_FORMAT 133 /* Resource/allocation operations */ #define STREAM_ERROR_CODE_ALLOCATION_FAILED 140 #define STREAM_ERROR_CODE_TEMPORARY_FILE_FAILED 141 diff --git a/main/streams/stream_errors.stub.php b/main/streams/stream_errors.stub.php index 7bec3e68db235..c55a4abfefa56 100644 --- a/main/streams/stream_errors.stub.php +++ b/main/streams/stream_errors.stub.php @@ -301,6 +301,11 @@ public function getWrapperName(): string {} * @cvalue STREAM_ERROR_CODE_ENCODING_FAILED */ const STREAM_ERROR_CODE_ENCODING_FAILED = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_DECODING_FAILED + */ +const STREAM_ERROR_CODE_DECODING_FAILED = UNKNOWN; /** * @var int * @cvalue STREAM_ERROR_CODE_INVALID_FORMAT diff --git a/main/streams/stream_errors_arginfo.h b/main/streams/stream_errors_arginfo.h index dca72e8b8375c..c51b1080c998a 100644 --- a/main/streams/stream_errors_arginfo.h +++ b/main/streams/stream_errors_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: ea73ead3bd777bd305d626af87d75dfc21140f97 */ + * Stub hash: e19e8ac3e877b2f3a8a2b2ec0033c57c356daffa */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_StreamException_getParam, 0, 0, IS_STRING, 1) ZEND_END_ARG_INFO() @@ -76,6 +76,7 @@ static void register_stream_errors_symbols(int module_number) REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_AUTH_FAILED", STREAM_ERROR_CODE_AUTH_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_ARCHIVING_FAILED", STREAM_ERROR_CODE_ARCHIVING_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_ENCODING_FAILED", STREAM_ERROR_CODE_ENCODING_FAILED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_DECODING_FAILED", STREAM_ERROR_CODE_DECODING_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_INVALID_FORMAT", STREAM_ERROR_CODE_INVALID_FORMAT, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_ALLOCATION_FAILED", STREAM_ERROR_CODE_ALLOCATION_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_TEMPORARY_FILE_FAILED", STREAM_ERROR_CODE_TEMPORARY_FILE_FAILED, CONST_PERSISTENT); From c051ba717b0df33b7ee72eaa5cb4d7c76fcbc833 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 16 Nov 2025 22:10:26 +0100 Subject: [PATCH 10/37] stream: convert plain wrappers errors --- main/streams/php_stream_errors.h | 25 +++++--- main/streams/plain_wrapper.c | 93 +++++++++++++++++++--------- main/streams/stream_errors.stub.php | 10 +++ main/streams/stream_errors_arginfo.h | 4 +- 4 files changed, 94 insertions(+), 38 deletions(-) diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h index c88f4b5113002..7f57a684b8250 100644 --- a/main/streams/php_stream_errors.h +++ b/main/streams/php_stream_errors.h @@ -63,13 +63,15 @@ BEGIN_EXTERN_C() #define STREAM_ERROR_CODE_MKDIR_FAILED 41 #define STREAM_ERROR_CODE_RMDIR_FAILED 42 #define STREAM_ERROR_CODE_STAT_FAILED 43 -#define STREAM_ERROR_CODE_CHMOD_FAILED 44 -#define STREAM_ERROR_CODE_CHOWN_FAILED 45 -#define STREAM_ERROR_CODE_TOUCH_FAILED 46 -#define STREAM_ERROR_CODE_INVALID_MODE 47 -#define STREAM_ERROR_CODE_MODE_NOT_SUPPORTED 48 -#define STREAM_ERROR_CODE_READONLY 49 -#define STREAM_ERROR_CODE_RECURSION_DETECTED 50 +#define STREAM_ERROR_CODE_META_FAILED 44 +#define STREAM_ERROR_CODE_CHMOD_FAILED 45 +#define STREAM_ERROR_CODE_CHOWN_FAILED 46 +#define STREAM_ERROR_CODE_COPY_FAILED 47 +#define STREAM_ERROR_CODE_TOUCH_FAILED 48 +#define STREAM_ERROR_CODE_INVALID_MODE 49 +#define STREAM_ERROR_CODE_MODE_NOT_SUPPORTED 50 +#define STREAM_ERROR_CODE_READONLY 51 +#define STREAM_ERROR_CODE_RECURSION_DETECTED 52 /* Wrapper/protocol operations */ #define STREAM_ERROR_CODE_WRAPPER_NOT_FOUND 70 #define STREAM_ERROR_CODE_WRAPPER_DISABLED 71 @@ -218,6 +220,15 @@ void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper); #define php_stream_wrapper_warn_param(wrapper, context, options, code, param, ...) \ php_stream_wrapper_error_param(wrapper, context, options, E_WARNING, true, code, param, __VA_ARGS__) +#define php_stream_wrapper_warn_param_nt(wrapper, context, options, code, param, ...) \ + php_stream_wrapper_error_param(wrapper, context, options, E_WARNING, false, code, param, __VA_ARGS__) + +#define php_stream_wrapper_warn_param2(wrapper, context, options, code, param1, param2, ...) \ + php_stream_wrapper_error_param2(wrapper, context, options, E_WARNING, true, code, param1, param2, __VA_ARGS__) + +#define php_stream_wrapper_warn_param2_nt(wrapper, context, options, code, param1, param2, ...) \ + php_stream_wrapper_error_param2(wrapper, context, options, E_WARNING, false, code, param1, param2, __VA_ARGS__) + #define php_stream_warn(stream, code, ...) \ php_stream_error(stream, E_WARNING, true, code, __VA_ARGS__) diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 688d271db8147..8572f6f0275bf 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -394,7 +394,8 @@ static ssize_t php_stdiop_write(php_stream *stream, const char *buf, size_t coun } if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { char errstr[256]; - php_error_docref(NULL, E_NOTICE, "Write of %zu bytes failed with errno=%d %s", + php_stream_notice(stream, STREAM_ERROR_CODE_WRITE_FAILED, + "Write of %zu bytes failed with errno=%d %s", count, errno, php_socket_strerror_s(errno, errstr, sizeof(errstr))); } } @@ -472,7 +473,8 @@ static ssize_t php_stdiop_read(php_stream *stream, char *buf, size_t count) } else { if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { char errstr[256]; - php_error_docref(NULL, E_NOTICE, "Read of %zu bytes failed with errno=%d %s", + php_stream_notice(stream, STREAM_ERROR_CODE_READ_FAILED, + "Read of %zu bytes failed with errno=%d %s", count, errno, php_socket_strerror_s(errno, errstr, sizeof(errstr))); } @@ -621,7 +623,8 @@ static int php_stdiop_seek(php_stream *stream, zend_off_t offset, int whence, ze assert(data != NULL); if (!data->is_seekable) { - php_error_docref(NULL, E_WARNING, "Cannot seek on this stream"); + php_stream_wrapper_warn(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + STREAM_ERROR_CODE_SEEK_NOT_SUPPORTED, "Cannot seek on this stream"); return -1; } @@ -1158,7 +1161,8 @@ PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, zen char *persistent_id = NULL; if (FAILURE == php_stream_parse_fopen_modes(mode, &open_flags)) { - php_stream_wrapper_log_error(&php_plain_files_wrapper, options, "`%s' is not a valid mode for fopen", mode); + php_stream_wrapper_log_warn(&php_plain_files_wrapper, NULL, options, + STREAM_ERROR_CODE_INVALID_MODE, "`%s' is not a valid mode for fopen", mode); return NULL; } @@ -1311,8 +1315,9 @@ static int php_plain_files_unlink(php_stream_wrapper *wrapper, const char *url, if (ret == -1) { if (options & REPORT_ERRORS) { char errstr[256]; - php_error_docref1(NULL, url, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, options, + STREAM_ERROR_CODE_UNLINK_FAILED, url, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return 0; } @@ -1379,20 +1384,22 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f * access to the file in the meantime. */ if (VCWD_CHOWN(url_to, sb.st_uid, sb.st_gid)) { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); if (errno != EPERM) { success = 0; } + php_stream_wrapper_error_param2(wrapper, context, options, E_WARNING, + !success, STREAM_ERROR_CODE_CHOWN_FAILED, url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } if (success) { if (VCWD_CHMOD(url_to, sb.st_mode)) { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); if (errno != EPERM) { success = 0; } + php_stream_wrapper_error_param2(wrapper, context, options, E_WARNING, + !success, STREAM_ERROR_CODE_CHOWN_FAILED, url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } } # endif @@ -1400,12 +1407,14 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f VCWD_UNLINK(url_from); } } else { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param2_nt(wrapper, context, options, + STREAM_ERROR_CODE_STAT_FAILED, url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } } else { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param2_nt(wrapper, context, options, + STREAM_ERROR_CODE_COPY_FAILED, url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } # if !defined(ZTS) && !defined(TSRM_WIN32) umask(oldmask); @@ -1418,8 +1427,9 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f #ifdef PHP_WIN32 php_win32_docref2_from_error(GetLastError(), url_from, url_to); #else - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param2(wrapper, context, options, + STREAM_ERROR_CODE_RENAME_FAILED, url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); #endif return 0; } @@ -1443,7 +1453,8 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i int ret = VCWD_MKDIR(dir, (mode_t)mode); if (ret < 0 && (options & REPORT_ERRORS)) { - php_error_docref(NULL, E_WARNING, "%s", strerror(errno)); + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_MKDIR_FAILED, "%s", strerror(errno)); return 0; } @@ -1452,7 +1463,8 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i char buf[MAXPATHLEN]; if (!expand_filepath_with_mode(dir, buf, NULL, 0, CWD_EXPAND)) { - php_error_docref(NULL, E_WARNING, "Invalid path"); + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_INVALID_PATH, "Invalid path"); return 0; } @@ -1504,7 +1516,9 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i int ret = VCWD_MKDIR(buf, (mode_t) mode); if (ret < 0 && errno != EEXIST) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_MKDIR_FAILED, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return 0; } @@ -1524,7 +1538,9 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i /* issue a warning to client when the last directory was created failed */ if (ret < 0) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_MKDIR_FAILED, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return 0; } @@ -1546,13 +1562,17 @@ static int php_plain_files_rmdir(php_stream_wrapper *wrapper, const char *url, i char errstr[256]; #ifdef PHP_WIN32 if (!php_win32_check_trailing_space(url, strlen(url))) { - php_error_docref1(NULL, url, E_WARNING, "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, options, + STREAM_ERROR_CODE_NOT_FOUND, url, + "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); return 0; } #endif if (VCWD_RMDIR(url) < 0) { - php_error_docref1(NULL, url, E_WARNING, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, options, + STREAM_ERROR_CODE_RMDIR_FAILED, url, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); return 0; } @@ -1575,7 +1595,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url #ifdef PHP_WIN32 if (!php_win32_check_trailing_space(url, strlen(url))) { - php_error_docref1(NULL, url, E_WARNING, "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + STREAM_ERROR_CODE_NOT_FOUND, url, + "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); return 0; } #endif @@ -1594,7 +1616,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url if (VCWD_ACCESS(url, F_OK) != 0) { FILE *file = VCWD_FOPEN(url, "w"); if (file == NULL) { - php_error_docref1(NULL, url, E_WARNING, "Unable to create file %s because %s", url, + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + STREAM_ERROR_CODE_PERMISSION_DENIED, url, + "Unable to create file %s because %s", url, php_socket_strerror_s(errno, errstr, sizeof(errstr))); return 0; } @@ -1608,7 +1632,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url case PHP_STREAM_META_OWNER: if(option == PHP_STREAM_META_OWNER_NAME) { if(php_get_uid_by_name((char *)value, &uid) != SUCCESS) { - php_error_docref1(NULL, url, E_WARNING, "Unable to find uid for %s", (char *)value); + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + STREAM_ERROR_CODE_META_FAILED, url, + "Unable to find uid for %s", (char *)value); return 0; } } else { @@ -1620,7 +1646,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url case PHP_STREAM_META_GROUP_NAME: if(option == PHP_STREAM_META_GROUP_NAME) { if(php_get_gid_by_name((char *)value, &gid) != SUCCESS) { - php_error_docref1(NULL, url, E_WARNING, "Unable to find gid for %s", (char *)value); + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + STREAM_ERROR_CODE_META_FAILED, url, + "Unable to find gid for %s", (char *)value); return 0; } } else { @@ -1638,8 +1666,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url return 0; } if (ret == -1) { - php_error_docref1(NULL, url, E_WARNING, "Operation failed: %s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + STREAM_ERROR_CODE_META_FAILED, url, + "Operation failed: %s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); return 0; } php_clear_stat_cache(0, NULL, 0); @@ -1732,7 +1761,9 @@ PHPAPI php_stream *_php_stream_fopen_with_path(const char *filename, const char *(cwd+3) = '\0'; if (snprintf(trypath, MAXPATHLEN, "%s%s", cwd, filename) >= MAXPATHLEN) { - php_error_docref(NULL, E_NOTICE, "%s/%s path was truncated to %d", cwd, filename, MAXPATHLEN); + php_stream_wrapper_notice(NULL, NULL, REPORT_ERRORS, + STREAM_ERROR_CODE_PATH_TOO_LONG, + "%s/%s path was truncated to %d", cwd, filename, MAXPATHLEN); } efree(cwd); @@ -1787,7 +1818,9 @@ PHPAPI php_stream *_php_stream_fopen_with_path(const char *filename, const char goto stream_skip; } if (snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename) >= MAXPATHLEN) { - php_error_docref(NULL, E_NOTICE, "%s/%s path was truncated to %d", ptr, filename, MAXPATHLEN); + php_stream_wrapper_notice(NULL, NULL, REPORT_ERRORS, + STREAM_ERROR_CODE_PATH_TOO_LONG, + "%s/%s path was truncated to %d", ptr, filename, MAXPATHLEN); } if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir_ex(trypath, 0)) { diff --git a/main/streams/stream_errors.stub.php b/main/streams/stream_errors.stub.php index c55a4abfefa56..b600a7faa796d 100644 --- a/main/streams/stream_errors.stub.php +++ b/main/streams/stream_errors.stub.php @@ -131,6 +131,11 @@ public function getWrapperName(): string {} * @cvalue STREAM_ERROR_CODE_STAT_FAILED */ const STREAM_ERROR_CODE_STAT_FAILED = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_META_FAILED + */ +const STREAM_ERROR_CODE_META_FAILED = UNKNOWN; /** * @var int * @cvalue STREAM_ERROR_CODE_CHMOD_FAILED @@ -141,6 +146,11 @@ public function getWrapperName(): string {} * @cvalue STREAM_ERROR_CODE_CHOWN_FAILED */ const STREAM_ERROR_CODE_CHOWN_FAILED = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_COPY_FAILED + */ +const STREAM_ERROR_CODE_COPY_FAILED = UNKNOWN; /** * @var int * @cvalue STREAM_ERROR_CODE_TOUCH_FAILED diff --git a/main/streams/stream_errors_arginfo.h b/main/streams/stream_errors_arginfo.h index c51b1080c998a..5859f825c4ee4 100644 --- a/main/streams/stream_errors_arginfo.h +++ b/main/streams/stream_errors_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: e19e8ac3e877b2f3a8a2b2ec0033c57c356daffa */ + * Stub hash: c14298e97a4960fe35b7b1b464a5b0b94099897f */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_StreamException_getParam, 0, 0, IS_STRING, 1) ZEND_END_ARG_INFO() @@ -42,8 +42,10 @@ static void register_stream_errors_symbols(int module_number) REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_MKDIR_FAILED", STREAM_ERROR_CODE_MKDIR_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_RMDIR_FAILED", STREAM_ERROR_CODE_RMDIR_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_STAT_FAILED", STREAM_ERROR_CODE_STAT_FAILED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_META_FAILED", STREAM_ERROR_CODE_META_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_CHMOD_FAILED", STREAM_ERROR_CODE_CHMOD_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_CHOWN_FAILED", STREAM_ERROR_CODE_CHOWN_FAILED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_COPY_FAILED", STREAM_ERROR_CODE_COPY_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_TOUCH_FAILED", STREAM_ERROR_CODE_TOUCH_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_INVALID_MODE", STREAM_ERROR_CODE_INVALID_MODE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_MODE_NOT_SUPPORTED", STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, CONST_PERSISTENT); From a56024b1fb991e219a15dc0e4a02fc18b577631e Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 16 Nov 2025 22:17:32 +0100 Subject: [PATCH 11/37] stream: convert cast errors --- main/streams/cast.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/main/streams/cast.c b/main/streams/cast.c index 4b7183024571b..b7edfe0568825 100644 --- a/main/streams/cast.c +++ b/main/streams/cast.c @@ -189,7 +189,6 @@ void php_stream_mode_sanitize_fdopen_fopencookie(php_stream *stream, char *resul result[res_curs] = '\0'; } /* }}} */ - /* {{{ php_stream_cast */ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err) { @@ -259,7 +258,7 @@ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, b) no memory -> lets bail */ - php_error_docref(NULL, E_ERROR, "fopencookie failed"); + php_stream_fatal(stream, STREAM_ERROR_CODE_CAST_FAILED, "fopencookie failed"); return FAILURE; #endif @@ -299,7 +298,8 @@ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, if (php_stream_is_filtered(stream)) { if (show_err) { - php_error_docref(NULL, E_WARNING, "Cannot cast a filtered stream on this system"); + php_stream_warn(stream, STREAM_ERROR_CODE_CAST_NOT_SUPPORTED, + "Cannot cast a filtered stream on this system"); } return FAILURE; } else if (stream->ops->cast && stream->ops->cast(stream, castas, ret) == SUCCESS) { @@ -315,7 +315,8 @@ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, "select()able descriptor" }; - php_error_docref(NULL, E_WARNING, "Cannot represent a stream of type %s as a %s", stream->ops->label, cast_names[castas]); + php_stream_warn(stream, STREAM_ERROR_CODE_CAST_NOT_SUPPORTED, + "Cannot represent a stream of type %s as a %s", stream->ops->label, cast_names[castas]); } return FAILURE; @@ -330,7 +331,9 @@ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, * will be accessing the stream. Emit a warning so that the end-user will * know that they should try something else */ - php_error_docref(NULL, E_WARNING, ZEND_LONG_FMT " bytes of buffered data lost during stream conversion!", (zend_long)(stream->writepos - stream->readpos)); + php_stream_warn_nt(stream, STREAM_ERROR_CODE_BUFFERED_DATA_LOST, + ZEND_LONG_FMT " bytes of buffered data lost during stream conversion!", + (zend_long)(stream->writepos - stream->readpos)); } if (castas == PHP_STREAM_AS_STDIO && ret) { From 5931642d678719bad21a56ac62c6580a270c9316 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 16 Nov 2025 22:23:41 +0100 Subject: [PATCH 12/37] stream: convert filter errors --- main/streams/filter.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main/streams/filter.c b/main/streams/filter.c index b63d789190792..55f1c7ef5ea76 100644 --- a/main/streams/filter.c +++ b/main/streams/filter.c @@ -343,7 +343,8 @@ PHPAPI zend_result php_stream_filter_append_ex(php_stream_filter_chain *chain, p php_stream_bucket_unlink(bucket); php_stream_bucket_delref(bucket); } - php_error_docref(NULL, E_WARNING, "Filter failed to process pre-buffered data"); + php_stream_warn(stream, STREAM_ERROR_CODE_FILTER_FAILED, + "Filter failed to process pre-buffered data"); return FAILURE; case PSFS_FEED_ME: /* We don't actually need data yet, From 69cab18c586583f2108a6c3cd42fe64954bcea72 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 16 Nov 2025 22:52:22 +0100 Subject: [PATCH 13/37] stream: convert core streams errors --- main/streams/php_stream_errors.h | 14 +++++++----- main/streams/stream_errors.stub.php | 10 +++++++++ main/streams/streams.c | 35 ++++++++++++++++++++--------- 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h index 7f57a684b8250..34894d2512706 100644 --- a/main/streams/php_stream_errors.h +++ b/main/streams/php_stream_errors.h @@ -73,12 +73,14 @@ BEGIN_EXTERN_C() #define STREAM_ERROR_CODE_READONLY 51 #define STREAM_ERROR_CODE_RECURSION_DETECTED 52 /* Wrapper/protocol operations */ -#define STREAM_ERROR_CODE_WRAPPER_NOT_FOUND 70 -#define STREAM_ERROR_CODE_WRAPPER_DISABLED 71 -#define STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED 72 -#define STREAM_ERROR_CODE_WRAPPER_REGISTRATION_FAILED 73 -#define STREAM_ERROR_CODE_WRAPPER_UNREGISTRATION_FAILED 74 -#define STREAM_ERROR_CODE_WRAPPER_RESTORATION_FAILED 75 +#define STREAM_ERROR_CODE_NO_OPENER 70 +#define STREAM_ERROR_CODE_PERSISTENT_NOT_SUPPORTED 71 +#define STREAM_ERROR_CODE_WRAPPER_NOT_FOUND 72 +#define STREAM_ERROR_CODE_WRAPPER_DISABLED 73 +#define STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED 74 +#define STREAM_ERROR_CODE_WRAPPER_REGISTRATION_FAILED 75 +#define STREAM_ERROR_CODE_WRAPPER_UNREGISTRATION_FAILED 76 +#define STREAM_ERROR_CODE_WRAPPER_RESTORATION_FAILED 77 /* Filter operations */ #define STREAM_ERROR_CODE_FILTER_NOT_FOUND 90 #define STREAM_ERROR_CODE_FILTER_FAILED 91 diff --git a/main/streams/stream_errors.stub.php b/main/streams/stream_errors.stub.php index b600a7faa796d..802a90e00fb8d 100644 --- a/main/streams/stream_errors.stub.php +++ b/main/streams/stream_errors.stub.php @@ -176,6 +176,16 @@ public function getWrapperName(): string {} * @cvalue STREAM_ERROR_CODE_RECURSION_DETECTED */ const STREAM_ERROR_CODE_RECURSION_DETECTED = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_NO_OPENER + */ +const STREAM_ERROR_CODE_NO_OPENER = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_PERSISTENT_NOT_SUPPORTED + */ +const STREAM_ERROR_CODE_PERSISTENT_NOT_SUPPORTED = UNKNOWN; /** * @var int * @cvalue STREAM_ERROR_CODE_WRAPPER_NOT_FOUND diff --git a/main/streams/streams.c b/main/streams/streams.c index a929c532bdc8a..fb9901d7c6aff 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -1184,7 +1184,7 @@ PHPAPI ssize_t _php_stream_write(php_stream *stream, const char *buf, size_t cou ZEND_ASSERT(buf != NULL); if (stream->ops->write == NULL) { - php_error_docref(NULL, E_NOTICE, "Stream is not writable"); + php_stream_notice(stream, STREAM_ERROR_CODE_NOT_WRITABLE, "Stream is not writable"); return (ssize_t) -1; } @@ -1318,7 +1318,8 @@ PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence) return 0; } - php_error_docref(NULL, E_WARNING, "Stream does not support seeking"); + php_stream_warn(stream, STREAM_ERROR_CODE_SEEK_NOT_SUPPORTED, + "Stream does not support seeking"); return -1; } @@ -1919,7 +1920,9 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const if (!localhost && path[n+3] != '\0' && path[n+3] != '/') { #endif if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Remote host file access not supported, %s", path); + php_stream_wrapper_warn(plain_files_wrapper, NULL, options, + STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + "Remote host file access not supported, %s", path); } return NULL; } @@ -1958,7 +1961,9 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const } if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "file:// wrapper is disabled in the server configuration"); + php_stream_wrapper_warn(plain_files_wrapper, NULL, options, + STREAM_ERROR_CODE_DISABLED, + "file:// wrapper is disabled in the server configuration"); } return NULL; } @@ -2056,7 +2061,8 @@ PHPAPI php_stream *_php_stream_opendir(const char *path, int options, stream->flags |= PHP_STREAM_FLAG_NO_BUFFER | PHP_STREAM_FLAG_IS_DIR; } } else if (wrapper) { - php_stream_wrapper_log_error(wrapper, options & ~REPORT_ERRORS, "not implemented"); + php_stream_wrapper_log_warn(wrapper, context, options & ~REPORT_ERRORS, + STREAM_ERROR_CODE_NO_OPENER, "not implemented"); } if (stream == NULL && (options & REPORT_ERRORS)) { php_stream_display_wrapper_errors(wrapper, path, "Failed to open directory"); @@ -2126,7 +2132,13 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options); if ((options & STREAM_USE_URL) && (!wrapper || !wrapper->is_url)) { - php_error_docref(NULL, E_WARNING, "This function may only be used against URLs"); + if (wrapper) { + php_stream_wrapper_warn(wrapper, context, options, + STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, + "This function may only be used against URLs"); + } else { + php_error_docref(NULL, E_WARNING, "This function may only be used against URLs"); + } if (resolved_path) { zend_string_release_ex(resolved_path, 0); } @@ -2135,7 +2147,8 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod if (wrapper) { if (!wrapper->wops->stream_opener) { - php_stream_wrapper_log_error(wrapper, options & ~REPORT_ERRORS, + php_stream_wrapper_log_warn(wrapper, context, options & ~REPORT_ERRORS, + STREAM_ERROR_CODE_NO_OPENER, "wrapper does not support stream open"); } else { stream = wrapper->wops->stream_opener(wrapper, @@ -2146,7 +2159,8 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod /* if the caller asked for a persistent stream but the wrapper did not * return one, force an error here */ if (stream && persistent && !stream->is_persistent) { - php_stream_wrapper_log_error(wrapper, options & ~REPORT_ERRORS, + php_stream_wrapper_log_warn(wrapper, context, options & ~REPORT_ERRORS, + STREAM_ERROR_CODE_PERSISTENT_NOT_SUPPORTED, "wrapper does not support persistent streams"); php_stream_close(stream); stream = NULL; @@ -2198,8 +2212,9 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod if (options & REPORT_ERRORS) { char *tmp = estrdup(path); php_strip_url_passwd(tmp); - php_error_docref1(NULL, tmp, E_WARNING, "could not make seekable - %s", - tmp); + php_stream_wrapper_warn_param(wrapper, context, options, + STREAM_ERROR_CODE_SEEK_NOT_SUPPORTED, tmp, + "could not make seekable - %s", tmp); efree(tmp); options &= ~REPORT_ERRORS; From eeb5c8678bba2f027de17f3fe5bcad5d05114bb1 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 17 Nov 2025 12:50:58 +0100 Subject: [PATCH 14/37] stream: add docref param to error functions --- main/streams/php_stream_errors.h | 42 +++++++----- main/streams/plain_wrapper.c | 4 +- main/streams/stream_errors.c | 108 +++++++++++++++++++------------ 3 files changed, 92 insertions(+), 62 deletions(-) diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h index 34894d2512706..ffd0969eef15f 100644 --- a/main/streams/php_stream_errors.h +++ b/main/streams/php_stream_errors.h @@ -122,8 +122,9 @@ BEGIN_EXTERN_C() typedef struct { zend_string *message; int code; - const char *wrapper_name; /* Points to wrapper->wops->label, no need to duplicate */ - const char *param; /* Points to passed string, caller manages lifetime for storage */ + const char *wrapper_name; /* Points to wrapper->wops->label, no need to duplicate */ + const char *docref; + const char *param; int severity; bool terminal; } php_stream_error_entry; @@ -132,17 +133,19 @@ typedef struct { PHPAPI void php_stream_wrapper_error( php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminal, int code, const char *fmt, ... -) ZEND_ATTRIBUTE_FORMAT(printf, 7, 8); +) ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); PHPAPI void php_stream_wrapper_error_param( php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminal, @@ -150,11 +153,12 @@ PHPAPI void php_stream_wrapper_error_param( const char *param, const char *fmt, ... -) ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); +) ZEND_ATTRIBUTE_FORMAT(printf, 9, 10); PHPAPI void php_stream_wrapper_error_param2( php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminal, @@ -163,16 +167,17 @@ PHPAPI void php_stream_wrapper_error_param2( const char *param2, const char *fmt, ... -) ZEND_ATTRIBUTE_FORMAT(printf, 9, 10); +) ZEND_ATTRIBUTE_FORMAT(printf, 10, 11); PHPAPI void php_stream_error( php_stream *stream, + const char *docref, int severity, bool terminal, int code, const char *fmt, ... -) ZEND_ATTRIBUTE_FORMAT(printf, 5, 6); +) ZEND_ATTRIBUTE_FORMAT(printf, 6, 7); /* Legacy wrapper error log - updated API */ PHPAPI void php_stream_wrapper_log_error( @@ -211,37 +216,40 @@ void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper); /* Convenience macros */ #define php_stream_wrapper_warn(wrapper, context, options, code, ...) \ - php_stream_wrapper_error(wrapper, context, options, E_WARNING, true, code, __VA_ARGS__) + php_stream_wrapper_error(wrapper, context, NULL, options, E_WARNING, true, code, __VA_ARGS__) #define php_stream_wrapper_warn_nt(wrapper, context, options, code, ...) \ - php_stream_wrapper_error(wrapper, context, options, E_WARNING, false, code, __VA_ARGS__) + php_stream_wrapper_error(wrapper, context, NULL, options, E_WARNING, false, code, __VA_ARGS__) #define php_stream_wrapper_notice(wrapper, context, options, code, ...) \ - php_stream_wrapper_error(wrapper, context, options, E_NOTICE, false, code, __VA_ARGS__) + php_stream_wrapper_error(wrapper, context, NULL, options, E_NOTICE, false, code, __VA_ARGS__) #define php_stream_wrapper_warn_param(wrapper, context, options, code, param, ...) \ - php_stream_wrapper_error_param(wrapper, context, options, E_WARNING, true, code, param, __VA_ARGS__) + php_stream_wrapper_error_param(wrapper, context, NULL, options, E_WARNING, true, code, param, __VA_ARGS__) #define php_stream_wrapper_warn_param_nt(wrapper, context, options, code, param, ...) \ - php_stream_wrapper_error_param(wrapper, context, options, E_WARNING, false, code, param, __VA_ARGS__) + php_stream_wrapper_error_param(wrapper, context, NULL, options, E_WARNING, false, code, param, __VA_ARGS__) #define php_stream_wrapper_warn_param2(wrapper, context, options, code, param1, param2, ...) \ - php_stream_wrapper_error_param2(wrapper, context, options, E_WARNING, true, code, param1, param2, __VA_ARGS__) + php_stream_wrapper_error_param2(wrapper, context, NULL, options, E_WARNING, true, code, param1, param2, __VA_ARGS__) #define php_stream_wrapper_warn_param2_nt(wrapper, context, options, code, param1, param2, ...) \ - php_stream_wrapper_error_param2(wrapper, context, options, E_WARNING, false, code, param1, param2, __VA_ARGS__) + php_stream_wrapper_error_param2(wrapper, context, NULL, options, E_WARNING, false, code, param1, param2, __VA_ARGS__) #define php_stream_warn(stream, code, ...) \ - php_stream_error(stream, E_WARNING, true, code, __VA_ARGS__) + php_stream_error(stream, NULL, E_WARNING, true, code, __VA_ARGS__) #define php_stream_warn_nt(stream, code, ...) \ - php_stream_error(stream, E_WARNING, false, code, __VA_ARGS__) + php_stream_error(stream, NULL, E_WARNING, false, code, __VA_ARGS__) + +#define php_stream_warn_docref(stream, docref, code, ...) \ + php_stream_error(stream, docref, E_WARNING, true, code, __VA_ARGS__) #define php_stream_notice(stream, code, ...) \ - php_stream_error(stream, E_NOTICE, false, code, __VA_ARGS__) + php_stream_error(stream, NULL, E_NOTICE, false, code, __VA_ARGS__) #define php_stream_fatal(stream, code, ...) \ - php_stream_error(stream, E_ERROR, true, code, __VA_ARGS__) + php_stream_error(stream, NULL, E_ERROR, true, code, __VA_ARGS__) /* Legacy log variants */ #define php_stream_wrapper_log_warn(wrapper, context, options, code, ...) \ diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 8572f6f0275bf..22375713a2a93 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -1387,7 +1387,7 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f if (errno != EPERM) { success = 0; } - php_stream_wrapper_error_param2(wrapper, context, options, E_WARNING, + php_stream_wrapper_error_param2(wrapper, context, NULL, options, E_WARNING, !success, STREAM_ERROR_CODE_CHOWN_FAILED, url_from, url_to, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } @@ -1397,7 +1397,7 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f if (errno != EPERM) { success = 0; } - php_stream_wrapper_error_param2(wrapper, context, options, E_WARNING, + php_stream_wrapper_error_param2(wrapper, context, NULL, options, E_WARNING, !success, STREAM_ERROR_CODE_CHOWN_FAILED, url_from, url_to, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } diff --git a/main/streams/stream_errors.c b/main/streams/stream_errors.c index 74e3af9a7a1c0..be4faabd1162a 100644 --- a/main/streams/stream_errors.c +++ b/main/streams/stream_errors.c @@ -32,6 +32,8 @@ static void php_stream_error_entry_dtor(void *error) { php_stream_error_entry *entry = *(php_stream_error_entry **) error; zend_string_release(entry->message); + efree(entry->docref); + efree(entry->param); efree(entry); } @@ -132,8 +134,8 @@ static void php_stream_throw_exception( /* Core error processing */ static void php_stream_process_error(php_stream_context *context, const char *wrapper_name, - php_stream *stream, int code, const char *message, const char *param, int severity, - bool terminal) + php_stream *stream, const char *docref, int code, const char *message, const char *param, + int severity, bool terminal) { int error_mode = php_stream_get_error_mode(context); @@ -141,9 +143,9 @@ static void php_stream_process_error(php_stream_context *context, const char *wr switch (error_mode) { case PHP_STREAM_ERROR_MODE_ERROR: if (param) { - php_error_docref1(NULL, param, severity, "%s", message); + php_error_docref1(docref, param, severity, "%s", message); } else { - php_error_docref(NULL, severity, "%s", message); + php_error_docref(docref, severity, "%s", message); } break; @@ -168,7 +170,8 @@ static void php_stream_process_error(php_stream_context *context, const char *wr if (!zend_is_callable_ex(handler, NULL, 0, NULL, &fcc, &is_callable_error)) { if (is_callable_error) { - zend_type_error("stream error handler must be a valid callback, %s", is_callable_error); + zend_type_error( + "stream error handler must be a valid callback, %s", is_callable_error); efree(is_callable_error); } else { zend_type_error("stream error must be a valid callback"); @@ -213,7 +216,8 @@ static void php_stream_process_error(php_stream_context *context, const char *wr /* Helper to create error entry */ static php_stream_error_entry *php_stream_create_error_entry(zend_string *message, int code, - const char *wrapper_name, const char *param, int severity, bool terminal) + const char *wrapper_name, const char *docref, const char *param, int severity, + bool terminal) { php_stream_error_entry *entry = emalloc(sizeof(php_stream_error_entry)); entry->message = message; /* Takes ownership */ @@ -227,18 +231,22 @@ static php_stream_error_entry *php_stream_create_error_entry(zend_string *messag /* Common storage function*/ static void php_stream_store_error_common(php_stream_context *context, php_stream *stream, - php_stream_wrapper *wrapper, zend_string *message, int code, const char *wrapper_name, - const char *param, int severity, bool terminal) + php_stream_wrapper *wrapper, zend_string *message, const char *docref, int code, + const char *wrapper_name, const char *param, int severity, bool terminal) { int error_mode = php_stream_get_error_mode(context); int store_mode = php_stream_get_error_store_mode(context, error_mode); if (!php_stream_should_store_error(store_mode, terminal)) { + efree(param); return; } - php_stream_error_entry *entry - = php_stream_create_error_entry(message, code, wrapper_name, param, severity, terminal); + if (docref != NULL) { + docref = estrdup(docref); + } + php_stream_error_entry *entry = php_stream_create_error_entry( + message, code, wrapper_name, docref, param, severity, terminal); zend_string_addref(message); /* Storage keeps a reference */ zend_llist *list; @@ -277,46 +285,51 @@ static void php_stream_store_error_common(php_stream_context *context, php_strea /* Wrapper error reporting - stores in FG(wrapper_stored_errors) */ static void php_stream_wrapper_error_internal(php_stream_wrapper *wrapper, - php_stream_context *context, int options, int severity, bool terminal, int code, - const char *param, const char *fmt, va_list args) + php_stream_context *context, const char *docref, int options, int severity, bool terminal, + int code, const char *param, const char *fmt, va_list args) { zend_string *message = vstrpprintf(0, fmt, args); const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown"; if (options & REPORT_ERRORS) { - php_stream_process_error( - context, wrapper_name, NULL, code, ZSTR_VAL(message), param, severity, terminal); + php_stream_process_error(context, wrapper_name, NULL, docref, code, ZSTR_VAL(message), + param, severity, terminal); } php_stream_store_error_common( - context, NULL, wrapper, message, code, wrapper_name, param, severity, terminal); + context, NULL, wrapper, message, docref, code, wrapper_name, param, severity, terminal); zend_string_release(message); } PHPAPI void php_stream_wrapper_error(php_stream_wrapper *wrapper, php_stream_context *context, - int options, int severity, bool terminal, int code, const char *fmt, ...) + const char *docref, int options, int severity, bool terminal, int code, const char *fmt, + ...) { va_list args; va_start(args, fmt); php_stream_wrapper_error_internal( - wrapper, context, options, severity, terminal, code, NULL, fmt, args); + wrapper, context, docref, options, severity, terminal, code, NULL, fmt, args); va_end(args); } PHPAPI void php_stream_wrapper_error_param(php_stream_wrapper *wrapper, php_stream_context *context, - int options, int severity, bool terminal, int code, const char *param, const char *fmt, ...) + const char *docref, int options, int severity, bool terminal, int code, const char *param, + const char *fmt, ...) { va_list args; va_start(args, fmt); + if (param = NULL) { + param = estrdup(param); + } php_stream_wrapper_error_internal( - wrapper, context, options, severity, terminal, code, param, fmt, args); + wrapper, context, docref, options, severity, terminal, code, param, fmt, args); va_end(args); } PHPAPI void php_stream_wrapper_error_param2(php_stream_wrapper *wrapper, - php_stream_context *context, int options, int severity, bool terminal, int code, - const char *param1, const char *param2, const char *fmt, ...) + php_stream_context *context, const char *docref, int options, int severity, bool terminal, + int code, const char *param1, const char *param2, const char *fmt, ...) { char *combined_param; spprintf(&combined_param, 0, "%s,%s", param1, param2); @@ -324,10 +337,8 @@ PHPAPI void php_stream_wrapper_error_param2(php_stream_wrapper *wrapper, va_list args; va_start(args, fmt); php_stream_wrapper_error_internal( - wrapper, context, options, severity, terminal, code, combined_param, fmt, args); + wrapper, context, docref, options, severity, terminal, code, combined_param, fmt, args); va_end(args); - - efree(combined_param); } /* Wrapper error logging - stores in FG(wrapper_logged_errors) */ @@ -336,8 +347,11 @@ static void php_stream_wrapper_log_store_error(const php_stream_wrapper *wrapper zend_string *message, int code, const char *wrapper_name, const char *param, int severity, bool terminal) { - php_stream_error_entry *entry - = php_stream_create_error_entry(message, code, wrapper_name, param, severity, terminal); + if (param != NULL) { + param = estrdup(param); + } + php_stream_error_entry *entry = php_stream_create_error_entry( + message, code, wrapper_name, NULL, param, severity, terminal); zend_string_addref(message); if (!FG(wrapper_logged_errors)) { @@ -359,15 +373,17 @@ static void php_stream_wrapper_log_store_error(const php_stream_wrapper *wrapper zend_llist_add_element(list, &entry); } -static void php_stream_wrapper_log_error_internal(const php_stream_wrapper *wrapper, php_stream_context *context, - int options, int severity, bool terminal, int code, const char *param, const char *fmt, va_list args) +static void php_stream_wrapper_log_error_internal(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminal, int code, + const char *param, const char *fmt, va_list args) { zend_string *message = vstrpprintf(0, args, args); const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown"; if (options & REPORT_ERRORS) { /* Report immediately using standard error functions */ - php_stream_wrapper_error_internal(wrapper, context, options, severity, terminal, code, param, fmt, args); + php_stream_wrapper_error_internal( + wrapper, context, options, severity, terminal, code, param, fmt, args); zend_string_release(message); } else { /* Store for later display in FG(wrapper_logged_errors) */ @@ -377,21 +393,25 @@ static void php_stream_wrapper_log_error_internal(const php_stream_wrapper *wrap } } -PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, php_stream_context *context, - int options, int severity, bool terminal, int code, const char *fmt, ...) +PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminal, int code, + const char *fmt, ...) { va_list args; va_start(args, fmt); - php_stream_wrapper_log_error_internal(wrapper, context, options, severity, terminal, code, NULL, fmt, args); + php_stream_wrapper_log_error_internal( + wrapper, context, options, severity, terminal, code, NULL, fmt, args); va_end(args); } -PHPAPI void php_stream_wrapper_log_error_param(const php_stream_wrapper *wrapper, php_stream_context *context, - int options, int severity, bool terminal, int code, const char *param, const char *fmt, ...) +PHPAPI void php_stream_wrapper_log_error_param(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminal, int code, + const char *param, const char *fmt, ...) { va_list args; va_start(args, fmt); - php_stream_wrapper_log_error_internal(wrapper, context, options, severity, terminal, code, param, fmt, args); + php_stream_wrapper_log_error_internal( + wrapper, context, options, severity, terminal, code, param, fmt, args); va_end(args); } @@ -400,12 +420,14 @@ static zend_llist *php_stream_get_wrapper_errors_list(php_stream_wrapper *wrappe if (!FG(wrapper_logged_errors)) { return NULL; } else { - return (zend_llist*) zend_hash_str_find_ptr(FG(wrapper_logged_errors), (const char*)&wrapper, sizeof(wrapper)); + return (zend_llist *) zend_hash_str_find_ptr( + FG(wrapper_logged_errors), (const char *) &wrapper, sizeof(wrapper)); } } /* {{{ wrapper error reporting */ -static void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption) +static void php_stream_display_wrapper_errors( + php_stream_wrapper *wrapper, const char *path, const char *caption) { char *tmp; char *msg; @@ -483,8 +505,8 @@ void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper) /* Stream error reporting */ -PHPAPI void php_stream_error( - php_stream *stream, int severity, bool terminal, int code, const char *fmt, ...) +PHPAPI void php_stream_error(php_stream *stream, const char *docref, int severity, bool terminal, + int code, const char *fmt, ...) { va_list args; va_start(args, fmt); @@ -497,12 +519,12 @@ PHPAPI void php_stream_error( php_stream_context *context = PHP_STREAM_CONTEXT(stream); /* Process the error - always report for stream errors */ - php_stream_process_error( - context, wrapper_name, stream, code, ZSTR_VAL(message), NULL, severity, terminal); + php_stream_process_error(context, wrapper_name, stream, docref, code, ZSTR_VAL(message), NULL, + severity, terminal); /* Store error */ - php_stream_store_error_common( - context, stream, wrapper, message, code, wrapper_name, NULL, severity, terminal); + php_stream_store_error_common(context, stream, wrapper, message, docref, code, wrapper_name, + NULL, severity, terminal); zend_string_release(message); } From 30a3ce720e5cedd686a3be6ef50cef044e1f0d73 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 17 Nov 2025 13:15:00 +0100 Subject: [PATCH 15/37] stream: convert transport errors --- main/streams/php_stream_errors.h | 7 +++++-- main/streams/transports.c | 32 +++++++++++++++++++------------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h index ffd0969eef15f..cb1c69556355b 100644 --- a/main/streams/php_stream_errors.h +++ b/main/streams/php_stream_errors.h @@ -46,8 +46,11 @@ BEGIN_EXTERN_C() #define STREAM_ERROR_CODE_SEEK_NOT_SUPPORTED 13 #define STREAM_ERROR_CODE_FLUSH_FAILED 14 #define STREAM_ERROR_CODE_TRUNCATE_FAILED 15 -#define STREAM_ERROR_CODE_NOT_WRITABLE 16 -#define STREAM_ERROR_CODE_NOT_READABLE 17 +#define STREAM_ERROR_CODE_CONNECT_FAILED 16 +#define STREAM_ERROR_CODE_BIND_FAILED 17 +#define STREAM_ERROR_CODE_LISTEN_FAILED 18 +#define STREAM_ERROR_CODE_NOT_WRITABLE 19 +#define STREAM_ERROR_CODE_NOT_READABLE 20 /* File system operations */ #define STREAM_ERROR_CODE_DISABLED 30 #define STREAM_ERROR_CODE_NOT_FOUND 31 diff --git a/main/streams/transports.c b/main/streams/transports.c index 83297d9a06ceb..ceab9a083de82 100644 --- a/main/streams/transports.c +++ b/main/streams/transports.c @@ -39,13 +39,13 @@ PHPAPI int php_stream_xport_unregister(const char *protocol) return zend_hash_str_del(&xport_hash, protocol, strlen(protocol)); } -#define ERR_REPORT(out_err, fmt, arg) \ +#define ERR_REPORT(code, out_err, fmt, arg) \ if (out_err) { *out_err = strpprintf(0, fmt, arg); } \ - else { php_error_docref(NULL, E_WARNING, fmt, arg); } + else { php_stream_wrapper_warn(NULL, NULL, REPORT_ERRORS, code, fmt, arg); } -#define ERR_RETURN(out_err, local_err, fmt) \ +#define ERR_RETURN(code, out_err, local_err, fmt) \ if (out_err) { *out_err = local_err; } \ - else { php_error_docref(NULL, E_WARNING, fmt, local_err ? ZSTR_VAL(local_err) : "Unspecified error"); \ + else { php_stream_wrapper_warn(NULL, NULL, REPORT_ERRORS, code, fmt, local_err ? ZSTR_VAL(local_err) : "Unspecified error"); \ if (local_err) { zend_string_release_ex(local_err, 0); local_err = NULL; } \ } @@ -116,7 +116,8 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in n = sizeof(wrapper_name) - 1; PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n); - ERR_REPORT(error_string, "Unable to find the socket transport \"%s\" - did you forget to enable it when you configured PHP?", + ERR_REPORT(STREAM_ERROR_CODE_WRAPPER_NOT_FOUND, error_string, + "Unable to find the socket transport \"%s\" - did you forget to enable it when you configured PHP?", wrapper_name); return NULL; @@ -125,7 +126,8 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in if (factory == NULL) { /* should never happen */ - php_error_docref(NULL, E_WARNING, "Could not find a factory !?"); + php_stream_wrapper_warn(NULL, context, REPORT_ERRORS, + STREAM_ERROR_CODE_WRAPPER_NOT_FOUND, "Could not find a factory !?"); return NULL; } @@ -146,7 +148,7 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in flags & STREAM_XPORT_CONNECT_ASYNC ? 1 : 0, timeout, &error_text, error_code)) { - ERR_RETURN(error_string, error_text, "connect() failed: %s"); + ERR_RETURN(STREAM_ERROR_CODE_CONNECT_FAILED, error_string, error_text, "connect() failed: %s"); failed = true; } @@ -156,7 +158,7 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in /* server */ if (flags & STREAM_XPORT_BIND) { if (0 != php_stream_xport_bind(stream, name, namelen, &error_text)) { - ERR_RETURN(error_string, error_text, "bind() failed: %s"); + ERR_RETURN(STREAM_ERROR_CODE_BIND_FAILED, error_string, error_text, "bind() failed: %s"); failed = true; } else if (flags & STREAM_XPORT_LISTEN) { zval *zbacklog = NULL; @@ -167,7 +169,7 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in } if (0 != php_stream_xport_listen(stream, backlog, &error_text)) { - ERR_RETURN(error_string, error_text, "listen() failed: %s"); + ERR_RETURN(STREAM_ERROR_CODE_LISTEN_FAILED, error_string, error_text, "listen() failed: %s"); failed = true; } } @@ -370,7 +372,8 @@ PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_cr return param.outputs.returncode; } - php_error_docref("streams.crypto", E_WARNING, "This stream does not support SSL/crypto"); + php_stream_warn_docref(stream, "streams.crypto", STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + "This stream does not support SSL/crypto"); return ret; } @@ -390,7 +393,8 @@ PHPAPI int php_stream_xport_crypto_enable(php_stream *stream, int activate) return param.outputs.returncode; } - php_error_docref("streams.crypto", E_WARNING, "This stream does not support SSL/crypto"); + php_stream_warn_docref(stream, "streams.crypto", STREAM_ERROR_CODE_SSL_NOT_SUPPORTED, + "This stream does not support SSL/crypto"); return ret; } @@ -412,7 +416,8 @@ PHPAPI int php_stream_xport_recvfrom(php_stream *stream, char *buf, size_t bufle } if (stream->readfilters.head) { - php_error_docref(NULL, E_WARNING, "Cannot peek or fetch OOB data from a filtered stream"); + php_stream_warn(stream, STREAM_ERROR_CODE_FILTER_FAILED, + "Cannot peek or fetch OOB data from a filtered stream"); return -1; } @@ -482,7 +487,8 @@ PHPAPI int php_stream_xport_sendto(php_stream *stream, const char *buf, size_t b oob = (flags & STREAM_OOB) == STREAM_OOB; if ((oob || addr) && stream->writefilters.head) { - php_error_docref(NULL, E_WARNING, "Cannot write OOB data, or data to a targeted address on a filtered stream"); + php_stream_warn(stream, STREAM_ERROR_CODE_FILTER_FAILED, + "Cannot write OOB data, or data to a targeted address on a filtered stream"); return -1; } From 1ad8e4e5bb479f03f1ec23bebe06810611209792 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 17 Nov 2025 15:53:46 +0100 Subject: [PATCH 16/37] stream: store wrapper erros by name instead of ptr --- main/streams/stream_errors.c | 71 ++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/main/streams/stream_errors.c b/main/streams/stream_errors.c index be4faabd1162a..0879a02c68954 100644 --- a/main/streams/stream_errors.c +++ b/main/streams/stream_errors.c @@ -32,6 +32,7 @@ static void php_stream_error_entry_dtor(void *error) { php_stream_error_entry *entry = *(php_stream_error_entry **) error; zend_string_release(entry->message); + efree(entry->wrapper_name); efree(entry->docref); efree(entry->param); efree(entry); @@ -220,18 +221,22 @@ static php_stream_error_entry *php_stream_create_error_entry(zend_string *messag bool terminal) { php_stream_error_entry *entry = emalloc(sizeof(php_stream_error_entry)); - entry->message = message; /* Takes ownership */ + entry->message = message; entry->code = code; - entry->wrapper_name = wrapper_name; + entry->wrapper_name = wrapper_name ? estrdup(wrapper_name) : NULL; + entry->docref = docref ? estrdup(docref) : NULL; entry->param = param; entry->severity = severity; entry->terminal = terminal; + + zend_string_addref(message); + return entry; } /* Common storage function*/ static void php_stream_store_error_common(php_stream_context *context, php_stream *stream, - php_stream_wrapper *wrapper, zend_string *message, const char *docref, int code, + zend_string *message, const char *docref, int code, const char *wrapper_name, const char *param, int severity, bool terminal) { int error_mode = php_stream_get_error_mode(context); @@ -241,13 +246,8 @@ static void php_stream_store_error_common(php_stream_context *context, php_strea efree(param); return; } - - if (docref != NULL) { - docref = estrdup(docref); - } php_stream_error_entry *entry = php_stream_create_error_entry( message, code, wrapper_name, docref, param, severity, terminal); - zend_string_addref(message); /* Storage keeps a reference */ zend_llist *list; @@ -267,15 +267,15 @@ static void php_stream_store_error_common(php_stream_context *context, php_strea list = NULL; } else { list = zend_hash_str_find_ptr( - FG(wrapper_stored_errors), (const char *) &wrapper, sizeof(wrapper)); + FG(wrapper_stored_errors), wrapper_name, strlen(wrapper_name)); } if (!list) { zend_llist new_list; zend_llist_init( &new_list, sizeof(php_stream_error_entry *), php_stream_error_entry_dtor, 0); - list = zend_hash_str_update_mem(FG(wrapper_stored_errors), (const char *) &wrapper, - sizeof(wrapper), &new_list, sizeof(new_list)); + list = zend_hash_str_update_mem(FG(wrapper_stored_errors), wrapper_name, + strlen(wrapper_name), &new_list, sizeof(new_list)); } } @@ -283,13 +283,11 @@ static void php_stream_store_error_common(php_stream_context *context, php_strea } /* Wrapper error reporting - stores in FG(wrapper_stored_errors) */ - -static void php_stream_wrapper_error_internal(php_stream_wrapper *wrapper, +static void php_stream_wrapper_error_internal_with_name(const char *wrapper_name, php_stream_context *context, const char *docref, int options, int severity, bool terminal, int code, const char *param, const char *fmt, va_list args) { zend_string *message = vstrpprintf(0, fmt, args); - const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown"; if (options & REPORT_ERRORS) { php_stream_process_error(context, wrapper_name, NULL, docref, code, ZSTR_VAL(message), @@ -297,11 +295,32 @@ static void php_stream_wrapper_error_internal(php_stream_wrapper *wrapper, } php_stream_store_error_common( - context, NULL, wrapper, message, docref, code, wrapper_name, param, severity, terminal); + context, NULL, message, docref, code, wrapper_name, param, severity, terminal); zend_string_release(message); } +static void php_stream_wrapper_error_internal(php_stream_wrapper *wrapper, + php_stream_context *context, const char *docref, int options, int severity, bool terminal, + int code, const char *param, const char *fmt, va_list args) +{ + const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown"; + + php_stream_wrapper_error_internal_with_name(wrapper_name, context, docref, options, severity, + terminal, code, param, fmt, args); +} + +PHPAPI void php_stream_wrapper_error_with_name(const char *wrapper_name, + php_stream_context *context, const char *docref, int options, int severity, bool terminal, + int code, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + php_stream_wrapper_error_internal_with_name( + wrapper_name, context, docref, options, severity, terminal, code, NULL, fmt, args); + va_end(args); +} + PHPAPI void php_stream_wrapper_error(php_stream_wrapper *wrapper, php_stream_context *context, const char *docref, int options, int severity, bool terminal, int code, const char *fmt, ...) @@ -343,16 +362,14 @@ PHPAPI void php_stream_wrapper_error_param2(php_stream_wrapper *wrapper, /* Wrapper error logging - stores in FG(wrapper_logged_errors) */ -static void php_stream_wrapper_log_store_error(const php_stream_wrapper *wrapper, - zend_string *message, int code, const char *wrapper_name, const char *param, int severity, - bool terminal) +static void php_stream_wrapper_log_store_error(zend_string *message, int code, + const char *wrapper_name, const char *param, int severity, bool terminal) { if (param != NULL) { param = estrdup(param); } php_stream_error_entry *entry = php_stream_create_error_entry( message, code, wrapper_name, NULL, param, severity, terminal); - zend_string_addref(message); if (!FG(wrapper_logged_errors)) { ALLOC_HASHTABLE(FG(wrapper_logged_errors)); @@ -360,14 +377,14 @@ static void php_stream_wrapper_log_store_error(const php_stream_wrapper *wrapper } zend_llist *list = zend_hash_str_find_ptr( - FG(wrapper_logged_errors), (const char *) &wrapper, sizeof(wrapper)); + FG(wrapper_logged_errors), wrapper_name, strlen(wrapper_name)); if (!list) { zend_llist new_list; zend_llist_init( &new_list, sizeof(php_stream_error_entry *), php_stream_error_entry_dtor, 0); - list = zend_hash_str_update_mem(FG(wrapper_logged_errors), (const char *) &wrapper, - sizeof(wrapper), &new_list, sizeof(new_list)); + list = zend_hash_str_update_mem(FG(wrapper_logged_errors), wrapper_name, + strlen(wrapper_name), &new_list, sizeof(new_list)); } zend_llist_add_element(list, &entry); @@ -382,13 +399,13 @@ static void php_stream_wrapper_log_error_internal(const php_stream_wrapper *wrap if (options & REPORT_ERRORS) { /* Report immediately using standard error functions */ - php_stream_wrapper_error_internal( - wrapper, context, options, severity, terminal, code, param, fmt, args); + php_stream_wrapper_error_internal_with_name( + wrapper_name, context, NULL, options, severity, terminal, code, param, fmt, args); zend_string_release(message); } else { /* Store for later display in FG(wrapper_logged_errors) */ php_stream_wrapper_log_store_error( - wrapper, message, code, wrapper_name, param, severity, terminal); + message, code, wrapper_name, param, severity, terminal); zend_string_release(message); } } @@ -523,8 +540,8 @@ PHPAPI void php_stream_error(php_stream *stream, const char *docref, int severit severity, terminal); /* Store error */ - php_stream_store_error_common(context, stream, wrapper, message, docref, code, wrapper_name, - NULL, severity, terminal); + php_stream_store_error_common(context, stream, message, docref, code, wrapper_name, NULL, + severity, terminal); zend_string_release(message); } From 9fde31d5153a528ca0945379da321a06dd7128e6 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 17 Nov 2025 16:53:50 +0100 Subject: [PATCH 17/37] stream: convert user stream wrapper errors --- main/streams/php_stream_errors.h | 135 ++++++++-------------- main/streams/stream_errors.stub.php | 30 +++++ main/streams/stream_errors_arginfo.h | 10 +- main/streams/userspace.c | 162 +++++++++++++++++---------- 4 files changed, 186 insertions(+), 151 deletions(-) diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h index cb1c69556355b..f015a01e4994d 100644 --- a/main/streams/php_stream_errors.h +++ b/main/streams/php_stream_errors.h @@ -72,18 +72,20 @@ BEGIN_EXTERN_C() #define STREAM_ERROR_CODE_COPY_FAILED 47 #define STREAM_ERROR_CODE_TOUCH_FAILED 48 #define STREAM_ERROR_CODE_INVALID_MODE 49 -#define STREAM_ERROR_CODE_MODE_NOT_SUPPORTED 50 -#define STREAM_ERROR_CODE_READONLY 51 -#define STREAM_ERROR_CODE_RECURSION_DETECTED 52 +#define STREAM_ERROR_CODE_INVALID_META 50 +#define STREAM_ERROR_CODE_MODE_NOT_SUPPORTED 51 +#define STREAM_ERROR_CODE_READONLY 52 +#define STREAM_ERROR_CODE_RECURSION_DETECTED 53 /* Wrapper/protocol operations */ -#define STREAM_ERROR_CODE_NO_OPENER 70 -#define STREAM_ERROR_CODE_PERSISTENT_NOT_SUPPORTED 71 -#define STREAM_ERROR_CODE_WRAPPER_NOT_FOUND 72 -#define STREAM_ERROR_CODE_WRAPPER_DISABLED 73 -#define STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED 74 -#define STREAM_ERROR_CODE_WRAPPER_REGISTRATION_FAILED 75 -#define STREAM_ERROR_CODE_WRAPPER_UNREGISTRATION_FAILED 76 -#define STREAM_ERROR_CODE_WRAPPER_RESTORATION_FAILED 77 +#define STREAM_ERROR_CODE_NOT_IMPLEMENTED 70 +#define STREAM_ERROR_CODE_NO_OPENER 71 +#define STREAM_ERROR_CODE_PERSISTENT_NOT_SUPPORTED 72 +#define STREAM_ERROR_CODE_WRAPPER_NOT_FOUND 73 +#define STREAM_ERROR_CODE_WRAPPER_DISABLED 74 +#define STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED 75 +#define STREAM_ERROR_CODE_WRAPPER_REGISTRATION_FAILED 76 +#define STREAM_ERROR_CODE_WRAPPER_UNREGISTRATION_FAILED 77 +#define STREAM_ERROR_CODE_WRAPPER_RESTORATION_FAILED 78 /* Filter operations */ #define STREAM_ERROR_CODE_FILTER_NOT_FOUND 90 #define STREAM_ERROR_CODE_FILTER_FAILED 91 @@ -120,12 +122,13 @@ BEGIN_EXTERN_C() /* Userspace stream operations */ #define STREAM_ERROR_CODE_USERSPACE_NOT_IMPLEMENTED 160 #define STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN 161 +#define STREAM_ERROR_CODE_USERSPACE_CALL_FAILED 162 /* Stored error entry */ typedef struct { zend_string *message; int code; - const char *wrapper_name; /* Points to wrapper->wops->label, no need to duplicate */ + const char *wrapper_name; const char *docref; const char *param; int severity; @@ -133,84 +136,37 @@ typedef struct { } php_stream_error_entry; /* Main error reporting functions */ -PHPAPI void php_stream_wrapper_error( - php_stream_wrapper *wrapper, - php_stream_context *context, - const char *docref, - int options, - int severity, - bool terminal, - int code, - const char *fmt, - ... -) ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); - -PHPAPI void php_stream_wrapper_error_param( - php_stream_wrapper *wrapper, - php_stream_context *context, - const char *docref, - int options, - int severity, - bool terminal, - int code, - const char *param, - const char *fmt, - ... -) ZEND_ATTRIBUTE_FORMAT(printf, 9, 10); - -PHPAPI void php_stream_wrapper_error_param2( - php_stream_wrapper *wrapper, - php_stream_context *context, - const char *docref, - int options, - int severity, - bool terminal, - int code, - const char *param1, - const char *param2, - const char *fmt, - ... -) ZEND_ATTRIBUTE_FORMAT(printf, 10, 11); - -PHPAPI void php_stream_error( - php_stream *stream, - const char *docref, - int severity, - bool terminal, - int code, - const char *fmt, - ... -) ZEND_ATTRIBUTE_FORMAT(printf, 6, 7); +PHPAPI void php_stream_wrapper_error_with_name(const char *wrapper_name, + php_stream_context *context, const char *docref, int options, int severity, bool terminal, + int code, const char *fmt, ...) ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); + +PHPAPI void php_stream_wrapper_error(php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminal, int code, const char *fmt, + ...) ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); + +PHPAPI void php_stream_wrapper_error_param(php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminal, int code, const char *param, + const char *fmt, ...) ZEND_ATTRIBUTE_FORMAT(printf, 9, 10); + +PHPAPI void php_stream_wrapper_error_param2(php_stream_wrapper *wrapper, + php_stream_context *context, const char *docref, int options, int severity, bool terminal, + int code, const char *param1, const char *param2, const char *fmt, ...) + ZEND_ATTRIBUTE_FORMAT(printf, 10, 11); + +PHPAPI void php_stream_error(php_stream *stream, const char *docref, int severity, bool terminal, + int code, const char *fmt, ...) ZEND_ATTRIBUTE_FORMAT(printf, 6, 7); /* Legacy wrapper error log - updated API */ -PHPAPI void php_stream_wrapper_log_error( - const php_stream_wrapper *wrapper, - php_stream_context *context, - int options, - int severity, - bool terminal, - int code, - const char *fmt, - ... -) ZEND_ATTRIBUTE_FORMAT(printf, 7, 8); - -PHPAPI void php_stream_wrapper_log_error_param( - const php_stream_wrapper *wrapper, - php_stream_context *context, - int options, - int severity, - bool terminal, - int code, - const char *param, - const char *fmt, - ... -) ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); - -PHPAPI void php_stream_display_wrapper_errors( - php_stream_wrapper *wrapper, - const char *path, - const char *caption -); +PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminal, int code, + const char *fmt, ...) ZEND_ATTRIBUTE_FORMAT(printf, 7, 8); + +PHPAPI void php_stream_wrapper_log_error_param(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminal, int code, + const char *param, const char *fmt, ...) ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); + +PHPAPI void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, + const char *path, const char *caption); PHPAPI void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper); @@ -221,6 +177,9 @@ void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper); #define php_stream_wrapper_warn(wrapper, context, options, code, ...) \ php_stream_wrapper_error(wrapper, context, NULL, options, E_WARNING, true, code, __VA_ARGS__) +#define php_stream_wrapper_warn_name(wrapper_name, context, options, code, ...) \ + php_stream_wrapper_error_with_name(wrapper_name, context, NULL, options, E_WARNING, true, code, __VA_ARGS__) + #define php_stream_wrapper_warn_nt(wrapper, context, options, code, ...) \ php_stream_wrapper_error(wrapper, context, NULL, options, E_WARNING, false, code, __VA_ARGS__) diff --git a/main/streams/stream_errors.stub.php b/main/streams/stream_errors.stub.php index 802a90e00fb8d..8a27e0060fc3d 100644 --- a/main/streams/stream_errors.stub.php +++ b/main/streams/stream_errors.stub.php @@ -51,6 +51,21 @@ public function getWrapperName(): string {} * @cvalue STREAM_ERROR_CODE_TRUNCATE_FAILED */ const STREAM_ERROR_CODE_TRUNCATE_FAILED = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_CONNECT_FAILED + */ +const STREAM_ERROR_CODE_CONNECT_FAILED = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_BIND_FAILED + */ +const STREAM_ERROR_CODE_BIND_FAILED = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_LISTEN_FAILED + */ +const STREAM_ERROR_CODE_LISTEN_FAILED = UNKNOWN; /** * @var int * @cvalue STREAM_ERROR_CODE_NOT_WRITABLE @@ -161,6 +176,11 @@ public function getWrapperName(): string {} * @cvalue STREAM_ERROR_CODE_INVALID_MODE */ const STREAM_ERROR_CODE_INVALID_MODE = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_INVALID_META + */ +const STREAM_ERROR_CODE_INVALID_META = UNKNOWN; /** * @var int * @cvalue STREAM_ERROR_CODE_MODE_NOT_SUPPORTED @@ -176,6 +196,11 @@ public function getWrapperName(): string {} * @cvalue STREAM_ERROR_CODE_RECURSION_DETECTED */ const STREAM_ERROR_CODE_RECURSION_DETECTED = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_NOT_IMPLEMENTED + */ +const STREAM_ERROR_CODE_NOT_IMPLEMENTED = UNKNOWN; /** * @var int * @cvalue STREAM_ERROR_CODE_NO_OPENER @@ -361,3 +386,8 @@ public function getWrapperName(): string {} * @cvalue STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN */ const STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN = UNKNOWN; +/** + * @var int + * @cvalue STREAM_ERROR_CODE_USERSPACE_CALL_FAILED + */ +const STREAM_ERROR_CODE_USERSPACE_CALL_FAILED = UNKNOWN; diff --git a/main/streams/stream_errors_arginfo.h b/main/streams/stream_errors_arginfo.h index 5859f825c4ee4..523cf64323a0e 100644 --- a/main/streams/stream_errors_arginfo.h +++ b/main/streams/stream_errors_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: c14298e97a4960fe35b7b1b464a5b0b94099897f */ + * Stub hash: faa3f0e64b8388a495ac645fd3eda49c1ed243bd */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_StreamException_getParam, 0, 0, IS_STRING, 1) ZEND_END_ARG_INFO() @@ -26,6 +26,9 @@ static void register_stream_errors_symbols(int module_number) REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_SEEK_NOT_SUPPORTED", STREAM_ERROR_CODE_SEEK_NOT_SUPPORTED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_FLUSH_FAILED", STREAM_ERROR_CODE_FLUSH_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_TRUNCATE_FAILED", STREAM_ERROR_CODE_TRUNCATE_FAILED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_CONNECT_FAILED", STREAM_ERROR_CODE_CONNECT_FAILED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_BIND_FAILED", STREAM_ERROR_CODE_BIND_FAILED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_LISTEN_FAILED", STREAM_ERROR_CODE_LISTEN_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_NOT_WRITABLE", STREAM_ERROR_CODE_NOT_WRITABLE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_NOT_READABLE", STREAM_ERROR_CODE_NOT_READABLE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_DISABLED", STREAM_ERROR_CODE_DISABLED, CONST_PERSISTENT); @@ -48,9 +51,13 @@ static void register_stream_errors_symbols(int module_number) REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_COPY_FAILED", STREAM_ERROR_CODE_COPY_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_TOUCH_FAILED", STREAM_ERROR_CODE_TOUCH_FAILED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_INVALID_MODE", STREAM_ERROR_CODE_INVALID_MODE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_INVALID_META", STREAM_ERROR_CODE_INVALID_META, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_MODE_NOT_SUPPORTED", STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_READONLY", STREAM_ERROR_CODE_READONLY, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_RECURSION_DETECTED", STREAM_ERROR_CODE_RECURSION_DETECTED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_NOT_IMPLEMENTED", STREAM_ERROR_CODE_NOT_IMPLEMENTED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_NO_OPENER", STREAM_ERROR_CODE_NO_OPENER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_PERSISTENT_NOT_SUPPORTED", STREAM_ERROR_CODE_PERSISTENT_NOT_SUPPORTED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_WRAPPER_NOT_FOUND", STREAM_ERROR_CODE_WRAPPER_NOT_FOUND, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_WRAPPER_DISABLED", STREAM_ERROR_CODE_WRAPPER_DISABLED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED", STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED, CONST_PERSISTENT); @@ -86,6 +93,7 @@ static void register_stream_errors_symbols(int module_number) REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_LOCK_NOT_SUPPORTED", STREAM_ERROR_CODE_LOCK_NOT_SUPPORTED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_USERSPACE_NOT_IMPLEMENTED", STREAM_ERROR_CODE_USERSPACE_NOT_IMPLEMENTED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN", STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_USERSPACE_CALL_FAILED", STREAM_ERROR_CODE_USERSPACE_CALL_FAILED, CONST_PERSISTENT); } static zend_class_entry *register_class_StreamException(zend_class_entry *class_entry_Exception) diff --git a/main/streams/userspace.c b/main/streams/userspace.c index f5e25aa96c772..4683dd270ab39 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -293,7 +293,8 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char * /* Try to catch bad usage without preventing flexibility */ if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) { - php_stream_wrapper_log_error(wrapper, options, "infinite recursion prevented"); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_RECURSION_DETECTED, "infinite recursion prevented"); return NULL; } FG(user_stream_current_filename) = filename; @@ -334,8 +335,8 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char * zval_ptr_dtor(&args[0]); if (UNEXPECTED(call_result == FAILURE)) { - php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_OPEN "\" is not implemented", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_wrapper_log_warn(wrapper, context, options,STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "\"%s::" USERSTREAM_OPEN "\" is not implemented", ZSTR_VAL(us->wrapper->ce->name)); zval_ptr_dtor(&args[3]); goto end; } @@ -357,8 +358,9 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char * /* set wrapper data to be a reference to our object */ ZVAL_COPY(&stream->wrapperdata, &us->object); } else { - php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_OPEN "\" call failed", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_USERSPACE_CALL_FAILED, + "\"%s::" USERSTREAM_OPEN "\" call failed", ZSTR_VAL(us->wrapper->ce->name)); } zval_ptr_dtor(&zretval); @@ -394,7 +396,8 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char /* Try to catch bad usage without preventing flexibility */ if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) { - php_stream_wrapper_log_error(wrapper, options, "infinite recursion prevented"); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_RECURSION_DETECTED, "infinite recursion prevented"); return NULL; } FG(user_stream_current_filename) = filename; @@ -419,8 +422,9 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char zval_ptr_dtor(&args[0]); 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)); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "\"%s::" USERSTREAM_DIR_OPEN "\" is not implemented", + ZSTR_VAL(us->wrapper->ce->name)); goto end; } /* Exception occurred in call */ @@ -435,8 +439,9 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char /* set wrapper data to be a reference to our object */ ZVAL_COPY(&stream->wrapperdata, &us->object); } else { - php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_DIR_OPEN "\" call failed", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_wrapper_log_warn(wrapper, context, options, + STREAM_ERROR_CODE_USERSPACE_CALL_FAILED, + "\"%s::" USERSTREAM_DIR_OPEN "\" call failed", ZSTR_VAL(us->wrapper->ce->name)); } zval_ptr_dtor(&zretval); @@ -479,10 +484,15 @@ PHP_FUNCTION(stream_wrapper_register) /* We failed. But why? */ if (zend_hash_exists(php_stream_get_url_stream_wrappers_hash(), protocol)) { - php_error_docref(NULL, E_WARNING, "Protocol %s:// is already defined.", ZSTR_VAL(protocol)); + php_stream_wrapper_warn(&uwrap->wrapper, NULL, REPORT_ERRORS, + STREAM_ERROR_CODE_WRAPPER_REGISTRATION_FAILED, + "Protocol %s:// is already defined.", ZSTR_VAL(protocol)); } else { /* Hash doesn't exist so it must have been an invalid protocol scheme */ - php_error_docref(NULL, E_WARNING, "Invalid protocol scheme specified. Unable to register wrapper class %s to %s://", ZSTR_VAL(uwrap->ce->name), ZSTR_VAL(protocol)); + php_stream_wrapper_warn(&uwrap->wrapper, NULL, REPORT_ERRORS, + STREAM_ERROR_CODE_WRAPPER_REGISTRATION_FAILED, + "Invalid protocol scheme specified. Unable to register wrapper class %s to %s://", + ZSTR_VAL(uwrap->ce->name), ZSTR_VAL(protocol)); } zend_list_delete(rsrc); @@ -502,7 +512,9 @@ PHP_FUNCTION(stream_wrapper_unregister) php_stream_wrapper *wrapper = zend_hash_find_ptr(php_stream_get_url_stream_wrappers_hash(), protocol); if (php_unregister_url_stream_wrapper_volatile(protocol) == FAILURE) { /* We failed */ - php_error_docref(NULL, E_WARNING, "Unable to unregister protocol %s://", ZSTR_VAL(protocol)); + php_stream_wrapper_warn(wrapper, NULL, REPORT_ERRORS, + STREAM_ERROR_CODE_WRAPPER_UNREGISTRATION_FAILED, + "Unable to unregister protocol %s://", ZSTR_VAL(protocol)); RETURN_FALSE; } @@ -530,13 +542,17 @@ PHP_FUNCTION(stream_wrapper_restore) global_wrapper_hash = php_stream_get_url_stream_wrappers_hash_global(); if ((wrapper = zend_hash_find_ptr(global_wrapper_hash, protocol)) == NULL) { - php_error_docref(NULL, E_WARNING, "%s:// never existed, nothing to restore", ZSTR_VAL(protocol)); + php_stream_wrapper_warn_name(user_stream_wops.label, NULL, REPORT_ERRORS, + STREAM_ERROR_CODE_WRAPPER_NOT_FOUND, + "%s:// never existed, nothing to restore", ZSTR_VAL(protocol)); RETURN_FALSE; } wrapper_hash = php_stream_get_url_stream_wrappers_hash(); if (wrapper_hash == global_wrapper_hash || zend_hash_find_ptr(wrapper_hash, protocol) == wrapper) { - php_error_docref(NULL, E_NOTICE, "%s:// was never changed, nothing to restore", ZSTR_VAL(protocol)); + php_stream_wrapper_notice(wrapper, NULL, REPORT_ERRORS, + STREAM_ERROR_CODE_WRAPPER_RESTORATION_FAILED, + "%s:// was never changed, nothing to restore", ZSTR_VAL(protocol)); RETURN_TRUE; } @@ -544,7 +560,9 @@ PHP_FUNCTION(stream_wrapper_restore) php_unregister_url_stream_wrapper_volatile(protocol); if (php_register_url_stream_wrapper_volatile(protocol, wrapper) == FAILURE) { - php_error_docref(NULL, E_WARNING, "Unable to restore original %s:// wrapper", ZSTR_VAL(protocol)); + php_stream_wrapper_warn(wrapper, NULL, REPORT_ERRORS, + STREAM_ERROR_CODE_WRAPPER_RESTORATION_FAILED, + "Unable to restore original %s:// wrapper", ZSTR_VAL(protocol)); RETURN_FALSE; } @@ -572,8 +590,8 @@ static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_ zval_ptr_dtor(&args[0]); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_WRITE " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_WRITE " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); } stream->flags &= ~PHP_STREAM_FLAG_NO_FCLOSE; @@ -593,7 +611,9 @@ static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_ /* don't allow strange buffer overruns due to bogus return */ if (didwrite > 0 && didwrite > count) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_WRITE " wrote " ZEND_LONG_FMT " bytes more data than requested (" ZEND_LONG_FMT " written, " ZEND_LONG_FMT " max)", + php_stream_warn_nt(stream, REPORT_ERRORS, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + "%s::" USERSTREAM_WRITE " wrote " ZEND_LONG_FMT " bytes more data than requested (" + ZEND_LONG_FMT " written, " ZEND_LONG_FMT " max)", ZSTR_VAL(us->wrapper->ce->name), (zend_long)(didwrite - count), (zend_long)didwrite, (zend_long)count); didwrite = count; @@ -624,8 +644,8 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count } if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_READ " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_READ " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); goto err; } @@ -641,8 +661,12 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count didread = Z_STRLEN(retval); if (didread > 0) { if (didread > count) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_READ " - read " ZEND_LONG_FMT " bytes more data than requested (" ZEND_LONG_FMT " read, " ZEND_LONG_FMT " max) - excess data will be lost", - ZSTR_VAL(us->wrapper->ce->name), (zend_long)(didread - count), (zend_long)didread, (zend_long)count); + php_stream_warn_nt(stream, REPORT_ERRORS, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + "%s::" USERSTREAM_READ " - read " ZEND_LONG_FMT + " bytes more data than requested (" ZEND_LONG_FMT " read, " + ZEND_LONG_FMT " max) - excess data will be lost", + ZSTR_VAL(us->wrapper->ce->name), (zend_long)(didread - count), + (zend_long)didread, (zend_long)count); didread = count; } memcpy(buf, Z_STRVAL(retval), didread); @@ -658,7 +682,7 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, + php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF", ZSTR_VAL(us->wrapper->ce->name)); stream->eof = 1; @@ -774,7 +798,8 @@ static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int when *newoffs = Z_LVAL(retval); ret = 0; } else if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_TELL " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_TELL " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); ret = -1; } else { ret = -1; @@ -838,8 +863,8 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb) zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STAT " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_STAT " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return -1; } if (UNEXPECTED(Z_ISUNDEF(retval))) { @@ -857,7 +882,7 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb) return ret; } -static int user_stream_set_check_liveliness(const php_userstream_data_t *us) +static int user_stream_set_check_liveliness(php_stream *stream, const php_userstream_data_t *us) { zval retval; @@ -866,7 +891,8 @@ static int user_stream_set_check_liveliness(const php_userstream_data_t *us) zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, + php_stream_warn(stream, REPORT_ERRORS, + STREAM_ERROR_CODE_NOT_IMPLEMENTED, "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; @@ -877,15 +903,16 @@ static int user_stream_set_check_liveliness(const php_userstream_data_t *us) 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", + php_stream_warn(stream, REPORT_ERRORS, + STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + "%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); return PHP_STREAM_OPTION_RETURN_ERR; } } -static int user_stream_set_locking(const php_userstream_data_t *us, int value) +static int user_stream_set_locking(php_stream *stream, const php_userstream_data_t *us, int value) { zval retval; zval zlock; @@ -920,9 +947,8 @@ static int user_stream_set_locking(const php_userstream_data_t *us, int value) /* lock support test (TODO: more check) */ return PHP_STREAM_OPTION_RETURN_OK; } - php_error_docref(NULL, E_WARNING, - "%s::" USERSTREAM_LOCK " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_LOCK " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; } if (UNEXPECTED(Z_ISUNDEF(retval))) { @@ -934,14 +960,15 @@ static int user_stream_set_locking(const php_userstream_data_t *us, int value) } // 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", + //php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + // "%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; } -static int user_stream_set_truncation(const php_userstream_data_t *us, int value, void *ptrparam) { +static int user_stream_set_truncation(php_stream *stream, const php_userstream_data_t *us, + int value, void *ptrparam) { zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_TRUNCATE, false); if (value == PHP_STREAM_TRUNCATE_SUPPORTED) { @@ -969,9 +996,8 @@ static int user_stream_set_truncation(const php_userstream_data_t *us, int value zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, - "%s::" USERSTREAM_TRUNCATE " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_TRUNCATE " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; } if (UNEXPECTED(Z_ISUNDEF(retval))) { @@ -980,15 +1006,16 @@ static int user_stream_set_truncation(const php_userstream_data_t *us, int value 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", + php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + "%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) +static int user_stream_set_option(php_stream *stream, const php_userstream_data_t *us, int option, + int value, void *ptrparam) { zval args[3]; ZVAL_LONG(&args[0], option); @@ -1013,7 +1040,7 @@ static int user_stream_set_option(const php_userstream_data_t *us, int option, i zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, + php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, "%s::" USERSTREAM_SET_OPTION " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; @@ -1038,19 +1065,19 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value switch (option) { case PHP_STREAM_OPTION_CHECK_LIVENESS: - return user_stream_set_check_liveliness(us); + return user_stream_set_check_liveliness(stream, us); case PHP_STREAM_OPTION_LOCKING: - return user_stream_set_locking(us, value); + return user_stream_set_locking(stream, us, value); case PHP_STREAM_OPTION_TRUNCATE_API: - return user_stream_set_truncation(us, value, ptrparam); + return user_stream_set_truncation(stream, 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); + return user_stream_set_option(stream, us, option, value, ptrparam); default: return PHP_STREAM_OPTION_RETURN_NOTIMPL; @@ -1083,7 +1110,8 @@ static int user_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_UNLINK " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%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; } @@ -1121,7 +1149,8 @@ static int user_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_RENAME " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%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; } @@ -1159,7 +1188,8 @@ static int user_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url, int zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_MKDIR " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%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; } @@ -1196,7 +1226,8 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_RMDIR " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%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; } @@ -1235,7 +1266,9 @@ static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url, i ZVAL_STRING(&args[2], value); break; default: - php_error_docref(NULL, E_WARNING, "Unknown option %d for " USERSTREAM_METADATA, option); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, + STREAM_ERROR_CODE_INVALID_META, + "Unknown option %d for " USERSTREAM_METADATA, option); return ret; } @@ -1258,7 +1291,8 @@ static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url, i zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_METADATA " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%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; } @@ -1296,8 +1330,8 @@ static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char *url, i zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STATURL " is not implemented!", - ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_STATURL " is not implemented!", ZSTR_VAL(uwrap->ce->name)); return -1; } if (UNEXPECTED(Z_ISUNDEF(zretval))) { @@ -1332,8 +1366,9 @@ static ssize_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t co zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_DIR_READ " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_DIR_READ " is not implemented!", + ZSTR_VAL(us->wrapper->ce->name)); return -1; } if (UNEXPECTED(Z_ISUNDEF(retval))) { @@ -1418,7 +1453,8 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr) if (UNEXPECTED(call_result == FAILURE)) { if (report_errors) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " is not implemented!", + php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + "%s::" USERSTREAM_CAST " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); } goto out; @@ -1432,14 +1468,16 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr) php_stream_from_zval_no_verify(intstream, &retval); if (!intstream) { if (report_errors) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " must return a stream resource", + php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + "%s::" USERSTREAM_CAST " must return a stream resource", ZSTR_VAL(us->wrapper->ce->name)); } break; } if (intstream == stream) { if (report_errors) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " must not return itself", + php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + "%s::" USERSTREAM_CAST " must not return itself", ZSTR_VAL(us->wrapper->ce->name)); } intstream = NULL; From 310f0cb3c5acc5c3030bb7ac80f4f746e9f40336 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 17 Nov 2025 17:03:17 +0100 Subject: [PATCH 18/37] stream: convert xp_socket errors --- main/streams/xp_socket.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c index db35a9b7952c8..4d4428442a867 100644 --- a/main/streams/xp_socket.c +++ b/main/streams/xp_socket.c @@ -106,9 +106,8 @@ static ssize_t php_sockop_write(php_stream *stream, const char *buf, size_t coun if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { estr = php_socket_strerror(err, NULL, 0); - php_error_docref(NULL, E_NOTICE, - "Send of %zu bytes failed with errno=%d %s", - count, err, estr); + php_stream_warn(stream, STREAM_ERROR_CODE_NETWORK_SEND_FAILED, + "Send of %zu bytes failed with errno=%d %s", count, err, estr); efree(estr); } } @@ -444,8 +443,7 @@ static int php_sockop_set_option(php_stream *stream, int option, int value, void xparam->inputs.addrlen); if (xparam->outputs.returncode == -1) { char *err = php_socket_strerror(php_socket_errno(), NULL, 0); - php_error_docref(NULL, E_WARNING, - "%s\n", err); + php_stream_warn(stream, STREAM_ERROR_CODE_NETWORK_SEND_FAILED, "%s", err); efree(err); } return PHP_STREAM_OPTION_RETURN_OK; @@ -585,7 +583,8 @@ static const php_stream_ops php_stream_unixdg_socket_ops = { /* network socket operations */ #ifdef AF_UNIX -static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr) +static inline int parse_unix_address(php_stream *stream, php_stream_xport_param *xparam, + struct sockaddr_un *unix_addr) { memset(unix_addr, 0, sizeof(*unix_addr)); unix_addr->sun_family = AF_UNIX; @@ -604,9 +603,9 @@ static inline int parse_unix_address(php_stream_xport_param *xparam, struct sock * BUT, to get into this branch of code, the name is too long, * so we don't care. */ xparam->inputs.namelen = max_length; - php_error_docref(NULL, E_NOTICE, - "socket path exceeded the maximum allowed length of %lu bytes " - "and was truncated", max_length); + php_stream_notice(stream, STREAM_ERROR_CODE_INVALID_PATH, + "socket path exceeded the maximum allowed length of %lu bytes and was truncated", + max_length); } memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen); @@ -686,7 +685,7 @@ static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t * return -1; } - parse_unix_address(xparam, &unix_addr); + parse_unix_address(stream, xparam, &unix_addr); return bind(sock->socket, (const struct sockaddr *)&unix_addr, (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen); @@ -775,7 +774,7 @@ static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_ return -1; } - parse_unix_address(xparam, &unix_addr); + parse_unix_address(stream, xparam, &unix_addr); ret = php_network_connect_socket(sock->socket, (const struct sockaddr *)&unix_addr, (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen, From fc806a306bbb36a355301e4d739bab32d08942c3 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 17 Nov 2025 17:41:03 +0100 Subject: [PATCH 19/37] stream: fix compilation issues in core stream errors --- main/streams/php_stream_errors.h | 6 +++--- main/streams/stream_errors.c | 33 +++++++++++++------------------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h index f015a01e4994d..66d705c2bc3bc 100644 --- a/main/streams/php_stream_errors.h +++ b/main/streams/php_stream_errors.h @@ -128,9 +128,9 @@ BEGIN_EXTERN_C() typedef struct { zend_string *message; int code; - const char *wrapper_name; - const char *docref; - const char *param; + char *wrapper_name; + char *docref; + char *param; int severity; bool terminal; } php_stream_error_entry; diff --git a/main/streams/stream_errors.c b/main/streams/stream_errors.c index 0879a02c68954..ec403acedfe5a 100644 --- a/main/streams/stream_errors.c +++ b/main/streams/stream_errors.c @@ -25,9 +25,6 @@ /* StreamException class entry */ static zend_class_entry *php_ce_stream_exception; -/* Error code registry */ -static HashTable *php_stream_wrapper_error_codes = NULL; - static void php_stream_error_entry_dtor(void *error) { php_stream_error_entry *entry = *(php_stream_error_entry **) error; @@ -217,7 +214,7 @@ static void php_stream_process_error(php_stream_context *context, const char *wr /* Helper to create error entry */ static php_stream_error_entry *php_stream_create_error_entry(zend_string *message, int code, - const char *wrapper_name, const char *docref, const char *param, int severity, + const char *wrapper_name, const char *docref, char *param, int severity, bool terminal) { php_stream_error_entry *entry = emalloc(sizeof(php_stream_error_entry)); @@ -237,7 +234,7 @@ static php_stream_error_entry *php_stream_create_error_entry(zend_string *messag /* Common storage function*/ static void php_stream_store_error_common(php_stream_context *context, php_stream *stream, zend_string *message, const char *docref, int code, - const char *wrapper_name, const char *param, int severity, bool terminal) + const char *wrapper_name, char *param, int severity, bool terminal) { int error_mode = php_stream_get_error_mode(context); int store_mode = php_stream_get_error_store_mode(context, error_mode); @@ -285,7 +282,7 @@ static void php_stream_store_error_common(php_stream_context *context, php_strea /* Wrapper error reporting - stores in FG(wrapper_stored_errors) */ static void php_stream_wrapper_error_internal_with_name(const char *wrapper_name, php_stream_context *context, const char *docref, int options, int severity, bool terminal, - int code, const char *param, const char *fmt, va_list args) + int code, char *param, const char *fmt, va_list args) { zend_string *message = vstrpprintf(0, fmt, args); @@ -302,7 +299,7 @@ static void php_stream_wrapper_error_internal_with_name(const char *wrapper_name static void php_stream_wrapper_error_internal(php_stream_wrapper *wrapper, php_stream_context *context, const char *docref, int options, int severity, bool terminal, - int code, const char *param, const char *fmt, va_list args) + int code, char *param, const char *fmt, va_list args) { const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown"; @@ -338,11 +335,9 @@ PHPAPI void php_stream_wrapper_error_param(php_stream_wrapper *wrapper, php_stre { va_list args; va_start(args, fmt); - if (param = NULL) { - param = estrdup(param); - } + char *param_copy = param ? estrdup(param): NULL; php_stream_wrapper_error_internal( - wrapper, context, docref, options, severity, terminal, code, param, fmt, args); + wrapper, context, docref, options, severity, terminal, code, param_copy, fmt, args); va_end(args); } @@ -365,11 +360,9 @@ PHPAPI void php_stream_wrapper_error_param2(php_stream_wrapper *wrapper, static void php_stream_wrapper_log_store_error(zend_string *message, int code, const char *wrapper_name, const char *param, int severity, bool terminal) { - if (param != NULL) { - param = estrdup(param); - } + char *param_copy = param ? estrdup(param): NULL; php_stream_error_entry *entry = php_stream_create_error_entry( - message, code, wrapper_name, NULL, param, severity, terminal); + message, code, wrapper_name, NULL, param_copy, severity, terminal); if (!FG(wrapper_logged_errors)) { ALLOC_HASHTABLE(FG(wrapper_logged_errors)); @@ -392,9 +385,9 @@ static void php_stream_wrapper_log_store_error(zend_string *message, int code, static void php_stream_wrapper_log_error_internal(const php_stream_wrapper *wrapper, php_stream_context *context, int options, int severity, bool terminal, int code, - const char *param, const char *fmt, va_list args) + char *param, const char *fmt, va_list args) { - zend_string *message = vstrpprintf(0, args, args); + zend_string *message = vstrpprintf(0, fmt, args); const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown"; if (options & REPORT_ERRORS) { @@ -427,8 +420,9 @@ PHPAPI void php_stream_wrapper_log_error_param(const php_stream_wrapper *wrapper { va_list args; va_start(args, fmt); + char *param_copy = param ? estrdup(param): NULL; php_stream_wrapper_log_error_internal( - wrapper, context, options, severity, terminal, code, param, fmt, args); + wrapper, context, options, severity, terminal, code, param_copy, fmt, args); va_end(args); } @@ -442,8 +436,7 @@ static zend_llist *php_stream_get_wrapper_errors_list(php_stream_wrapper *wrappe } } -/* {{{ wrapper error reporting */ -static void php_stream_display_wrapper_errors( +void php_stream_display_wrapper_errors( php_stream_wrapper *wrapper, const char *path, const char *caption) { char *tmp; From 49b634106ce5e279c5bbf67ef31c03f0011e486b Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 17 Nov 2025 19:51:55 +0100 Subject: [PATCH 20/37] stream: extend and fix php_stream_display_wrapper_errors --- main/streams/php_stream_errors.h | 5 +---- main/streams/stream_errors.c | 9 +++++---- main/streams/streams.c | 6 ++++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h index 66d705c2bc3bc..9a926c952c520 100644 --- a/main/streams/php_stream_errors.h +++ b/main/streams/php_stream_errors.h @@ -166,13 +166,10 @@ PHPAPI void php_stream_wrapper_log_error_param(const php_stream_wrapper *wrapper const char *param, const char *fmt, ...) ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); PHPAPI void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, - const char *path, const char *caption); + php_stream_context *context, int code, const char *path, const char *caption); PHPAPI void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper); -void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption); -void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper); - /* Convenience macros */ #define php_stream_wrapper_warn(wrapper, context, options, code, ...) \ php_stream_wrapper_error(wrapper, context, NULL, options, E_WARNING, true, code, __VA_ARGS__) diff --git a/main/streams/stream_errors.c b/main/streams/stream_errors.c index ec403acedfe5a..4c69b4a72ddf2 100644 --- a/main/streams/stream_errors.c +++ b/main/streams/stream_errors.c @@ -436,8 +436,8 @@ static zend_llist *php_stream_get_wrapper_errors_list(php_stream_wrapper *wrappe } } -void php_stream_display_wrapper_errors( - php_stream_wrapper *wrapper, const char *path, const char *caption) +void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, + php_stream_context *context, int code, const char *path, const char *caption) { char *tmp; char *msg; @@ -471,7 +471,7 @@ void php_stream_display_wrapper_errors( for (err_entry_p = zend_llist_get_first_ex(err_list, &pos), i = 0; err_entry_p; err_entry_p = zend_llist_get_next_ex(err_list, &pos), i++) { - strcat(msg, ZSTR_VAL((*err_entry_p)->message)); + l += ZSTR_LEN((*err_entry_p)->message); if (i < count - 1) { l += brlen; } @@ -499,7 +499,8 @@ void php_stream_display_wrapper_errors( } php_strip_url_passwd(tmp); - php_error_docref1(NULL, tmp, E_WARNING, "%s: %s", caption, msg); + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, code, tmp, + "%s: %s", caption, msg); efree(tmp); if (free_msg) { efree(msg); diff --git a/main/streams/streams.c b/main/streams/streams.c index fb9901d7c6aff..067c51c472619 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -2065,7 +2065,8 @@ PHPAPI php_stream *_php_stream_opendir(const char *path, int options, STREAM_ERROR_CODE_NO_OPENER, "not implemented"); } if (stream == NULL && (options & REPORT_ERRORS)) { - php_stream_display_wrapper_errors(wrapper, path, "Failed to open directory"); + php_stream_display_wrapper_errors(wrapper, context, path, STREAM_ERROR_CODE_OPEN_FAILED, + "Failed to open directory"); } php_stream_tidy_wrapper_error_log(wrapper); @@ -2232,7 +2233,8 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod } if (stream == NULL && (options & REPORT_ERRORS)) { - php_stream_display_wrapper_errors(wrapper, path, "Failed to open stream"); + php_stream_display_wrapper_errors(wrapper, context, path, STREAM_ERROR_CODE_OPEN_FAILED, + "Failed to open stream"); if (opened_path && *opened_path) { zend_string_release_ex(*opened_path, 0); *opened_path = NULL; From 2fe8d6d61150c792547fde3833dbce141433bb4d Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 17 Nov 2025 20:13:33 +0100 Subject: [PATCH 21/37] stream: fix phar errors --- ext/phar/dirstream.c | 6 +++--- ext/phar/dirstream.h | 2 +- ext/phar/stream.c | 30 +++++++++++++++--------------- ext/phar/stream.h | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c index 6ea3f7ba2fb06..e364300b0b5f3 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -253,7 +253,7 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, const char *path, char *error; phar_archive_data *phar; - if ((resource = phar_parse_url(wrapper, path, mode, options)) == NULL) { + if ((resource = phar_parse_url(wrapper, context, path, mode, options)) == NULL) { php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, "phar url \"%s\" is unknown", path); return NULL; @@ -376,7 +376,7 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo return 0; } - if ((resource = phar_parse_url(wrapper, url_from, "w", options)) == NULL) { + if ((resource = phar_parse_url(wrapper, context, url_from, "w", options)) == NULL) { return 0; } @@ -520,7 +520,7 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options return 0; } - if ((resource = phar_parse_url(wrapper, url, "w", options)) == NULL) { + if ((resource = phar_parse_url(wrapper, context, url, "w", options)) == NULL) { return 0; } diff --git a/ext/phar/dirstream.h b/ext/phar/dirstream.h index 4debfecde41a1..42d79c6fc21e9 100644 --- a/ext/phar/dirstream.h +++ b/ext/phar/dirstream.h @@ -24,7 +24,7 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options #ifdef PHAR_DIRSTREAM #include "ext/standard/url.h" -php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options); +php_url* phar_parse_url(php_stream_wrapper *wrapper, php_stream_context *context, const char *filename, const char *mode, int options); /* directory handlers */ static ssize_t phar_dir_write(php_stream *stream, const char *buf, size_t count); diff --git a/ext/phar/stream.c b/ext/phar/stream.c index 3d11d3ad76bc8..3c66ffca75704 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -57,7 +57,8 @@ const php_stream_wrapper php_stream_phar_wrapper = { /** * Open a phar file for streams API */ -php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options) /* {{{ */ +php_url* phar_parse_url(php_stream_wrapper *wrapper, php_stream_context *context, + const char *filename, const char *mode, int options) { php_url *resource; char *arch = NULL, *entry = NULL, *error; @@ -68,7 +69,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const } if (mode[0] == 'a') { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_warn(wrapper, NULL, options, STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_MODE_NOT_SUPPORTED, "phar error: open mode append not supported"); } return NULL; @@ -76,12 +77,12 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const if (phar_split_fname(filename, strlen(filename), &arch, &arch_len, &entry, &entry_len, 2, (mode[0] == 'w' ? 2 : 0)) == FAILURE) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { if (arch && !entry) { - php_stream_wrapper_log_warn(wrapper, NULL, options, STREAM_ERROR_CODE_INVALID_PATH, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_PATH, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", filename, arch); arch = NULL; } else { - php_stream_wrapper_log_warn(wrapper, NULL, options, STREAM_ERROR_CODE_INVALID_URL, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, "phar error: invalid url or non-existent phar \"%s\"", filename); } } @@ -115,7 +116,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const } if (PHAR_G(readonly) && (!pphar || !pphar->is_data)) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_warn(wrapper, NULL, options, STREAM_ERROR_CODE_READONLY, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_READONLY, "phar error: write operations disabled by the php.ini setting phar.readonly"); } php_url_free(resource); @@ -136,7 +137,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const if (error) { spprintf(&error, 0, "Cannot open cached phar '%s' as writeable, copy on write failed", ZSTR_VAL(resource->host)); if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_warn(wrapper, NULL, options, STREAM_ERROR_CODE_OPEN_FAILED, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_OPEN_FAILED, "%s", error); } efree(error); } @@ -148,7 +149,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const { if (error) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_warn(wrapper, NULL, options, STREAM_ERROR_CODE_NOT_FOUND, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_NOT_FOUND, "%s", error); } efree(error); } @@ -158,7 +159,6 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const } return resource; } -/* }}} */ /** * used for fopen('phar://...') and company @@ -174,7 +174,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha php_stream *fpf; zval *pzoption, *metadata; - if ((resource = phar_parse_url(wrapper, path, mode, options)) == NULL) { + if ((resource = phar_parse_url(wrapper, context, path, mode, options)) == NULL) { return NULL; } @@ -456,7 +456,7 @@ static ssize_t phar_stream_write(php_stream *stream, const char *buf, size_t cou php_stream_seek(data->fp, data->position + data->zero, SEEK_SET); if (count != php_stream_write(data->fp, buf, count)) { - php_stream_wrapper_log_warn(stream->wrapper, stream->flags, STREAM_ERROR_CODE_WRITE_FAILED, + php_stream_warn(stream, STREAM_ERROR_CODE_WRITE_FAILED, "phar error: Could not write %zu characters to \"%s\" in phar \"%s\"", count, ZSTR_VAL(data->internal_file->filename), data->phar->fname); return -1; @@ -485,7 +485,7 @@ static int phar_stream_flush(php_stream *stream) /* {{{ */ data->internal_file->timestamp = time(0); ret = phar_flush(data->phar, &error); if (error) { - php_stream_wrapper_log_warn(stream->wrapper, REPORT_ERRORS, STREAM_ERROR_CODE_FLUSH_FAILED, "%s", error); + php_stream_warn(stream, STREAM_ERROR_CODE_FLUSH_FAILED, "%s", error); efree(error); } return ret; @@ -575,7 +575,7 @@ static int phar_wrapper_stat(php_stream_wrapper *wrapper, const char *url, int f phar_entry_info *entry; size_t internal_file_len; - if ((resource = phar_parse_url(wrapper, url, "r", flags|PHP_STREAM_URL_STAT_QUIET)) == NULL) { + if ((resource = phar_parse_url(wrapper, context, url, "r", flags|PHP_STREAM_URL_STAT_QUIET)) == NULL) { return FAILURE; } @@ -675,7 +675,7 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int phar_entry_data *idata; phar_archive_data *pphar; - if ((resource = phar_parse_url(wrapper, url, "rb", options)) == NULL) { + if ((resource = phar_parse_url(wrapper, context, url, "rb", options)) == NULL) { php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_UNLINK_FAILED, "phar error: unlink failed"); return 0; @@ -758,7 +758,7 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from error = NULL; - if ((resource_from = phar_parse_url(wrapper, url_from, "wb", options|PHP_STREAM_URL_STAT_QUIET)) == NULL) { + if ((resource_from = phar_parse_url(wrapper, context, url_from, "wb", options|PHP_STREAM_URL_STAT_QUIET)) == NULL) { php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", url_from, url_to, url_from); @@ -777,7 +777,7 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from return 0; } - if ((resource_to = phar_parse_url(wrapper, url_to, "wb", options|PHP_STREAM_URL_STAT_QUIET)) == NULL) { + if ((resource_to = phar_parse_url(wrapper, context, url_to, "wb", options|PHP_STREAM_URL_STAT_QUIET)) == NULL) { php_url_free(resource_from); php_stream_wrapper_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", diff --git a/ext/phar/stream.h b/ext/phar/stream.h index 83b395b4cfca3..e39ffb2c10baf 100644 --- a/ext/phar/stream.h +++ b/ext/phar/stream.h @@ -20,7 +20,7 @@ BEGIN_EXTERN_C() #include "ext/standard/url.h" -php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options); +php_url* phar_parse_url(php_stream_wrapper *wrapper, php_stream_context *context, const char *filename, const char *mode, int options); ZEND_ATTRIBUTE_NONNULL void phar_entry_remove(phar_entry_data *idata, char **error); static php_stream* phar_wrapper_open_url(php_stream_wrapper *wrapper, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC); From add642732a15d79f338aad80c731642e6d7f4647 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 17 Nov 2025 21:52:41 +0100 Subject: [PATCH 22/37] stream: fix http wrapper errors --- ext/standard/http_fopen_wrapper.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index b772fb0364a6f..5852beff7a380 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -288,7 +288,7 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w size_t last_header_value_len = strlen(last_header_value); if (last_header_value_len > HTTP_HEADER_MAX_LOCATION_SIZE) { header_info->error = true; - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_RESPONSE, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_RESPONSE, "HTTP Location header size is over the limit of %d bytes", HTTP_HEADER_MAX_LOCATION_SIZE); zend_string_efree(last_header_line_str); @@ -469,7 +469,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, #endif if (d > timeoutmax) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_PARAM, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_PARAM, "timeout must be lower than " ZEND_ULONG_FMT, (zend_ulong)timeoutmax); zend_string_release(transport_string); php_uri_struct_free(resource); @@ -500,7 +500,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } if (errstr) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, "%s", ZSTR_VAL(errstr)); zend_string_release_ex(errstr, 0); errstr = NULL; @@ -1108,7 +1108,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, php_uri_struct_free(resource); /* check for invalid redirection URLs */ if ((resource = php_uri_parse_to_struct(uri_parser, new_path, strlen(new_path), PHP_URI_COMPONENT_READ_MODE_RAW, true)) == NULL) { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, "Invalid redirect URL! %s", new_path); efree(new_path); goto out; @@ -1121,7 +1121,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, s = (unsigned char*)ZSTR_VAL(val); e = s + ZSTR_LEN(val); \ while (s < e) { \ if (iscntrl(*s)) { \ - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_INVALID_URL, \ + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_INVALID_URL, \ "Invalid redirect URL! %s", new_path); \ efree(new_path); \ goto out; \ @@ -1148,7 +1148,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, --redirect_max, new_flags, response_header STREAMS_CC); efree(new_path); } else { - php_stream_wrapper_log_warn(wrapper, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, + php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_ERROR, "HTTP request failed! %s", tmp_line); } } From 1a44b798953a0151783f1fef605fb0228291eadd Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 17 Nov 2025 22:10:13 +0100 Subject: [PATCH 23/37] stream: fix user wrapper error calls --- main/streams/userspace.c | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/main/streams/userspace.c b/main/streams/userspace.c index 4683dd270ab39..4ba3bed83d3be 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -590,7 +590,7 @@ static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_ zval_ptr_dtor(&args[0]); if (UNEXPECTED(call_result == FAILURE)) { - php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, "%s::" USERSTREAM_WRITE " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); } @@ -611,7 +611,7 @@ static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_ /* don't allow strange buffer overruns due to bogus return */ if (didwrite > 0 && didwrite > count) { - php_stream_warn_nt(stream, REPORT_ERRORS, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + php_stream_warn_nt(stream, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, "%s::" USERSTREAM_WRITE " wrote " ZEND_LONG_FMT " bytes more data than requested (" ZEND_LONG_FMT " written, " ZEND_LONG_FMT " max)", ZSTR_VAL(us->wrapper->ce->name), @@ -644,7 +644,7 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count } if (UNEXPECTED(call_result == FAILURE)) { - php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, "%s::" USERSTREAM_READ " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); goto err; } @@ -661,7 +661,7 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count didread = Z_STRLEN(retval); if (didread > 0) { if (didread > count) { - php_stream_warn_nt(stream, REPORT_ERRORS, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + php_stream_warn_nt(stream, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, "%s::" USERSTREAM_READ " - read " ZEND_LONG_FMT " bytes more data than requested (" ZEND_LONG_FMT " read, " ZEND_LONG_FMT " max) - excess data will be lost", @@ -682,7 +682,7 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF", ZSTR_VAL(us->wrapper->ce->name)); stream->eof = 1; @@ -798,7 +798,7 @@ static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int when *newoffs = Z_LVAL(retval); ret = 0; } else if (UNEXPECTED(call_result == FAILURE)) { - php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, "%s::" USERSTREAM_TELL " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); ret = -1; } else { @@ -863,7 +863,7 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb) zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, "%s::" USERSTREAM_STAT " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return -1; } @@ -891,8 +891,7 @@ static int user_stream_set_check_liveliness(php_stream *stream, const php_userst zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_stream_warn(stream, REPORT_ERRORS, - STREAM_ERROR_CODE_NOT_IMPLEMENTED, + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; @@ -903,8 +902,7 @@ static int user_stream_set_check_liveliness(php_stream *stream, const php_userst 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_stream_warn(stream, REPORT_ERRORS, - STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + php_stream_warn(stream, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, "%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); @@ -947,7 +945,7 @@ static int user_stream_set_locking(php_stream *stream, const php_userstream_data /* lock support test (TODO: more check) */ return PHP_STREAM_OPTION_RETURN_OK; } - php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, "%s::" USERSTREAM_LOCK " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; } @@ -960,7 +958,7 @@ static int user_stream_set_locking(php_stream *stream, const php_userstream_data } // 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_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + //php_stream_warn(stream, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, // "%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); @@ -996,7 +994,7 @@ static int user_stream_set_truncation(php_stream *stream, const php_userstream_d zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, "%s::" USERSTREAM_TRUNCATE " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; } @@ -1006,7 +1004,7 @@ static int user_stream_set_truncation(php_stream *stream, const php_userstream_d 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_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + php_stream_warn(stream, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, "%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); @@ -1040,7 +1038,7 @@ static int user_stream_set_option(php_stream *stream, const php_userstream_data_ zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, "%s::" USERSTREAM_SET_OPTION " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; @@ -1366,7 +1364,7 @@ static ssize_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t co zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, "%s::" USERSTREAM_DIR_READ " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return -1; @@ -1453,7 +1451,7 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr) if (UNEXPECTED(call_result == FAILURE)) { if (report_errors) { - php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_NOT_IMPLEMENTED, + php_stream_warn(stream, STREAM_ERROR_CODE_NOT_IMPLEMENTED, "%s::" USERSTREAM_CAST " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); } @@ -1468,7 +1466,7 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr) php_stream_from_zval_no_verify(intstream, &retval); if (!intstream) { if (report_errors) { - php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + php_stream_warn(stream, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, "%s::" USERSTREAM_CAST " must return a stream resource", ZSTR_VAL(us->wrapper->ce->name)); } @@ -1476,7 +1474,7 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr) } if (intstream == stream) { if (report_errors) { - php_stream_warn(stream, REPORT_ERRORS, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, + php_stream_warn(stream, STREAM_ERROR_CODE_USERSPACE_INVALID_RETURN, "%s::" USERSTREAM_CAST " must not return itself", ZSTR_VAL(us->wrapper->ce->name)); } From 5fbd71f2cdc31eb4d5981b4ee659f1b832b82358 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 17 Nov 2025 22:10:27 +0100 Subject: [PATCH 24/37] stream: fix php_stream_display_wrapper_errors usage --- main/streams/streams.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/streams/streams.c b/main/streams/streams.c index 067c51c472619..68a03e1499330 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -2065,7 +2065,7 @@ PHPAPI php_stream *_php_stream_opendir(const char *path, int options, STREAM_ERROR_CODE_NO_OPENER, "not implemented"); } if (stream == NULL && (options & REPORT_ERRORS)) { - php_stream_display_wrapper_errors(wrapper, context, path, STREAM_ERROR_CODE_OPEN_FAILED, + php_stream_display_wrapper_errors(wrapper, context, STREAM_ERROR_CODE_OPEN_FAILED, path, "Failed to open directory"); } php_stream_tidy_wrapper_error_log(wrapper); @@ -2233,7 +2233,7 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod } if (stream == NULL && (options & REPORT_ERRORS)) { - php_stream_display_wrapper_errors(wrapper, context, path, STREAM_ERROR_CODE_OPEN_FAILED, + php_stream_display_wrapper_errors(wrapper, context, STREAM_ERROR_CODE_OPEN_FAILED, path, "Failed to open stream"); if (opened_path && *opened_path) { zend_string_release_ex(*opened_path, 0); From 8125e09bdbc1286648cffa92c97f112fe946979c Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 17 Nov 2025 22:28:41 +0100 Subject: [PATCH 25/37] stream: fix wrapper selection for displaying logged errors --- main/streams/stream_errors.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/main/streams/stream_errors.c b/main/streams/stream_errors.c index 4c69b4a72ddf2..97e5692a035e5 100644 --- a/main/streams/stream_errors.c +++ b/main/streams/stream_errors.c @@ -426,20 +426,19 @@ PHPAPI void php_stream_wrapper_log_error_param(const php_stream_wrapper *wrapper va_end(args); } -static zend_llist *php_stream_get_wrapper_errors_list(php_stream_wrapper *wrapper) +static zend_llist *php_stream_get_wrapper_errors_list(const char *wrapper_name) { if (!FG(wrapper_logged_errors)) { return NULL; } else { return (zend_llist *) zend_hash_str_find_ptr( - FG(wrapper_logged_errors), (const char *) &wrapper, sizeof(wrapper)); + FG(wrapper_logged_errors), wrapper_name, strlen(wrapper_name)); } } void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, php_stream_context *context, int code, const char *path, const char *caption) { - char *tmp; char *msg; char errstr[256]; int free_msg = 0; @@ -449,9 +448,10 @@ void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, return; } - tmp = estrdup(path); + const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown"; + char *tmp = estrdup(path); if (wrapper) { - zend_llist *err_list = php_stream_get_wrapper_errors_list(wrapper); + zend_llist *err_list = php_stream_get_wrapper_errors_list(wrapper_name); if (err_list) { size_t l = 0; int brlen; From 3c2c4ccb3e85a7fb33ce8a8fd96c4f246533497c Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 18 Nov 2025 10:58:40 +0100 Subject: [PATCH 26/37] stream: fix wrapper key in php_stream_tidy_wrapper_error_log --- main/streams/stream_errors.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main/streams/stream_errors.c b/main/streams/stream_errors.c index 97e5692a035e5..6fad781750d2a 100644 --- a/main/streams/stream_errors.c +++ b/main/streams/stream_errors.c @@ -510,7 +510,8 @@ void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper) { if (wrapper && FG(wrapper_logged_errors)) { - zend_hash_str_del(FG(wrapper_logged_errors), (const char *) &wrapper, sizeof(wrapper)); + const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown"; + zend_hash_str_del(FG(wrapper_logged_errors), wrapper_name, strlen(wrapper_name)); } } From d8e94498e23ff3f36d455cbfd5cba1d950841239 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 18 Nov 2025 11:36:40 +0100 Subject: [PATCH 27/37] stream: stream remove not reported warning --- ext/standard/php_fopen_wrapper.c | 3 +-- ext/standard/tests/file/php_fd_wrapper_03.phpt | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/ext/standard/php_fopen_wrapper.c b/ext/standard/php_fopen_wrapper.c index b13f18e08bf78..902b0f853f4e4 100644 --- a/ext/standard/php_fopen_wrapper.c +++ b/ext/standard/php_fopen_wrapper.c @@ -396,8 +396,7 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c } else { /* invalid php://thingy */ php_stream_wrapper_warn(wrapper, context, options, - STREAM_ERROR_CODE_INVALID_URL, - "Invalid php:// URL specified"); + STREAM_ERROR_CODE_INVALID_URL, "Invalid php:// URL specified"); return NULL; } diff --git a/ext/standard/tests/file/php_fd_wrapper_03.phpt b/ext/standard/tests/file/php_fd_wrapper_03.phpt index 991c497f5e193..a19d1f5acd94b 100644 --- a/ext/standard/tests/file/php_fd_wrapper_03.phpt +++ b/ext/standard/tests/file/php_fd_wrapper_03.phpt @@ -10,7 +10,6 @@ fopen("php://fd/1/", "w"); echo "\nDone.\n"; ?> --EXPECTF-- -Warning: fopen(): Invalid php:// URL specified in %s on line %d Warning: fopen(php://fd): Failed to open stream: operation failed in %s on line 2 From f35908124e8446372dd14656f81c2ab604056963 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 18 Nov 2025 12:06:34 +0100 Subject: [PATCH 28/37] stream: fix reporting errors in rename --- ext/standard/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/file.c b/ext/standard/file.c index fdeabd1872d20..6fb021b44c173 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -1228,7 +1228,7 @@ PHP_FUNCTION(rename) context = php_stream_context_from_zval(zcontext, 0); - RETURN_BOOL(wrapper->wops->rename(wrapper, old_name, new_name, 0, context)); + RETURN_BOOL(wrapper->wops->rename(wrapper, old_name, new_name, REPORT_ERRORS, context)); } /* }}} */ From 7b894003ef6610eee9cb6de3ad1fa8afb228a056 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 18 Nov 2025 12:46:38 +0100 Subject: [PATCH 29/37] stream: do not store wrapper errors if no REPORT_ERRORS set --- main/streams/stream_errors.c | 65 +++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/main/streams/stream_errors.c b/main/streams/stream_errors.c index 6fad781750d2a..cdf3775ed277d 100644 --- a/main/streams/stream_errors.c +++ b/main/streams/stream_errors.c @@ -286,10 +286,8 @@ static void php_stream_wrapper_error_internal_with_name(const char *wrapper_name { zend_string *message = vstrpprintf(0, fmt, args); - if (options & REPORT_ERRORS) { - php_stream_process_error(context, wrapper_name, NULL, docref, code, ZSTR_VAL(message), - param, severity, terminal); - } + php_stream_process_error(context, wrapper_name, NULL, docref, code, ZSTR_VAL(message), + param, severity, terminal); php_stream_store_error_common( context, NULL, message, docref, code, wrapper_name, param, severity, terminal); @@ -311,48 +309,56 @@ PHPAPI void php_stream_wrapper_error_with_name(const char *wrapper_name, php_stream_context *context, const char *docref, int options, int severity, bool terminal, int code, const char *fmt, ...) { - va_list args; - va_start(args, fmt); - php_stream_wrapper_error_internal_with_name( - wrapper_name, context, docref, options, severity, terminal, code, NULL, fmt, args); - va_end(args); + if (options & REPORT_ERRORS) { + va_list args; + va_start(args, fmt); + php_stream_wrapper_error_internal_with_name( + wrapper_name, context, docref, options, severity, terminal, code, NULL, fmt, args); + va_end(args); + } } PHPAPI void php_stream_wrapper_error(php_stream_wrapper *wrapper, php_stream_context *context, const char *docref, int options, int severity, bool terminal, int code, const char *fmt, ...) { - va_list args; - va_start(args, fmt); - php_stream_wrapper_error_internal( - wrapper, context, docref, options, severity, terminal, code, NULL, fmt, args); - va_end(args); + if (options & REPORT_ERRORS) { + va_list args; + va_start(args, fmt); + php_stream_wrapper_error_internal( + wrapper, context, docref, options, severity, terminal, code, NULL, fmt, args); + va_end(args); + } } PHPAPI void php_stream_wrapper_error_param(php_stream_wrapper *wrapper, php_stream_context *context, const char *docref, int options, int severity, bool terminal, int code, const char *param, const char *fmt, ...) { - va_list args; - va_start(args, fmt); - char *param_copy = param ? estrdup(param): NULL; - php_stream_wrapper_error_internal( - wrapper, context, docref, options, severity, terminal, code, param_copy, fmt, args); - va_end(args); + if (options & REPORT_ERRORS) { + va_list args; + va_start(args, fmt); + char *param_copy = param ? estrdup(param): NULL; + php_stream_wrapper_error_internal( + wrapper, context, docref, options, severity, terminal, code, param_copy, fmt, args); + va_end(args); + } } PHPAPI void php_stream_wrapper_error_param2(php_stream_wrapper *wrapper, php_stream_context *context, const char *docref, int options, int severity, bool terminal, int code, const char *param1, const char *param2, const char *fmt, ...) { - char *combined_param; - spprintf(&combined_param, 0, "%s,%s", param1, param2); - - va_list args; - va_start(args, fmt); - php_stream_wrapper_error_internal( - wrapper, context, docref, options, severity, terminal, code, combined_param, fmt, args); - va_end(args); + if (options & REPORT_ERRORS) { + char *combined_param; + spprintf(&combined_param, 0, "%s,%s", param1, param2); + + va_list args; + va_start(args, fmt); + php_stream_wrapper_error_internal( + wrapper, context, docref, options, severity, terminal, code, combined_param, fmt, args); + va_end(args); + } } /* Wrapper error logging - stores in FG(wrapper_logged_errors) */ @@ -394,13 +400,12 @@ static void php_stream_wrapper_log_error_internal(const php_stream_wrapper *wrap /* Report immediately using standard error functions */ php_stream_wrapper_error_internal_with_name( wrapper_name, context, NULL, options, severity, terminal, code, param, fmt, args); - zend_string_release(message); } else { /* Store for later display in FG(wrapper_logged_errors) */ php_stream_wrapper_log_store_error( message, code, wrapper_name, param, severity, terminal); - zend_string_release(message); } + zend_string_release(message); } PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, From 11574447b20c027812d0d876c712ff0ca6663964 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 18 Nov 2025 14:41:06 +0100 Subject: [PATCH 30/37] stream: add stream_get_errors function --- ext/standard/basic_functions.stub.php | 6 ++ ext/standard/basic_functions_arginfo.h | 8 ++- ext/standard/streamsfuncs.c | 98 ++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 1 deletion(-) diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 7913ca0e00194..392551601f0c6 100644 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -3535,6 +3535,12 @@ function stream_resolve_include_path(string $filename): string|false {} */ function stream_get_wrappers(): array {} +/** + * @param resource|string|null $subject + * @return array + */ +function stream_get_errors($subject = null): array {} + /** * @return array * @refcount 1 diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 0a21d7d76426c..60076ea434eca 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f6bf6cdd07080c01d3a0cb08d71409d05b1084f9 */ + * Stub hash: 010f9a285daa0b90bfe254a29c26544b99c4512d */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) @@ -1980,6 +1980,10 @@ ZEND_END_ARG_INFO() #define arginfo_stream_get_wrappers arginfo_ob_list_handlers +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stream_get_errors, 0, 0, IS_ARRAY, 0) + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, subject, "null") +ZEND_END_ARG_INFO() + #define arginfo_stream_get_transports arginfo_ob_list_handlers #define arginfo_stream_is_local arginfo_rewind @@ -2815,6 +2819,7 @@ ZEND_FUNCTION(stream_get_meta_data); ZEND_FUNCTION(stream_get_line); ZEND_FUNCTION(stream_resolve_include_path); ZEND_FUNCTION(stream_get_wrappers); +ZEND_FUNCTION(stream_get_errors); ZEND_FUNCTION(stream_get_transports); ZEND_FUNCTION(stream_is_local); ZEND_FUNCTION(stream_isatty); @@ -3422,6 +3427,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(stream_get_line, arginfo_stream_get_line) ZEND_FE(stream_resolve_include_path, arginfo_stream_resolve_include_path) ZEND_FE(stream_get_wrappers, arginfo_stream_get_wrappers) + ZEND_FE(stream_get_errors, arginfo_stream_get_errors) ZEND_FE(stream_get_transports, arginfo_stream_get_transports) ZEND_FE(stream_is_local, arginfo_stream_is_local) ZEND_FE(stream_isatty, arginfo_stream_isatty) diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index 506ce0dafed8b..6472bbc23dedc 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -594,6 +594,104 @@ PHP_FUNCTION(stream_get_wrappers) } /* }}} */ +/* Helper function to convert error list to array */ +static void php_stream_errors_list_to_array(zend_llist *list, zval *return_value) +{ + php_stream_error_entry **err_entry_p; + zend_llist_position pos; + + for (err_entry_p = zend_llist_get_first_ex(list, &pos); + err_entry_p; + err_entry_p = zend_llist_get_next_ex(list, &pos)) { + php_stream_error_entry *entry = *err_entry_p; + zval error_array; + array_init(&error_array); + + add_assoc_str(&error_array, "message", zend_string_copy(entry->message)); + add_assoc_long(&error_array, "code", entry->code); + add_assoc_long(&error_array, "severity", entry->severity); + add_assoc_bool(&error_array, "terminal", entry->terminal); + + if (entry->wrapper_name) { + add_assoc_string(&error_array, "wrapper", entry->wrapper_name); + } + if (entry->param) { + add_assoc_string(&error_array, "param", entry->param); + } + if (entry->docref) { + add_assoc_string(&error_array, "docref", entry->docref); + } + + add_next_index_zval(return_value, &error_array); + } +} + +/* Retrieves list of stored stream errors */ +PHP_FUNCTION(stream_get_errors) +{ + zval *subject = NULL; + php_stream *stream = NULL; + char *wrapper_name = NULL; + size_t wrapper_name_len = 0; + bool get_all_wrappers = false; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(subject) + ZEND_PARSE_PARAMETERS_END(); + + /* Determine what we're querying for */ + if (subject == NULL) { + /* No parameter - get all wrapper errors */ + get_all_wrappers = true; + } else if (Z_TYPE_P(subject) == IS_RESOURCE) { + /* Stream resource - get errors for this stream */ + php_stream_from_zval_no_verify(stream, subject); + if (stream == NULL) { + zend_argument_type_error(1, "must be a valid stream resource"); + RETURN_THROWS(); + } + } else if (Z_TYPE_P(subject) == IS_STRING) { + /* Wrapper name - get errors for this wrapper */ + wrapper_name = Z_STRVAL_P(subject); + wrapper_name_len = Z_STRLEN_P(subject); + } else { + zend_argument_type_error(1, "must be a stream resource, string, or null"); + RETURN_THROWS(); + } + + array_init(return_value); + + /* Handle stream errors */ + if (stream) { + if (stream->error_list) { + php_stream_errors_list_to_array(stream->error_list, return_value); + } + } else if (get_all_wrappers) { + /* Get errors from all wrappers */ + if (FG(wrapper_stored_errors)) { + zend_string *key; + zval *val; + ZEND_HASH_FOREACH_STR_KEY_VAL(FG(wrapper_stored_errors), key, val) { + if (key) { + zend_llist *list = (zend_llist *) Z_PTR_P(val); + php_stream_errors_list_to_array(list, return_value); + } + } ZEND_HASH_FOREACH_END(); + } + } else if (wrapper_name) { + /* Get errors for specific wrapper */ + if (FG(wrapper_stored_errors)) { + zend_llist *list = zend_hash_str_find_ptr( + FG(wrapper_stored_errors), wrapper_name, wrapper_name_len); + + if (list) { + php_stream_errors_list_to_array(list, return_value); + } + } + } +} + /* {{{ stream_select related functions */ static int stream_array_to_fd_set(const HashTable *stream_array, fd_set *fds, php_socket_t *max_fd) { From cced189ba982d1a23dace8becd49570553a7ad11 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 18 Nov 2025 18:31:18 +0100 Subject: [PATCH 31/37] stream: fix error storing for persistent streams --- main/streams/php_stream_errors.h | 1 + main/streams/stream_errors.c | 39 ++++++++++++++++++++------------ 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h index 9a926c952c520..d53e9a0a5fe1e 100644 --- a/main/streams/php_stream_errors.h +++ b/main/streams/php_stream_errors.h @@ -133,6 +133,7 @@ typedef struct { char *param; int severity; bool terminal; + bool persistent; } php_stream_error_entry; /* Main error reporting functions */ diff --git a/main/streams/stream_errors.c b/main/streams/stream_errors.c index cdf3775ed277d..03e989a1395ca 100644 --- a/main/streams/stream_errors.c +++ b/main/streams/stream_errors.c @@ -29,10 +29,12 @@ static void php_stream_error_entry_dtor(void *error) { php_stream_error_entry *entry = *(php_stream_error_entry **) error; zend_string_release(entry->message); - efree(entry->wrapper_name); - efree(entry->docref); + pefree(entry->wrapper_name, entry->persistent); + pefree(entry->docref, entry->persistent); + // param is not currently supported for streams so cannot be persistent + ZEND_ASSERT(!entry->persistent || entry->param == NULL); efree(entry->param); - efree(entry); + pefree(entry, entry->persistent); } static void php_stream_error_list_dtor(zval *item) @@ -215,18 +217,23 @@ static void php_stream_process_error(php_stream_context *context, const char *wr /* Helper to create error entry */ static php_stream_error_entry *php_stream_create_error_entry(zend_string *message, int code, const char *wrapper_name, const char *docref, char *param, int severity, - bool terminal) + bool terminal, bool persistent) { - php_stream_error_entry *entry = emalloc(sizeof(php_stream_error_entry)); + if (persistent) { + message = zend_string_dup(message, true); + } else { + zend_string_addref(message); + } + + php_stream_error_entry *entry = pemalloc(sizeof(php_stream_error_entry), persistent); entry->message = message; entry->code = code; - entry->wrapper_name = wrapper_name ? estrdup(wrapper_name) : NULL; - entry->docref = docref ? estrdup(docref) : NULL; + entry->wrapper_name = wrapper_name ? pestrdup(wrapper_name, persistent) : NULL; + entry->docref = docref ? pestrdup(docref, persistent) : NULL; entry->param = param; entry->severity = severity; entry->terminal = terminal; - - zend_string_addref(message); + entry->persistent = persistent; return entry; } @@ -243,17 +250,16 @@ static void php_stream_store_error_common(php_stream_context *context, php_strea efree(param); return; } - php_stream_error_entry *entry = php_stream_create_error_entry( - message, code, wrapper_name, docref, param, severity, terminal); zend_llist *list; - + bool persistent = false; if (stream) { + persistent = stream->is_persistent; /* Store in stream's error list */ if (!stream->error_list) { - stream->error_list = emalloc(sizeof(zend_llist)); + stream->error_list = pemalloc(sizeof(zend_llist), persistent); zend_llist_init(stream->error_list, sizeof(php_stream_error_entry *), - php_stream_error_entry_dtor, 0); + php_stream_error_entry_dtor, persistent); } list = stream->error_list; } else { @@ -276,6 +282,9 @@ static void php_stream_store_error_common(php_stream_context *context, php_strea } } + php_stream_error_entry *entry = php_stream_create_error_entry( + message, code, wrapper_name, docref, param, severity, terminal, persistent); + zend_llist_add_element(list, &entry); } @@ -368,7 +377,7 @@ static void php_stream_wrapper_log_store_error(zend_string *message, int code, { char *param_copy = param ? estrdup(param): NULL; php_stream_error_entry *entry = php_stream_create_error_entry( - message, code, wrapper_name, NULL, param_copy, severity, terminal); + message, code, wrapper_name, NULL, param_copy, severity, terminal, false); if (!FG(wrapper_logged_errors)) { ALLOC_HASHTABLE(FG(wrapper_logged_errors)); From b7d0897e268253aaecba1e893dfb001b4d2e4cc7 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 18 Nov 2025 18:31:59 +0100 Subject: [PATCH 32/37] stream: add missing error mode and store constants --- main/streams/stream_errors.stub.php | 42 ++++++++++++++++++++++++++++ main/streams/stream_errors_arginfo.h | 10 ++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/main/streams/stream_errors.stub.php b/main/streams/stream_errors.stub.php index 8a27e0060fc3d..fb6c5584a5f48 100644 --- a/main/streams/stream_errors.stub.php +++ b/main/streams/stream_errors.stub.php @@ -11,6 +11,48 @@ public function getParam(): ?string {} public function getWrapperName(): string {} } +/** + * @var int + * @cvalue PHP_STREAM_ERROR_MODE_ERROR + */ +const STREAM_ERROR_MODE_ERROR = UNKNOWN; +/** + * @var int + * @cvalue PHP_STREAM_ERROR_MODE_EXCEPTION + */ +const STREAM_ERROR_MODE_EXCEPTION = UNKNOWN; +/** + * @var int + * @cvalue PHP_STREAM_ERROR_MODE_SILENT + */ +const STREAM_ERROR_MODE_SILENT = UNKNOWN; + +/** + * @var int + * @cvalue PHP_STREAM_ERROR_STORE_AUTO + */ +const STREAM_ERROR_STORE_AUTO = UNKNOWN; +/** + * @var int + * @cvalue PHP_STREAM_ERROR_STORE_NONE + */ +const STREAM_ERROR_STORE_NONE = UNKNOWN; +/** + * @var int + * @cvalue PHP_STREAM_ERROR_STORE_NON_TERM + */ +const STREAM_ERROR_STORE_NON_TERMINAL = UNKNOWN; +/** + * @var int + * @cvalue PHP_STREAM_ERROR_STORE_TERMINAL + */ +const STREAM_ERROR_STORE_TERMINAL = UNKNOWN; +/** + * @var int + * @cvalue PHP_STREAM_ERROR_STORE_ALL + */ +const STREAM_ERROR_STORE_ALL = UNKNOWN; + /** * @var int * @cvalue STREAM_ERROR_CODE_NONE diff --git a/main/streams/stream_errors_arginfo.h b/main/streams/stream_errors_arginfo.h index 523cf64323a0e..79d86059dbdb6 100644 --- a/main/streams/stream_errors_arginfo.h +++ b/main/streams/stream_errors_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: faa3f0e64b8388a495ac645fd3eda49c1ed243bd */ + * Stub hash: 97a63d009e4a3e951b0c3b213e9cb784d1af5634 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_StreamException_getParam, 0, 0, IS_STRING, 1) ZEND_END_ARG_INFO() @@ -18,6 +18,14 @@ static const zend_function_entry class_StreamException_methods[] = { static void register_stream_errors_symbols(int module_number) { + REGISTER_LONG_CONSTANT("STREAM_ERROR_MODE_ERROR", PHP_STREAM_ERROR_MODE_ERROR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_MODE_EXCEPTION", PHP_STREAM_ERROR_MODE_EXCEPTION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_MODE_SILENT", PHP_STREAM_ERROR_MODE_SILENT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_STORE_AUTO", PHP_STREAM_ERROR_STORE_AUTO, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_STORE_NONE", PHP_STREAM_ERROR_STORE_NONE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_STORE_NON_TERMINAL", PHP_STREAM_ERROR_STORE_NON_TERM, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_STORE_TERMINAL", PHP_STREAM_ERROR_STORE_TERMINAL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_ERROR_STORE_ALL", PHP_STREAM_ERROR_STORE_ALL, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_NONE", STREAM_ERROR_CODE_NONE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_GENERIC", STREAM_ERROR_CODE_GENERIC, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_ERROR_CODE_READ_FAILED", STREAM_ERROR_CODE_READ_FAILED, CONST_PERSISTENT); From 30785adedd8f19df18e8cef9c66ea7927827e3ff Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 18 Nov 2025 18:34:18 +0100 Subject: [PATCH 33/37] stream: set context for opened streams This is limited to non persistent streams to be safe. If it shows problematic for BC reasons, it should be limited to only some context (e.g. if error options set). --- main/streams/streams.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/main/streams/streams.c b/main/streams/streams.c index 68a03e1499330..07cbeee02fb50 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -2185,6 +2185,9 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod stream->open_filename = __zend_orig_filename ? __zend_orig_filename : __zend_filename; stream->open_lineno = __zend_orig_lineno ? __zend_orig_lineno : __zend_lineno; #endif + if (stream->ctx == NULL && context != NULL && !persistent) { + php_stream_context_set(stream, context); + } } if (stream != NULL && (options & STREAM_MUST_SEEK)) { From 93d0152853d2154704da9f881f34133b4ab9e911 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 18 Nov 2025 18:36:20 +0100 Subject: [PATCH 34/37] stream: fix leaked stream error list --- main/streams/streams.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/main/streams/streams.c b/main/streams/streams.c index 07cbeee02fb50..3fb64cdb74127 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -377,6 +377,11 @@ fprintf(stderr, "stream_free: %s:%p[%s] preserve_handle=%d release_cast=%d remov ZVAL_UNDEF(&stream->wrapperdata); } + if (stream->error_list) { + zend_llist_destroy(stream->error_list); + pefree(stream->error_list, stream->is_persistent); + } + if (stream->readbuf) { pefree(stream->readbuf, stream->is_persistent); stream->readbuf = NULL; From 32264a71bb84b765c064ae91780b6183df70f240 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 18 Nov 2025 18:37:30 +0100 Subject: [PATCH 35/37] stream: add tests for new stream error handling --- ...stream_errors_exception_mode_terminal.phpt | 24 +++ .../stream_errors_mix_modes_storage.phpt | 194 ++++++++++++++++++ .../stream_errors_modes_with_auto_store.phpt | 49 +++++ .../stream_errors_silent_with_handler.phpt | 33 +++ .../stream_errors_silent_with_storage.phpt | 27 +++ .../stream_errors_silent_without_storage.phpt | 20 ++ .../streams/stream_errors_standard_error.phpt | 20 ++ 7 files changed, 367 insertions(+) create mode 100644 ext/standard/tests/streams/stream_errors_exception_mode_terminal.phpt create mode 100644 ext/standard/tests/streams/stream_errors_mix_modes_storage.phpt create mode 100644 ext/standard/tests/streams/stream_errors_modes_with_auto_store.phpt create mode 100644 ext/standard/tests/streams/stream_errors_silent_with_handler.phpt create mode 100644 ext/standard/tests/streams/stream_errors_silent_with_storage.phpt create mode 100644 ext/standard/tests/streams/stream_errors_silent_without_storage.phpt create mode 100644 ext/standard/tests/streams/stream_errors_standard_error.phpt diff --git a/ext/standard/tests/streams/stream_errors_exception_mode_terminal.phpt b/ext/standard/tests/streams/stream_errors_exception_mode_terminal.phpt new file mode 100644 index 0000000000000..016b989276fa0 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_exception_mode_terminal.phpt @@ -0,0 +1,24 @@ +--TEST-- +Stream errors - exception mode for terminal errors +--FILE-- + [ + 'error_mode' => STREAM_ERROR_MODE_EXCEPTION, + ] +]); + +try { + $stream = fopen('php://nonexistent', 'r', false, $context); +} catch (StreamException $e) { + echo "Caught: " . $e->getMessage() . "\n"; + echo "Code: " . $e->getCode() . "\n"; + echo "Wrapper: " . $e->getWrapperName() . "\n"; +} + +?> +--EXPECTF-- +Caught: Failed to open stream: operation failed +Code: 36 +Wrapper: PHP diff --git a/ext/standard/tests/streams/stream_errors_mix_modes_storage.phpt b/ext/standard/tests/streams/stream_errors_mix_modes_storage.phpt new file mode 100644 index 0000000000000..cb765de62fe07 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_mix_modes_storage.phpt @@ -0,0 +1,194 @@ +--TEST-- +Stream errors - silent mode with mixed error stores +--FILE-- +position += $count; + return str_repeat('x', $count + 10); + } + + public function stream_eof() { + return $this->position >= 100; + } + + public function stream_stat() { + return []; + } +} + +stream_wrapper_register('test', 'TestStream'); + +function stream_test_errors($title, $context) { + $stream = fopen('test://foo', 'r', false, $context); + try { + echo $title . "\n"; + $readin = fopen( 'php://stdin', 'r' ); + $data = fread( $stream, 10 ); + + $read = [$readin, $stream]; + $write = NULL; + $except = NULL; + stream_select($read, $write, $except, 0); + } catch (StreamException $e) { + echo 'EXCEPTION: ' . $e->getMessage() . "\n"; + } + + $errors = stream_get_errors($stream); + var_dump($errors); +} + +stream_test_errors('ALL', stream_context_create([ + 'stream' => [ + 'error_mode' => STREAM_ERROR_MODE_SILENT, + 'error_store' => STREAM_ERROR_STORE_ALL, + ] +])); +stream_test_errors('NON TERMINAL', stream_context_create([ + 'stream' => [ + 'error_mode' => STREAM_ERROR_MODE_SILENT, + 'error_store' => STREAM_ERROR_STORE_NON_TERMINAL, + ] +])); +stream_test_errors('TERMINAL', stream_context_create([ + 'stream' => [ + 'error_mode' => STREAM_ERROR_MODE_SILENT, + 'error_store' => STREAM_ERROR_STORE_TERMINAL, + ] +])); +stream_test_errors('AUTO EXCEPTION', stream_context_create([ + 'stream' => [ + 'error_mode' => STREAM_ERROR_MODE_EXCEPTION, + 'error_store' => STREAM_ERROR_STORE_AUTO, + ] +])); +stream_test_errors('AUTO ERROR', stream_context_create([ + 'stream' => [ + 'error_mode' => STREAM_ERROR_MODE_ERROR, + 'error_store' => STREAM_ERROR_STORE_AUTO, + ] +])); + +?> +--EXPECTF-- +ALL +array(3) { + [0]=> + array(5) { + ["message"]=> + string(113) "TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost" + ["code"]=> + int(161) + ["severity"]=> + int(2) + ["terminal"]=> + bool(false) + ["wrapper"]=> + string(10) "user-space" + } + [1]=> + array(5) { + ["message"]=> + string(43) "TestStream::stream_cast is not implemented!" + ["code"]=> + int(70) + ["severity"]=> + int(2) + ["terminal"]=> + bool(true) + ["wrapper"]=> + string(10) "user-space" + } + [2]=> + array(5) { + ["message"]=> + string(73) "Cannot represent a stream of type user-space as a select()able descriptor" + ["code"]=> + int(101) + ["severity"]=> + int(2) + ["terminal"]=> + bool(true) + ["wrapper"]=> + string(10) "user-space" + } +} +NON TERMINAL +array(1) { + [0]=> + array(5) { + ["message"]=> + string(113) "TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost" + ["code"]=> + int(161) + ["severity"]=> + int(2) + ["terminal"]=> + bool(false) + ["wrapper"]=> + string(10) "user-space" + } +} +TERMINAL +array(2) { + [0]=> + array(5) { + ["message"]=> + string(43) "TestStream::stream_cast is not implemented!" + ["code"]=> + int(70) + ["severity"]=> + int(2) + ["terminal"]=> + bool(true) + ["wrapper"]=> + string(10) "user-space" + } + [1]=> + array(5) { + ["message"]=> + string(73) "Cannot represent a stream of type user-space as a select()able descriptor" + ["code"]=> + int(101) + ["severity"]=> + int(2) + ["terminal"]=> + bool(true) + ["wrapper"]=> + string(10) "user-space" + } +} +AUTO EXCEPTION +EXCEPTION: Cannot represent a stream of type user-space as a select()able descriptor +array(1) { + [0]=> + array(5) { + ["message"]=> + string(113) "TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost" + ["code"]=> + int(161) + ["severity"]=> + int(2) + ["terminal"]=> + bool(false) + ["wrapper"]=> + string(10) "user-space" + } +} +AUTO ERROR + +Warning: fread(): TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost in %s on line %d + +Warning: stream_select(): TestStream::stream_cast is not implemented! in %s on line %d + +Warning: stream_select(): Cannot represent a stream of type user-space as a select()able descriptor in %s on line %d +array(0) { +} diff --git a/ext/standard/tests/streams/stream_errors_modes_with_auto_store.phpt b/ext/standard/tests/streams/stream_errors_modes_with_auto_store.phpt new file mode 100644 index 0000000000000..bda445eabe428 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_modes_with_auto_store.phpt @@ -0,0 +1,49 @@ +--TEST-- +Stream errors - error_store AUTO mode behavior +--FILE-- + [ + 'error_mode' => STREAM_ERROR_MODE_ERROR, + 'error_store' => STREAM_ERROR_STORE_AUTO, + ] +]); + +@fopen('php://nonexistent', 'r', false, $context1); +$errors1 = stream_get_errors('PHP'); +echo "ERROR mode AUTO: " . count($errors1) . "\n"; + +// AUTO with EXCEPTION mode should store NON_TERM +$context2 = stream_context_create([ + 'stream' => [ + 'error_mode' => STREAM_ERROR_MODE_EXCEPTION, + 'error_store' => STREAM_ERROR_STORE_AUTO, + ] +]); + +try { + fopen('php://nonexistent2', 'r', false, $context2); +} catch (StreamException $e) {} + +$errors2 = stream_get_errors('PHP'); +echo "EXCEPTION mode AUTO: " . count($errors2) . "\n"; + +// AUTO with SILENT mode should store ALL +$context3 = stream_context_create([ + 'stream' => [ + 'error_mode' => STREAM_ERROR_MODE_SILENT, + 'error_store' => STREAM_ERROR_STORE_AUTO, + ] +]); + +fopen('php://nonexistent3', 'r', false, $context3); +$errors3 = stream_get_errors('PHP'); +echo "SILENT mode AUTO: " . count($errors3) . "\n"; + +?> +--EXPECTF-- +ERROR mode AUTO: 0 +EXCEPTION mode AUTO: %d +SILENT mode AUTO: %d diff --git a/ext/standard/tests/streams/stream_errors_silent_with_handler.phpt b/ext/standard/tests/streams/stream_errors_silent_with_handler.phpt new file mode 100644 index 0000000000000..9ce86adb09658 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_silent_with_handler.phpt @@ -0,0 +1,33 @@ +--TEST-- +Stream errors - custom error handler +--FILE-- + [ + 'error_mode' => STREAM_ERROR_MODE_SILENT, + 'error_handler' => function($wrapper, $stream, $code, $message, $param) use (&$handler_called) { + $handler_called = true; + echo "Handler called\n"; + echo "Wrapper: $wrapper\n"; + echo "Code: $code\n"; + echo "Message: $message\n"; + echo "Param: " . ($param ?? 'null') . "\n"; + } + ] +]); + +$stream = fopen('php://nonexistent', 'r', false, $context); + +var_dump($handler_called); + +?> +--EXPECT-- +Handler called +Wrapper: PHP +Code: 36 +Message: Failed to open stream: operation failed +Param: php://nonexistent +bool(true) diff --git a/ext/standard/tests/streams/stream_errors_silent_with_storage.phpt b/ext/standard/tests/streams/stream_errors_silent_with_storage.phpt new file mode 100644 index 0000000000000..563857d308fff --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_silent_with_storage.phpt @@ -0,0 +1,27 @@ +--TEST-- +Stream errors - silent mode with error storage +--FILE-- + [ + 'error_mode' => STREAM_ERROR_MODE_SILENT, + ] +]); + +$stream = fopen('php://nonexistent', 'r', false, $context); +var_dump($stream); + +$errors = stream_get_errors(); +echo "Error count: " . count($errors) . "\n"; +if (count($errors) > 0) { + echo "First error code: " . $errors[0]['code'] . "\n"; + echo "First error wrapper: " . $errors[0]['wrapper'] . "\n"; +} + +?> +--EXPECT-- +bool(false) +Error count: 1 +First error code: 36 +First error wrapper: PHP diff --git a/ext/standard/tests/streams/stream_errors_silent_without_storage.phpt b/ext/standard/tests/streams/stream_errors_silent_without_storage.phpt new file mode 100644 index 0000000000000..6ed79378d8226 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_silent_without_storage.phpt @@ -0,0 +1,20 @@ +--TEST-- +Stream errors - error_store NONE option +--FILE-- + [ + 'error_mode' => STREAM_ERROR_MODE_SILENT, + 'error_store' => STREAM_ERROR_STORE_NONE, + ] +]); + +$stream = fopen('php://nonexistent', 'r', false, $context); + +$errors = stream_get_errors(); +echo "Error count: " . count($errors) . "\n"; + +?> +--EXPECT-- +Error count: 0 diff --git a/ext/standard/tests/streams/stream_errors_standard_error.phpt b/ext/standard/tests/streams/stream_errors_standard_error.phpt new file mode 100644 index 0000000000000..a9725f0b89b26 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_standard_error.phpt @@ -0,0 +1,20 @@ +--TEST-- +Stream errors - error mode with standard error reporting +--FILE-- + [ + 'error_mode' => STREAM_ERROR_MODE_ERROR, + ] +]); + +// This will trigger a warning +$stream = fopen('php://nonexistent', 'r', false, $context); + +var_dump($stream); + +?> +--EXPECTF-- +Warning: fopen(php://nonexistent): Failed to open stream: %s in %s on line %d +bool(false) From bdf9600e03d15e248fd0b24ee24900b5eee4f3d1 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 18 Nov 2025 19:44:53 +0100 Subject: [PATCH 36/37] stream: update reflection class name test --- .../tests/ReflectionExtension_getClassNames_basic.phpt | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt index 47813255381e4..dc72254abc076 100644 --- a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt @@ -16,5 +16,6 @@ AssertionError Directory RoundingMode StreamBucket +StreamException __PHP_Incomplete_Class php_user_filter From 34b0ae84658ea86bfc78e3288079199f427d430d Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 18 Nov 2025 23:10:55 +0100 Subject: [PATCH 37/37] stream: use wrapper ptr key for logged errors --- main/streams/stream_errors.c | 72 ++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/main/streams/stream_errors.c b/main/streams/stream_errors.c index 03e989a1395ca..5acc7cd7d2f41 100644 --- a/main/streams/stream_errors.c +++ b/main/streams/stream_errors.c @@ -25,13 +25,16 @@ /* StreamException class entry */ static zend_class_entry *php_ce_stream_exception; +#define PHP_STREAM_ERRORS_WRAPPER_NAME(_wrapper) \ + (_wrapper && _wrapper->wops && _wrapper->wops->label ? _wrapper->wops->label : "unknown") + static void php_stream_error_entry_dtor(void *error) { php_stream_error_entry *entry = *(php_stream_error_entry **) error; zend_string_release(entry->message); pefree(entry->wrapper_name, entry->persistent); pefree(entry->docref, entry->persistent); - // param is not currently supported for streams so cannot be persistent + // param is not currently supported for streams so cannot be persistent ZEND_ASSERT(!entry->persistent || entry->param == NULL); efree(entry->param); pefree(entry, entry->persistent); @@ -216,8 +219,8 @@ static void php_stream_process_error(php_stream_context *context, const char *wr /* Helper to create error entry */ static php_stream_error_entry *php_stream_create_error_entry(zend_string *message, int code, - const char *wrapper_name, const char *docref, char *param, int severity, - bool terminal, bool persistent) + const char *wrapper_name, const char *docref, char *param, int severity, bool terminal, + bool persistent) { if (persistent) { message = zend_string_dup(message, true); @@ -240,8 +243,8 @@ static php_stream_error_entry *php_stream_create_error_entry(zend_string *messag /* Common storage function*/ static void php_stream_store_error_common(php_stream_context *context, php_stream *stream, - zend_string *message, const char *docref, int code, - const char *wrapper_name, char *param, int severity, bool terminal) + zend_string *message, const char *docref, int code, const char *wrapper_name, char *param, + int severity, bool terminal) { int error_mode = php_stream_get_error_mode(context); int store_mode = php_stream_get_error_store_mode(context, error_mode); @@ -295,8 +298,8 @@ static void php_stream_wrapper_error_internal_with_name(const char *wrapper_name { zend_string *message = vstrpprintf(0, fmt, args); - php_stream_process_error(context, wrapper_name, NULL, docref, code, ZSTR_VAL(message), - param, severity, terminal); + php_stream_process_error(context, wrapper_name, NULL, docref, code, ZSTR_VAL(message), param, + severity, terminal); php_stream_store_error_common( context, NULL, message, docref, code, wrapper_name, param, severity, terminal); @@ -308,10 +311,10 @@ static void php_stream_wrapper_error_internal(php_stream_wrapper *wrapper, php_stream_context *context, const char *docref, int options, int severity, bool terminal, int code, char *param, const char *fmt, va_list args) { - const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown"; + const char *wrapper_name = PHP_STREAM_ERRORS_WRAPPER_NAME(wrapper); - php_stream_wrapper_error_internal_with_name(wrapper_name, context, docref, options, severity, - terminal, code, param, fmt, args); + php_stream_wrapper_error_internal_with_name( + wrapper_name, context, docref, options, severity, terminal, code, param, fmt, args); } PHPAPI void php_stream_wrapper_error_with_name(const char *wrapper_name, @@ -347,7 +350,7 @@ PHPAPI void php_stream_wrapper_error_param(php_stream_wrapper *wrapper, php_stre if (options & REPORT_ERRORS) { va_list args; va_start(args, fmt); - char *param_copy = param ? estrdup(param): NULL; + char *param_copy = param ? estrdup(param) : NULL; php_stream_wrapper_error_internal( wrapper, context, docref, options, severity, terminal, code, param_copy, fmt, args); va_end(args); @@ -364,20 +367,20 @@ PHPAPI void php_stream_wrapper_error_param2(php_stream_wrapper *wrapper, va_list args; va_start(args, fmt); - php_stream_wrapper_error_internal( - wrapper, context, docref, options, severity, terminal, code, combined_param, fmt, args); + php_stream_wrapper_error_internal(wrapper, context, docref, options, severity, terminal, + code, combined_param, fmt, args); va_end(args); } } /* Wrapper error logging - stores in FG(wrapper_logged_errors) */ -static void php_stream_wrapper_log_store_error(zend_string *message, int code, - const char *wrapper_name, const char *param, int severity, bool terminal) +static void php_stream_wrapper_log_store_error(const php_stream_wrapper *wrapper, + zend_string *message, int code, const char *param, int severity, bool terminal) { - char *param_copy = param ? estrdup(param): NULL; + char *param_copy = param ? estrdup(param) : NULL; php_stream_error_entry *entry = php_stream_create_error_entry( - message, code, wrapper_name, NULL, param_copy, severity, terminal, false); + message, code, NULL, NULL, param_copy, severity, terminal, false); if (!FG(wrapper_logged_errors)) { ALLOC_HASHTABLE(FG(wrapper_logged_errors)); @@ -385,14 +388,14 @@ static void php_stream_wrapper_log_store_error(zend_string *message, int code, } zend_llist *list = zend_hash_str_find_ptr( - FG(wrapper_logged_errors), wrapper_name, strlen(wrapper_name)); + FG(wrapper_logged_errors), (const char *) &wrapper, sizeof(wrapper)); if (!list) { zend_llist new_list; zend_llist_init( &new_list, sizeof(php_stream_error_entry *), php_stream_error_entry_dtor, 0); - list = zend_hash_str_update_mem(FG(wrapper_logged_errors), wrapper_name, - strlen(wrapper_name), &new_list, sizeof(new_list)); + list = zend_hash_str_update_mem(FG(wrapper_logged_errors), (const char *) &wrapper, + sizeof(wrapper), &new_list, sizeof(new_list)); } zend_llist_add_element(list, &entry); @@ -403,7 +406,7 @@ static void php_stream_wrapper_log_error_internal(const php_stream_wrapper *wrap char *param, const char *fmt, va_list args) { zend_string *message = vstrpprintf(0, fmt, args); - const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown"; + const char *wrapper_name = PHP_STREAM_ERRORS_WRAPPER_NAME(wrapper); if (options & REPORT_ERRORS) { /* Report immediately using standard error functions */ @@ -411,8 +414,7 @@ static void php_stream_wrapper_log_error_internal(const php_stream_wrapper *wrap wrapper_name, context, NULL, options, severity, terminal, code, param, fmt, args); } else { /* Store for later display in FG(wrapper_logged_errors) */ - php_stream_wrapper_log_store_error( - message, code, wrapper_name, param, severity, terminal); + php_stream_wrapper_log_store_error(wrapper, message, code, param, severity, terminal); } zend_string_release(message); } @@ -434,24 +436,24 @@ PHPAPI void php_stream_wrapper_log_error_param(const php_stream_wrapper *wrapper { va_list args; va_start(args, fmt); - char *param_copy = param ? estrdup(param): NULL; + char *param_copy = param ? estrdup(param) : NULL; php_stream_wrapper_log_error_internal( wrapper, context, options, severity, terminal, code, param_copy, fmt, args); va_end(args); } -static zend_llist *php_stream_get_wrapper_errors_list(const char *wrapper_name) +static zend_llist *php_stream_get_wrapper_errors_list(php_stream_wrapper *wrapper) { if (!FG(wrapper_logged_errors)) { return NULL; } else { return (zend_llist *) zend_hash_str_find_ptr( - FG(wrapper_logged_errors), wrapper_name, strlen(wrapper_name)); + FG(wrapper_logged_errors), (const char *) &wrapper, sizeof(wrapper)); } } -void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, - php_stream_context *context, int code, const char *path, const char *caption) +void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, php_stream_context *context, + int code, const char *path, const char *caption) { char *msg; char errstr[256]; @@ -462,10 +464,9 @@ void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, return; } - const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown"; char *tmp = estrdup(path); if (wrapper) { - zend_llist *err_list = php_stream_get_wrapper_errors_list(wrapper_name); + zend_llist *err_list = php_stream_get_wrapper_errors_list(wrapper); if (err_list) { size_t l = 0; int brlen; @@ -513,8 +514,8 @@ void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, } php_strip_url_passwd(tmp); - php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, code, tmp, - "%s: %s", caption, msg); + php_stream_wrapper_warn_param( + wrapper, context, REPORT_ERRORS, code, tmp, "%s: %s", caption, msg); efree(tmp); if (free_msg) { efree(msg); @@ -524,8 +525,7 @@ void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper) { if (wrapper && FG(wrapper_logged_errors)) { - const char *wrapper_name = wrapper ? wrapper->wops->label : "unknown"; - zend_hash_str_del(FG(wrapper_logged_errors), wrapper_name, strlen(wrapper_name)); + zend_hash_str_del(FG(wrapper_logged_errors), (const char *) &wrapper, sizeof(wrapper)); } } @@ -549,8 +549,8 @@ PHPAPI void php_stream_error(php_stream *stream, const char *docref, int severit severity, terminal); /* Store error */ - php_stream_store_error_common(context, stream, message, docref, code, wrapper_name, NULL, - severity, terminal); + php_stream_store_error_common( + context, stream, message, docref, code, wrapper_name, NULL, severity, terminal); zend_string_release(message); }