Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
972d7c7
stream: add initial stream errors API
bukka Sep 30, 2025
e705e04
stream: add error codes and handler check
bukka Nov 16, 2025
5e32752
stream: convert phar errors
bukka Nov 16, 2025
a54de16
stream: convert ftp wrapper errors
bukka Nov 16, 2025
f8ecb9b
stream: add few more errors to replace generic errors in phar and ftp
bukka Nov 16, 2025
ccd02f1
stream: convert http wrapper errors
bukka Nov 16, 2025
53fa92e
stream: add context param to php_stream_wrapper_log_* variants
bukka Nov 16, 2025
0f261b2
stream: convert php fopen wrapper
bukka Nov 16, 2025
d57eb7d
stream: convert memory wrappers errors
bukka Nov 16, 2025
c051ba7
stream: convert plain wrappers errors
bukka Nov 16, 2025
a56024b
stream: convert cast errors
bukka Nov 16, 2025
5931642
stream: convert filter errors
bukka Nov 16, 2025
69cab18
stream: convert core streams errors
bukka Nov 16, 2025
eeb5c86
stream: add docref param to error functions
bukka Nov 17, 2025
30a3ce7
stream: convert transport errors
bukka Nov 17, 2025
1ad8e4e
stream: store wrapper erros by name instead of ptr
bukka Nov 17, 2025
9fde31d
stream: convert user stream wrapper errors
bukka Nov 17, 2025
310f0cb
stream: convert xp_socket errors
bukka Nov 17, 2025
fc806a3
stream: fix compilation issues in core stream errors
bukka Nov 17, 2025
49b6341
stream: extend and fix php_stream_display_wrapper_errors
bukka Nov 17, 2025
2fe8d6d
stream: fix phar errors
bukka Nov 17, 2025
add6427
stream: fix http wrapper errors
bukka Nov 17, 2025
1a44b79
stream: fix user wrapper error calls
bukka Nov 17, 2025
5fbd71f
stream: fix php_stream_display_wrapper_errors usage
bukka Nov 17, 2025
8125e09
stream: fix wrapper selection for displaying logged errors
bukka Nov 17, 2025
3c2c4cc
stream: fix wrapper key in php_stream_tidy_wrapper_error_log
bukka Nov 18, 2025
d8e9449
stream: stream remove not reported warning
bukka Nov 18, 2025
f359081
stream: fix reporting errors in rename
bukka Nov 18, 2025
7b89400
stream: do not store wrapper errors if no REPORT_ERRORS set
bukka Nov 18, 2025
1157444
stream: add stream_get_errors function
bukka Nov 18, 2025
cced189
stream: fix error storing for persistent streams
bukka Nov 18, 2025
b7d0897
stream: add missing error mode and store constants
bukka Nov 18, 2025
30785ad
stream: set context for opened streams
bukka Nov 18, 2025
93d0152
stream: fix leaked stream error list
bukka Nov 18, 2025
32264a7
stream: add tests for new stream error handling
bukka Nov 18, 2025
bdf9600
stream: update reflection class name test
bukka Nov 18, 2025
34b0ae8
stream: use wrapper ptr key for logged errors
bukka Nov 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
98 changes: 68 additions & 30 deletions ext/phar/dirstream.c
Original file line number Diff line number Diff line change
Expand Up @@ -253,37 +253,43 @@ 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) {
php_stream_wrapper_log_error(wrapper, options, "phar url \"%s\" is unknown", path);
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;
}

/* 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, 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_error(wrapper, options, "phar error: invalid url \"%s\", must have at least phar://%s/", path, path);
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_error(wrapper, options, "phar error: not a phar url \"%s\"", path);
php_stream_wrapper_log_warn(wrapper, context, options, STREAM_ERROR_CODE_PROTOCOL_UNSUPPORTED,
"phar error: not a phar url \"%s\"", path);
return NULL;
}

phar_request_initialize();

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, context, 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, context, options, STREAM_ERROR_CODE_NOT_FOUND,
"phar file \"%s\" is unknown", ZSTR_VAL(resource->host));
}
php_url_free(resource);
return NULL;
Expand Down Expand Up @@ -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, context, options, STREAM_ERROR_CODE_NOT_FOUND,
"phar error: cannot create directory \"%s\", no phar archive specified", url_from);
return 0;
}

Expand All @@ -364,29 +371,34 @@ 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, context, options, STREAM_ERROR_CODE_READONLY,
"phar error: cannot create directory \"%s\", write operations disabled", url_from);
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;
}

/* 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, 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_error(wrapper, options, "phar error: not a phar stream url \"%s\"", url_from);
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_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, 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);
php_url_free(resource);
return 0;
Expand All @@ -398,27 +410,35 @@ 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, 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);
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, 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);
php_url_free(resource);
return 0;
}

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, 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);
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, 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);
php_url_free(resource);
return 0;
Expand Down Expand Up @@ -447,15 +467,19 @@ 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, 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);
return 0;
}

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, 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);
efree(error);
return 0;
Expand All @@ -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, 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;
}

Expand All @@ -490,29 +515,34 @@ 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, context, options, STREAM_ERROR_CODE_READONLY,
"phar error: cannot rmdir directory \"%s\", write operations disabled", url);
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;
}

/* 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, 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_error(wrapper, options, "phar error: not a phar stream url \"%s\"", url);
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_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, 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);
php_url_free(resource);
return 0;
Expand All @@ -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, 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_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, 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));
}
php_url_free(resource);
return 0;
Expand All @@ -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, context, options, STREAM_ERROR_CODE_RMDIR_FAILED,
"phar error: Directory not empty");
if (entry->is_temp_dir) {
zend_string_efree(entry->filename);
efree(entry);
Expand All @@ -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, context, options, STREAM_ERROR_CODE_RMDIR_FAILED,
"phar error: Directory not empty");
if (entry->is_temp_dir) {
zend_string_efree(entry->filename);
efree(entry);
Expand All @@ -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, 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);
efree(error);
return 0;
Expand Down
2 changes: 1 addition & 1 deletion ext/phar/dirstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading
Loading