diff --git a/Zend/Optimizer/zend_func_infos.h b/Zend/Optimizer/zend_func_infos.h index b7b118c710c53..b76785283dc5c 100644 --- a/Zend/Optimizer/zend_func_infos.h +++ b/Zend/Optimizer/zend_func_infos.h @@ -596,6 +596,7 @@ static const func_info_t func_infos[] = { F1("stream_get_line", MAY_BE_STRING|MAY_BE_FALSE), F1("stream_resolve_include_path", MAY_BE_STRING|MAY_BE_FALSE), F1("stream_get_wrappers", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING), + F1("stream_last_errors", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_OBJECT), F1("stream_get_transports", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING), #if defined(HAVE_GETTIMEOFDAY) F1("uniqid", MAY_BE_STRING), diff --git a/build/gen_stub.php b/build/gen_stub.php index 9aac6b23e78b2..0e3caa90ba0d4 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -3378,6 +3378,7 @@ class ClassInfo { public array $funcInfos; /** @var EnumCaseInfo[] */ private readonly array $enumCaseInfos; + private readonly bool $generateCNameTable; public readonly ?string $cond; public ?int $phpVersionIdMinimumCompatibility; public readonly bool $isUndocumentable; @@ -3408,6 +3409,7 @@ public function __construct( array $propertyInfos, array $funcInfos, array $enumCaseInfos, + bool $generateCNameTable, ?string $cond, ?int $minimumPhpVersionIdCompatibility, bool $isUndocumentable @@ -3428,6 +3430,7 @@ public function __construct( $this->propertyInfos = $propertyInfos; $this->funcInfos = $funcInfos; $this->enumCaseInfos = $enumCaseInfos; + $this->generateCNameTable = $generateCNameTable; $this->cond = $cond; $this->phpVersionIdMinimumCompatibility = $minimumPhpVersionIdCompatibility; $this->isUndocumentable = $isUndocumentable; @@ -3622,30 +3625,49 @@ public function getCDeclarations(): string if ($this->type !== "enum") { return ''; } - + $code = ''; - + if ($this->cond) { $code .= "#if {$this->cond}\n"; } - + $cEnumName = 'zend_enum_' . str_replace('\\', '_', $this->name->toString()); - + $code .= "typedef enum {$cEnumName} {\n"; - + $i = 1; foreach ($this->enumCaseInfos as $case) { $cName = 'ZEND_ENUM_' . str_replace('\\', '_', $this->name->toString()) . '_' . $case->name; $code .= "\t{$cName} = {$i},\n"; $i++; } - + $code .= "} {$cEnumName};\n"; + + $caseCount = count($this->enumCaseInfos); + + if ($this->generateCNameTable && $caseCount > 0) { + $cPrefix = 'ZEND_ENUM_' . str_replace('\\', '_', $this->name->toString()); + $useGuard = $cPrefix . '_USE_NAME_TABLE'; + $code .= "\n#define {$cPrefix}_CASE_COUNT {$caseCount}\n"; + $code .= "\n#ifdef {$useGuard}\n"; + $code .= "static const char *{$cEnumName}_case_names[{$cPrefix}_CASE_COUNT + 1] = {\n"; + + foreach ($this->enumCaseInfos as $case) { + $cName = $cPrefix . '_' . $case->name; + $code .= "\t[{$cName}] = \"{$case->name}\",\n"; + } + + $code .= "};\n"; + $code .= "#endif\n"; + } + if ($this->cond) { $code .= "#endif\n"; } - + return $code; } @@ -5183,6 +5205,7 @@ function parseClass( $isStrictProperties = array_key_exists('strict-properties', $tagMap); $isNotSerializable = array_key_exists('not-serializable', $tagMap); $isUndocumentable = $isUndocumentable || array_key_exists('undocumentable', $tagMap); + $generateCNameTable = array_key_exists('c-name-table', $tagMap); foreach ($tags as $tag) { if ($tag->name === 'alias') { $alias = $tag->getValue(); @@ -5244,6 +5267,7 @@ function parseClass( $properties, $methods, $enumCases, + $generateCNameTable, $cond, $minimumPhpVersionIdCompatibility, $isUndocumentable diff --git a/configure.ac b/configure.ac index 214c0ab91b2a5..024c86772cd16 100644 --- a/configure.ac +++ b/configure.ac @@ -1689,6 +1689,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/phar/dirstream.c b/ext/phar/dirstream.c index f37599e7db117..5cafde364465b 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -253,26 +253,31 @@ 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, InvalidUrl, + "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, InvalidPath, + "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, InvalidUrl, + "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, ProtocolUnsupported, + "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, context, options, NotFound, "%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, NotFound, + "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, context, options, NotFound, + "phar error: cannot create directory \"%s\", no phar archive specified", url_from); return 0; } @@ -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, 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, InvalidUrl, + "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, ProtocolUnsupported, + "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, MkdirFailed, + "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, context, options, AlreadyExists, + "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, MkdirFailed, + "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, context, options, AlreadyExists, + "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, MkdirFailed, + "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, context, options, MkdirFailed, + "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, context, options, MkdirFailed, + "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, context, options, NotFound, + "phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url); return 0; } @@ -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, 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, InvalidUrl, + "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, ProtocolUnsupported, + "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, RmdirFailed, + "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, context, options, RmdirFailed, + "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, NotFound, + "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, context, options, RmdirFailed, + "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, context, options, RmdirFailed, + "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, context, options, RmdirFailed, + "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/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 786b195517861..15d9f1f5b1c5f 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,17 +69,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, context, options, ModeNotSupported, + "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, context, options, InvalidPath, + "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, context, options, InvalidUrl, + "phar error: invalid url or non-existent phar \"%s\"", filename); } } return NULL; @@ -111,7 +116,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, context, options, Readonly, + "phar error: write operations disabled by the php.ini setting phar.readonly"); } php_url_free(resource); return NULL; @@ -120,7 +126,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, NULL, options, OpenFailed, "%s", error); } efree(error); } @@ -131,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_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, OpenFailed, "%s", error); } efree(error); } @@ -143,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_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, "%s", error); } efree(error); } @@ -153,7 +159,6 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const } return resource; } -/* }}} */ /** * used for fopen('phar://...') and company @@ -169,20 +174,22 @@ 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; } /* 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, context, options, InvalidUrl, + "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, context, options, ProtocolUnsupported, + "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, time(NULL)))) { if (error) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, CreateFailed, "%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, context, options, CreateFailed, + "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, context, options, InvalidFormat, + "file %s is not a valid phar archive", ZSTR_VAL(resource->host)); efree(internal_file); php_url_free(resource); return NULL; @@ -256,7 +265,8 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha if (stream == NULL) { stream = phar_open_archive_fp(phar); if (UNEXPECTED(!stream)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: could not reopen phar \"%s\"", ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, OpenFailed, + "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, context, options, NotFound, "%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, context, options, NotFound, + "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, context, options, ArchivingFailed, "%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_warn(stream, WriteFailed, + "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_warn(stream, FlushFailed, "%s", error); efree(error); } return ret; @@ -564,7 +577,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; } @@ -664,21 +677,24 @@ 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) { - php_stream_wrapper_log_error(wrapper, options, "phar error: unlink failed"); + if ((resource = phar_parse_url(wrapper, context, url, "rb", options)) == NULL) { + php_stream_wrapper_log_warn(wrapper, context, options, UnlinkFailed, + "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, context, options, InvalidUrl, + "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, ProtocolUnsupported, + "phar error: not a phar stream url \"%s\"", url); return 0; } @@ -687,7 +703,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, context, options, Readonly, + "phar error: write operations disabled by the php.ini setting phar.readonly"); return 0; } @@ -697,10 +714,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, context, options, UnlinkFailed, + "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, context, options, NotFound, + "unlink of \"%s\" failed, file does not exist", url); } efree(internal_file); php_url_free(resource); @@ -711,7 +730,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, context, options, UnlinkFailed, + "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); @@ -721,7 +742,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, context, options, UnlinkFailed, "%s", error); efree(error); } return 1; @@ -739,8 +760,10 @@ 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); + 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, InvalidUrl, + "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)) { @@ -751,13 +774,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, 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) { + if ((resource_to = phar_parse_url(wrapper, context, 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, InvalidUrl, + "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)) { @@ -769,14 +795,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, 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, RenameFailed, + "phar error: cannot rename \"%s\" to \"%s\", not within the same phar archive", + url_from, url_to); return 0; } @@ -784,35 +813,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, InvalidUrl, + "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, InvalidUrl, + "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, ProtocolUnsupported, + "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, ProtocolUnsupported, + "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, RenameFailed, + "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); efree(error); return 0; } @@ -820,7 +858,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, RenameFailed, + "phar error: cannot rename \"%s\" to \"%s\": could not make cached phar writeable", + url_from, url_to); return 0; } @@ -831,7 +871,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, NotFound, + "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 */ @@ -851,7 +893,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, RenameFailed, + "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); efree(error); zend_hash_del(&phar->manifest, entry->filename); return 0; @@ -865,7 +908,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, NotFound, + "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source does not exist", + url_from, url_to); return 0; } @@ -944,7 +989,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, RenameFailed, + "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); efree(error); return 0; } 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); diff --git a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt index 47813255381e4..4e985e674f229 100644 --- a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt @@ -16,5 +16,10 @@ AssertionError Directory RoundingMode StreamBucket +StreamError +StreamErrorCode +StreamErrorMode +StreamErrorStore +StreamException __PHP_Incomplete_Class php_user_filter diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index fbbeeb0b433f4..05821094fc048 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/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 6d0c565fc2d41..e587b9c30636e 100644 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -3376,7 +3376,10 @@ function soundex(string $string): string {} /* streamsfuncs.c */ -function stream_select(?array &$read, ?array &$write, ?array &$except, ?int $seconds, ?int $microseconds = null): int|false {} +/** + * @param resource|null $context + */ +function stream_select(?array &$read, ?array &$write, ?array &$except, ?int $seconds, ?int $microseconds = null, $context = null): int|false {} /** * @return resource @@ -3479,17 +3482,19 @@ function stream_socket_shutdown($stream, int $mode): bool {} #ifdef HAVE_SOCKETPAIR /** + * @param resource|null $context * @return array|false * @refcount 1 */ -function stream_socket_pair(int $domain, int $type, int $protocol): array|false {} +function stream_socket_pair(int $domain, int $type, int $protocol, $context = null): array|false {} #endif /** * @param resource $from * @param resource $to + * @param resource|null $context */ -function stream_copy_to_stream($from, $to, ?int $length = null, int $offset = 0): int|false {} +function stream_copy_to_stream($from, $to, ?int $length = null, int $offset = 0, $context = null): int|false {} /** * @param resource $stream @@ -3549,14 +3554,25 @@ function stream_resolve_include_path(string $filename): string|false {} */ function stream_get_wrappers(): array {} +/** + * @refcount 1 + * @return array + */ +function stream_last_errors(): array {} + +function stream_clear_errors(): void {} + /** * @return array * @refcount 1 */ function stream_get_transports(): array {} -/** @param resource|string $stream */ -function stream_is_local($stream): bool {} +/** + * @param resource|string $stream + * @param resource|null $context + */ +function stream_is_local($stream, $context = null): bool {} /** @param resource $stream */ function stream_isatty($stream): bool {} diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index d0109fa27c960..7e460fbdf699b 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 basic_functions.stub.php instead. - * Stub hash: f5583557f058e4862750d1262296d7f59cb0eed0 + * Stub hash: 426cf6f68840941b614915fe566021802bb12abe * Has decl header: yes */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) @@ -1823,6 +1823,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_stream_select, 0, 4, MAY_BE_LONG ZEND_ARG_TYPE_INFO(1, except, IS_ARRAY, 1) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 1) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, microseconds, IS_LONG, 1, "null") + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, context, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_stream_context_create, 0, 0, 0) @@ -1937,6 +1938,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_stream_socket_pair, 0, 3, MAY_BE ZEND_ARG_TYPE_INFO(0, domain, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, protocol, IS_LONG, 0) + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, context, "null") ZEND_END_ARG_INFO() #endif @@ -1945,6 +1947,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_stream_copy_to_stream, 0, 2, MAY ZEND_ARG_INFO(0, to) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, "0") + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, context, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_stream_get_contents, 0, 1, MAY_BE_STRING|MAY_BE_FALSE) @@ -1987,9 +1990,16 @@ ZEND_END_ARG_INFO() #define arginfo_stream_get_wrappers arginfo_ob_list_handlers +#define arginfo_stream_last_errors arginfo_ob_list_handlers + +#define arginfo_stream_clear_errors arginfo_flush + #define arginfo_stream_get_transports arginfo_ob_list_handlers -#define arginfo_stream_is_local arginfo_rewind +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stream_is_local, 0, 1, _IS_BOOL, 0) + ZEND_ARG_INFO(0, stream) + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, context, "null") +ZEND_END_ARG_INFO() #define arginfo_stream_isatty arginfo_rewind @@ -2829,6 +2839,8 @@ 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_last_errors); +ZEND_FUNCTION(stream_clear_errors); ZEND_FUNCTION(stream_get_transports); ZEND_FUNCTION(stream_is_local); ZEND_FUNCTION(stream_isatty); @@ -3437,6 +3449,8 @@ 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_last_errors, arginfo_stream_last_errors) + ZEND_FE(stream_clear_errors, arginfo_stream_clear_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/basic_functions_decl.h b/ext/standard/basic_functions_decl.h index 139b47f2444d4..d29b658012501 100644 --- a/ext/standard/basic_functions_decl.h +++ b/ext/standard/basic_functions_decl.h @@ -1,8 +1,8 @@ /* This is a generated file, edit basic_functions.stub.php instead. - * Stub hash: f5583557f058e4862750d1262296d7f59cb0eed0 */ + * Stub hash: 426cf6f68840941b614915fe566021802bb12abe */ -#ifndef ZEND_BASIC_FUNCTIONS_DECL_f5583557f058e4862750d1262296d7f59cb0eed0_H -#define ZEND_BASIC_FUNCTIONS_DECL_f5583557f058e4862750d1262296d7f59cb0eed0_H +#ifndef ZEND_BASIC_FUNCTIONS_DECL_426cf6f68840941b614915fe566021802bb12abe_H +#define ZEND_BASIC_FUNCTIONS_DECL_426cf6f68840941b614915fe566021802bb12abe_H typedef enum zend_enum_RoundingMode { ZEND_ENUM_RoundingMode_HalfAwayFromZero = 1, @@ -15,4 +15,4 @@ typedef enum zend_enum_RoundingMode { ZEND_ENUM_RoundingMode_PositiveInfinity = 8, } zend_enum_RoundingMode; -#endif /* ZEND_BASIC_FUNCTIONS_DECL_f5583557f058e4862750d1262296d7f59cb0eed0_H */ +#endif /* ZEND_BASIC_FUNCTIONS_DECL_426cf6f68840941b614915fe566021802bb12abe_H */ diff --git a/ext/standard/file.c b/ext/standard/file.c index 364985f786b7e..b5809f48c10ab 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -223,7 +223,9 @@ PHP_FUNCTION(flock) Z_PARAM_ZVAL(wouldblock) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); php_flock_common(stream, operation, 2, wouldblock, return_value); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -259,6 +261,8 @@ PHP_FUNCTION(get_meta_tags) RETURN_FALSE; } + php_stream_error_operation_begin(); + array_init(return_value); tok_last = TOK_EOF; @@ -370,6 +374,8 @@ PHP_FUNCTION(get_meta_tags) if (value) efree(value); if (name) efree(name); php_stream_close(md.stream); + + php_stream_error_operation_end_for_stream(md.stream); } /* }}} */ @@ -404,12 +410,13 @@ PHP_FUNCTION(file_get_contents) RETURN_THROWS(); } + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, 0); - stream = php_stream_open_wrapper_ex(filename, "rb", (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context); if (!stream) { + php_stream_error_operation_end(context); RETURN_FALSE; } @@ -420,8 +427,9 @@ PHP_FUNCTION(file_get_contents) } if (offset != 0 && php_stream_seek(stream, offset, ((offset > 0) ? SEEK_SET : SEEK_END)) < 0) { - php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", offset); php_stream_close(stream); + php_stream_error_operation_end(context); + php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", offset); RETURN_FALSE; } @@ -432,6 +440,7 @@ PHP_FUNCTION(file_get_contents) } php_stream_close(stream); + php_stream_error_operation_end(context); } /* }}} */ @@ -461,6 +470,7 @@ PHP_FUNCTION(file_put_contents) php_stream_from_zval(srcstream, data); } + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT); if (flags & PHP_FILE_APPEND) { @@ -479,11 +489,13 @@ PHP_FUNCTION(file_put_contents) stream = php_stream_open_wrapper_ex(filename, mode, ((flags & PHP_FILE_USE_INCLUDE_PATH) ? USE_PATH : 0) | REPORT_ERRORS, NULL, context); if (stream == NULL) { + php_stream_error_operation_end(context); RETURN_FALSE; } if ((flags & LOCK_EX) && (!php_stream_supports_lock(stream) || php_stream_lock(stream, LOCK_EX))) { php_stream_close(stream); + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Exclusive locks are not supported for this stream"); RETURN_FALSE; } @@ -566,6 +578,7 @@ PHP_FUNCTION(file_put_contents) break; } php_stream_close(stream); + php_stream_error_operation_end(context); if (numbytes < 0) { RETURN_FALSE; @@ -611,10 +624,12 @@ PHP_FUNCTION(file) include_new_line = !(flags & PHP_FILE_IGNORE_NEW_LINES); skip_blank_lines = flags & PHP_FILE_SKIP_EMPTY_LINES; + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT); stream = php_stream_open_wrapper_ex(filename, "rb", (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context); if (!stream) { + php_stream_error_operation_end(context); RETURN_FALSE; } @@ -668,6 +683,7 @@ PHP_FUNCTION(file) } php_stream_close(stream); + php_stream_error_operation_end(context); } /* }}} */ @@ -707,7 +723,9 @@ PHP_FUNCTION(tmpfile) ZEND_PARSE_PARAMETERS_NONE(); + php_stream_error_operation_begin(); stream = php_stream_fopen_tmpfile(); + php_stream_error_operation_end_for_stream(stream); if (stream) { php_stream_to_zval(stream, return_value); @@ -735,9 +753,11 @@ PHP_FUNCTION(fopen) Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, 0); stream = php_stream_open_wrapper_ex(filename, mode, (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context); + php_stream_error_operation_end(context); if (stream == NULL) { RETURN_FALSE; @@ -761,9 +781,11 @@ PHPAPI PHP_FUNCTION(fclose) RETURN_FALSE; } + php_stream_error_operation_begin(); php_stream_free(stream, PHP_STREAM_FREE_KEEP_RSRC | (stream->is_persistent ? PHP_STREAM_FREE_CLOSE_PERSISTENT : PHP_STREAM_FREE_CLOSE)); + php_stream_error_operation_end_for_stream(stream); RETURN_TRUE; } @@ -811,6 +833,7 @@ PHP_FUNCTION(popen) RETURN_FALSE; } + php_stream_error_operation_begin(); stream = php_stream_fopen_from_pipe(fp, mode); if (stream == NULL) { @@ -819,6 +842,7 @@ PHP_FUNCTION(popen) } else { php_stream_to_zval(stream, return_value); } + php_stream_error_operation_end_for_stream(stream); efree(posix_mode); } @@ -833,9 +857,11 @@ PHP_FUNCTION(pclose) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); FG(pclose_wait) = 1; zend_list_close(stream->res); FG(pclose_wait) = 0; + php_stream_error_operation_end_for_stream(stream); RETURN_LONG(FG(pclose_ret)); } /* }}} */ @@ -849,11 +875,13 @@ PHPAPI PHP_FUNCTION(feof) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); if (php_stream_eof(stream)) { - RETURN_TRUE; + RETVAL_TRUE; } else { - RETURN_FALSE; + RETVAL_FALSE; } + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -873,9 +901,11 @@ PHPAPI PHP_FUNCTION(fgets) Z_PARAM_LONG_OR_NULL(len, len_is_null) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); if (len_is_null) { /* ask streams to give us a buffer of an appropriate size */ buf = php_stream_get_line(stream, NULL, 0, &line_len); + php_stream_error_operation_end_for_stream(stream); if (buf == NULL) { RETURN_FALSE; } @@ -889,7 +919,9 @@ PHPAPI PHP_FUNCTION(fgets) } str = zend_string_alloc(len, 0); - if (php_stream_get_line(stream, ZSTR_VAL(str), len, &line_len) == NULL) { + buf = php_stream_get_line(stream, ZSTR_VAL(str), len, &line_len); + php_stream_error_operation_end_for_stream(stream); + if (buf == NULL) { zend_string_efree(str); RETURN_FALSE; } @@ -914,7 +946,9 @@ PHPAPI PHP_FUNCTION(fgetc) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); int result = php_stream_getc(stream); + php_stream_error_operation_end_for_stream(stream); if (result == EOF) { RETVAL_FALSE; @@ -933,7 +967,6 @@ PHP_FUNCTION(fscanf) zval *file_handle; char *buf, *format; size_t len; - void *what; ZEND_PARSE_PARAMETERS_START(2, -1) Z_PARAM_RESOURCE(file_handle) @@ -941,16 +974,18 @@ PHP_FUNCTION(fscanf) Z_PARAM_VARIADIC('*', args, argc) ZEND_PARSE_PARAMETERS_END(); - what = zend_fetch_resource2(Z_RES_P(file_handle), "File-Handle", php_file_le_stream(), php_file_le_pstream()); + php_stream *stream = zend_fetch_resource2(Z_RES_P(file_handle), "File-Handle", php_file_le_stream(), php_file_le_pstream()); - /* we can't do a ZEND_VERIFY_RESOURCE(what), otherwise we end up + /* we can't do a ZEND_VERIFY_RESOURCE(stream), otherwise we end up * with a leak if we have an invalid filehandle. This needs changing * if the code behind ZEND_VERIFY_RESOURCE changed. - cc */ - if (!what) { + if (!stream) { RETURN_THROWS(); } - buf = php_stream_get_line((php_stream *) what, NULL, 0, &len); + php_stream_error_operation_begin(); + buf = php_stream_get_line(stream, NULL, 0, &len); + php_stream_error_operation_end_for_stream(stream); if (buf == NULL) { RETURN_FALSE; } @@ -996,7 +1031,9 @@ PHPAPI PHP_FUNCTION(fwrite) RETURN_LONG(0); } + php_stream_error_operation_begin(); ret = php_stream_write(stream, input, num_bytes); + php_stream_error_operation_end_for_stream(stream); if (ret < 0) { RETURN_FALSE; } @@ -1015,8 +1052,9 @@ PHPAPI PHP_FUNCTION(fflush) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); ret = php_stream_flush(stream); - + php_stream_error_operation_end_for_stream(stream); RETURN_BOOL(!ret); } /* }}} */ @@ -1024,13 +1062,17 @@ PHPAPI PHP_FUNCTION(fflush) /* {{{ Rewind the position of a file pointer */ PHPAPI PHP_FUNCTION(rewind) { + int ret; php_stream *stream; ZEND_PARSE_PARAMETERS_START(1, 1) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); - RETURN_BOOL(-1 != php_stream_rewind(stream)); + php_stream_error_operation_begin(); + ret = php_stream_rewind(stream); + php_stream_error_operation_end_for_stream(stream); + RETURN_BOOL(-1 != ret); } /* }}} */ @@ -1044,7 +1086,9 @@ PHPAPI PHP_FUNCTION(ftell) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); ret = php_stream_tell(stream); + php_stream_error_operation_end_for_stream(stream); if (ret == -1) { RETURN_FALSE; } @@ -1065,7 +1109,9 @@ PHPAPI PHP_FUNCTION(fseek) Z_PARAM_LONG(whence) ZEND_PARSE_PARAMETERS_END(); - RETURN_LONG(php_stream_seek(stream, offset, (int) whence)); + php_stream_error_operation_begin(); + RETVAL_LONG(php_stream_seek(stream, offset, (int) whence)); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -1089,7 +1135,9 @@ PHP_FUNCTION(mkdir) context = php_stream_context_from_zval(zcontext, 0); - RETURN_BOOL(php_stream_mkdir(dir, (int)mode, (recursive ? PHP_STREAM_MKDIR_RECURSIVE : 0) | REPORT_ERRORS, context)); + php_stream_error_operation_begin(); + RETVAL_BOOL(php_stream_mkdir(dir, (int)mode, (recursive ? PHP_STREAM_MKDIR_RECURSIVE : 0) | REPORT_ERRORS, context)); + php_stream_error_operation_end(context); } /* }}} */ @@ -1109,7 +1157,9 @@ PHP_FUNCTION(rmdir) context = php_stream_context_from_zval(zcontext, 0); - RETURN_BOOL(php_stream_rmdir(dir, REPORT_ERRORS, context)); + php_stream_error_operation_begin(); + RETVAL_BOOL(php_stream_rmdir(dir, REPORT_ERRORS, context)); + php_stream_error_operation_end(context); } /* }}} */ @@ -1133,14 +1183,17 @@ PHP_FUNCTION(readfile) context = php_stream_context_from_zval(zcontext, 0); + php_stream_error_operation_begin(); stream = php_stream_open_wrapper_ex(filename, "rb", (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context); if (stream) { size = php_stream_passthru(stream); php_stream_close(stream); - RETURN_LONG(size); + RETVAL_LONG(size); + } else { + RETVAL_FALSE; } + php_stream_error_operation_end(context); - RETURN_FALSE; } /* }}} */ @@ -1182,7 +1235,9 @@ PHPAPI PHP_FUNCTION(fpassthru) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); size = php_stream_passthru(stream); + php_stream_error_operation_end_for_stream(stream); RETURN_LONG(size); } /* }}} */ @@ -1203,26 +1258,31 @@ PHP_FUNCTION(rename) Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); + context = php_stream_context_from_zval(zcontext, 0); + wrapper = php_stream_locate_url_wrapper(old_name, NULL, 0); if (!wrapper || !wrapper->wops) { + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Unable to locate stream wrapper"); RETURN_FALSE; } if (!wrapper->wops->rename) { + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "%s wrapper does not support renaming", wrapper->wops->label ? wrapper->wops->label : "Source"); RETURN_FALSE; } if (wrapper != php_stream_locate_url_wrapper(new_name, NULL, 0)) { + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Cannot rename a file across wrapper types"); RETURN_FALSE; } - context = php_stream_context_from_zval(zcontext, 0); - - RETURN_BOOL(wrapper->wops->rename(wrapper, old_name, new_name, 0, context)); + RETVAL_BOOL(wrapper->wops->rename(wrapper, old_name, new_name, REPORT_ERRORS, context)); + php_stream_error_operation_end(context); } /* }}} */ @@ -1241,20 +1301,24 @@ PHP_FUNCTION(unlink) Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, 0); wrapper = php_stream_locate_url_wrapper(filename, NULL, 0); if (!wrapper || !wrapper->wops) { + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Unable to locate stream wrapper"); RETURN_FALSE; } if (!wrapper->wops->unlink) { + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "%s does not allow unlinking", wrapper->wops->label ? wrapper->wops->label : "Wrapper"); RETURN_FALSE; } - RETURN_BOOL(wrapper->wops->unlink(wrapper, filename, REPORT_ERRORS, context)); + RETVAL_BOOL(wrapper->wops->unlink(wrapper, filename, REPORT_ERRORS, context)); + php_stream_error_operation_end(context); } /* }}} */ @@ -1266,12 +1330,15 @@ PHP_FUNCTION(fsync) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); if (!php_stream_sync_supported(stream)) { + php_stream_error_operation_end_for_stream(stream); php_error_docref(NULL, E_WARNING, "Can't fsync this stream!"); RETURN_FALSE; } - RETURN_BOOL(php_stream_sync(stream, /* data_only */ 0) == 0); + RETVAL_BOOL(php_stream_sync(stream, /* data_only */ 0) == 0); + php_stream_error_operation_end_for_stream(stream); } PHP_FUNCTION(fdatasync) @@ -1282,12 +1349,15 @@ PHP_FUNCTION(fdatasync) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); if (!php_stream_sync_supported(stream)) { + php_stream_error_operation_end_for_stream(stream); php_error_docref(NULL, E_WARNING, "Can't fsync this stream!"); RETURN_FALSE; } - RETURN_BOOL(php_stream_sync(stream, /* data_only */ 1) == 0); + RETVAL_BOOL(php_stream_sync(stream, /* data_only */ 1) == 0); + php_stream_error_operation_end_for_stream(stream); } /* {{{ Truncate file to 'size' length */ @@ -1306,12 +1376,16 @@ PHP_FUNCTION(ftruncate) RETURN_THROWS(); } + php_stream_error_operation_begin(); + if (!php_stream_truncate_supported(stream)) { + php_stream_error_operation_end_for_stream(stream); php_error_docref(NULL, E_WARNING, "Can't truncate this stream!"); RETURN_FALSE; } - RETURN_BOOL(0 == php_stream_truncate_set_size(stream, size)); + RETVAL_BOOL(0 == php_stream_truncate_set_size(stream, size)); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ PHPAPI void php_fstat(php_stream *stream, zval *return_value) @@ -1395,7 +1469,9 @@ PHP_FUNCTION(fstat) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); php_fstat(stream, return_value); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -1414,13 +1490,16 @@ PHP_FUNCTION(copy) Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); + context = php_stream_context_from_zval(zcontext, 0); + if (php_stream_locate_url_wrapper(source, NULL, 0) == &php_plain_files_wrapper && php_check_open_basedir(source)) { + php_stream_error_operation_end(context); RETURN_FALSE; } - context = php_stream_context_from_zval(zcontext, 0); - - RETURN_BOOL(php_copy_file_ctx(source, target, 0, context) == SUCCESS); + RETVAL_BOOL(php_copy_file_ctx(source, target, 0, context) == SUCCESS); + php_stream_error_operation_end(context); } /* }}} */ @@ -1545,7 +1624,9 @@ PHPAPI PHP_FUNCTION(fread) RETURN_THROWS(); } + php_stream_error_operation_begin(); str = php_stream_read_to_str(stream, len); + php_stream_error_operation_end_for_stream(stream); if (!str) { RETURN_FALSE; } @@ -1664,7 +1745,9 @@ PHP_FUNCTION(fputcsv) RETURN_THROWS(); } + php_stream_error_operation_begin(); ret = php_fputcsv(stream, fields, delimiter, enclosure, escape_char, eol_str); + php_stream_error_operation_end_for_stream(stream); if (ret < 0) { RETURN_FALSE; } @@ -1797,19 +1880,23 @@ PHP_FUNCTION(fgetcsv) RETURN_THROWS(); } + php_stream_error_operation_begin(); if (len < 0) { if ((buf = php_stream_get_line(stream, NULL, 0, &buf_len)) == NULL) { + php_stream_error_operation_end_for_stream(stream); RETURN_FALSE; } } else { buf = emalloc(len + 1); if (php_stream_get_line(stream, buf, len + 1, &buf_len) == NULL) { efree(buf); + php_stream_error_operation_end_for_stream(stream); RETURN_FALSE; } } HashTable *values = php_fgetcsv(stream, delimiter, enclosure, escape_char, buf_len, buf); + php_stream_error_operation_end_for_stream(stream); if (values == NULL) { values = php_bc_fgetcsv_empty_line(); } diff --git a/ext/standard/file.h b/ext/standard/file.h index f8faebd028293..6f08dad366f88 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 */ + php_stream_error_state stream_error_state; int pclose_wait; #ifdef HAVE_GETHOSTBYNAME_R struct hostent tmp_host_info; diff --git a/ext/standard/ftp_fopen_wrapper.c b/ext/standard/ftp_fopen_wrapper.c index 92200e64f2384..73d58d083e77a 100644 --- a/ext/standard/ftp_fopen_wrapper.c +++ b/ext/standard/ftp_fopen_wrapper.c @@ -108,7 +108,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, + ProtocolError, + "FTP server error %d:%s", result, tmp_line); ret = EOF; } } @@ -186,7 +188,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, context, options, SslNotSupported, + "Server doesn't support FTPS."); goto connect_errexit; } else { /* we must reuse the old SSL session id */ @@ -205,7 +208,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, context, options, SslNotSupported, + "Unable to activate SSL mode"); php_stream_close(stream); stream = NULL; goto connect_errexit; @@ -236,7 +240,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, context, options, AuthFailed, err_msg, val); \ goto connect_errexit; \ } \ s++; \ @@ -433,7 +437,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, context, options, ModeNotSupported, + "FTP does not support simultaneous read/write connections"); return NULL; } if (strchr(mode, 'a')) { @@ -444,7 +449,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, context, options, InvalidMode, + "Unknown file open mode"); return NULL; } @@ -455,7 +461,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, context, options, ModeNotSupported, + "FTP proxy may only be used in read mode"); return NULL; } } @@ -507,7 +514,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, context, options, AlreadyExists, + "Remote file already exists and overwrite context option not specified"); errno = EEXIST; goto errexit; } @@ -531,7 +539,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, context, options, ResumptionFailed, + "Unable to resume from offset " ZEND_LONG_FMT, Z_LVAL_P(tmpzval)); goto errexit; } } @@ -576,7 +585,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, context, options, SslNotSupported, + "Unable to activate SSL mode"); php_stream_close(datastream); datastream = NULL; tmp_line[0]='\0'; @@ -598,10 +608,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, context, options, ProtocolError, + "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, context, options, NetworkSendFailed, + "Failed to set up data channel: %s", ZSTR_VAL(error_message)); zend_string_release(error_message); } return NULL; @@ -746,7 +758,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, context, options, SslNotSupported, + "Unable to activate SSL mode"); php_stream_close(datastream); datastream = NULL; goto opendir_errexit; @@ -770,7 +783,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, context, options, ProtocolError, + "FTP server reports %s", tmp_line); } return NULL; } @@ -909,14 +923,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, AuthFailed, + "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, InvalidPath, + "Invalid path provided in %s", url); } goto unlink_errexit; } @@ -927,7 +943,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, UnlinkFailed, + "Error Deleting file: %s", tmp_line); } goto unlink_errexit; } @@ -991,7 +1008,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, AuthFailed, + "Unable to connect to %s", ZSTR_VAL(resource_from->host)); } goto rename_errexit; } @@ -1002,7 +1020,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, RenameFailed, + "Error Renaming file: %s", tmp_line); } goto rename_errexit; } @@ -1013,7 +1032,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, RenameFailed, + "Error Renaming file: %s", tmp_line); } goto rename_errexit; } @@ -1046,14 +1066,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, AuthFailed, + "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, InvalidPath, + "Invalid path provided in %s", url); } goto mkdir_errexit; } @@ -1094,7 +1116,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, MkdirFailed, + "%s", tmp_line); } break; } @@ -1138,14 +1161,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, AuthFailed, + "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, InvalidPath, + "Invalid path provided in %s", url); } goto rmdir_errexit; } @@ -1155,7 +1180,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, RmdirFailed, + "%s", tmp_line); } goto rmdir_errexit; } diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index 1891347e2a66b..11b33f5020ca0 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -245,7 +245,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, context, options, InvalidResponse, "HTTP invalid response format (space in header name)!"); zend_string_efree(last_header_line_str); return NULL; @@ -263,7 +263,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, context, options, InvalidResponse, "HTTP invalid response format (no colon in header line)!"); zend_string_efree(last_header_line_str); return NULL; @@ -287,7 +287,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, context, options, InvalidResponse, "HTTP Location header size is over the limit of %d bytes", HTTP_HEADER_MAX_LOCATION_SIZE); zend_string_efree(last_header_line_str); @@ -388,7 +388,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, context, options, RedirectLimit, + "Redirection limit reached, aborting"); return NULL; } @@ -421,7 +422,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, context, options, ModeNotSupported, + "HTTP wrapper does not support writeable connections"); php_uri_struct_free(resource); return NULL; } @@ -450,7 +452,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, context, options, InvalidUrl, + "HTTP wrapper full URI path does not allow CR or LF characters"); php_uri_struct_free(resource); zend_string_release(transport_string); return NULL; @@ -465,7 +468,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, context, options, InvalidParam, + "timeout must be lower than " ZEND_ULONG_FMT, (zend_ulong)timeoutmax); zend_string_release(transport_string); php_uri_struct_free(resource); return NULL; @@ -495,7 +499,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, context, options, ProtocolError, + "%s", ZSTR_VAL(errstr)); zend_string_release_ex(errstr, 0); errstr = NULL; } @@ -546,7 +551,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, context, options, ProtocolError, + "Cannot connect to HTTPS server through proxy"); php_stream_close(stream); stream = NULL; } @@ -569,7 +575,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, context, options, SslNotSupported, + "Cannot connect to HTTPS server through proxy"); php_stream_close(stream); stream = NULL; } @@ -826,7 +833,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, InvalidHeader, + "Cannot construct User-agent header"); } efree(ua); } @@ -865,7 +873,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, InvalidHeader, + "Content-type not specified assuming application/x-www-form-urlencoded"); } smart_str_appends(&req_buf, "\r\n"); smart_str_append(&req_buf, Z_STR_P(tmpzval)); @@ -952,7 +961,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, context, options, ProtocolError, + "HTTP request failed!"); goto out; } } @@ -970,7 +980,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, context, options, InvalidResponse, "HTTP invalid header name (cannot start with CR character)!"); goto out; } @@ -1001,7 +1011,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, context, options, InvalidResponse, "HTTP invalid response format (folding header at the start)!"); goto out; } @@ -1097,7 +1107,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, context, options, InvalidUrl, + "Invalid redirect URL! %s", new_path); efree(new_path); goto out; } @@ -1109,7 +1120,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, context, options, InvalidUrl, \ + "Invalid redirect URL! %s", new_path); \ efree(new_path); \ goto out; \ } \ @@ -1135,7 +1147,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, context, options, ProtocolError, + "HTTP request failed! %s", tmp_line); } } out: diff --git a/ext/standard/php_fopen_wrapper.c b/ext/standard/php_fopen_wrapper.c index ea33ba4904346..8f5be5fb873db 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, + CreateFailed, + "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, + CreateFailed, + "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, + 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, + 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, + 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, + 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, + InvalidUrl, "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, + InvalidParam, "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, + DupFailed, "Error duping file descriptor " ZEND_LONG_FMT "; possibly it doesn't exist: " "[%d]: %s", fildes_ori, errno, strerror(errno)); return NULL; @@ -380,7 +395,8 @@ 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, + InvalidUrl, "Invalid php:// URL specified"); return NULL; } diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index 506ce0dafed8b..5305d79ffcb8f 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -50,11 +50,15 @@ PHP_FUNCTION(stream_socket_pair) zend_long domain, type, protocol; php_stream *s1, *s2; php_socket_t pair[2]; + zval *zcontext = NULL; + php_stream_context *context = NULL; - ZEND_PARSE_PARAMETERS_START(3, 3) + ZEND_PARSE_PARAMETERS_START(3, 4) Z_PARAM_LONG(domain) Z_PARAM_LONG(type) Z_PARAM_LONG(protocol) + Z_PARAM_OPTIONAL + Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); if (0 != socketpair((int)domain, (int)type, (int)protocol, pair)) { @@ -64,10 +68,14 @@ PHP_FUNCTION(stream_socket_pair) RETURN_FALSE; } + php_stream_error_operation_begin(); + context = php_stream_context_from_zval(zcontext, 0); + s1 = php_stream_sock_open_from_socket(pair[0], 0); if (s1 == NULL) { close(pair[0]); close(pair[1]); + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Failed to open stream from socketpair"); RETURN_FALSE; } @@ -75,6 +83,7 @@ PHP_FUNCTION(stream_socket_pair) if (s2 == NULL) { php_stream_free(s1, PHP_STREAM_FREE_CLOSE); close(pair[1]); + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Failed to open stream from socketpair"); RETURN_FALSE; } @@ -88,6 +97,8 @@ PHP_FUNCTION(stream_socket_pair) add_next_index_resource(return_value, s1->res); add_next_index_resource(return_value, s2->res); + + php_stream_error_operation_end(context); } /* }}} */ #endif @@ -126,6 +137,7 @@ PHP_FUNCTION(stream_socket_client) RETURN_THROWS(); } + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT); if (flags & PHP_STREAM_CLIENT_PERSISTENT) { @@ -160,6 +172,7 @@ PHP_FUNCTION(stream_socket_client) (flags & PHP_STREAM_CLIENT_ASYNC_CONNECT ? STREAM_XPORT_CONNECT_ASYNC : 0), hashkey, tv_pointer, context, &errstr, &err); + php_stream_error_operation_end(context); if (stream == NULL) { /* host might contain binary characters */ @@ -217,6 +230,7 @@ PHP_FUNCTION(stream_socket_server) Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT); if (zerrno) { @@ -230,6 +244,8 @@ PHP_FUNCTION(stream_socket_server) STREAM_XPORT_SERVER | (int)flags, NULL, NULL, context, &errstr, &err); + php_stream_error_operation_end(context); + if (stream == NULL) { php_error_docref(NULL, E_WARNING, "Unable to connect to %s (%s)", host, errstr == NULL ? "Unknown error" : ZSTR_VAL(errstr)); } @@ -295,6 +311,8 @@ PHP_FUNCTION(stream_socket_accept) tv_pointer = &tv; } + php_stream_error_operation_begin(); + if (0 == php_stream_xport_accept(stream, &clistream, zpeername ? &peername : NULL, NULL, NULL, @@ -313,6 +331,8 @@ PHP_FUNCTION(stream_socket_accept) RETVAL_FALSE; } + php_stream_error_operation_end_for_stream(stream); + if (errstr) { zend_string_release_ex(errstr, 0); } @@ -331,10 +351,11 @@ PHP_FUNCTION(stream_socket_get_name) Z_PARAM_BOOL(want_peer) ZEND_PARSE_PARAMETERS_END(); - if (0 != php_stream_xport_get_name(stream, want_peer, - &name, - NULL, NULL - ) || !name) { + php_stream_error_operation_begin(); + int ret = php_stream_xport_get_name(stream, want_peer, &name, NULL, NULL); + php_stream_error_operation_end_for_stream(stream); + + if (0 != ret || !name) { RETURN_FALSE; } @@ -365,15 +386,18 @@ PHP_FUNCTION(stream_socket_sendto) Z_PARAM_STRING(target_addr, target_addr_len) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); if (target_addr_len) { /* parse the address */ if (FAILURE == php_network_parse_network_address_with_port(target_addr, target_addr_len, (struct sockaddr*)&sa, &sl)) { + php_stream_error_operation_end_for_stream(stream); php_error_docref(NULL, E_WARNING, "Failed to parse `%s' into a valid network address", target_addr); RETURN_FALSE; } } - RETURN_LONG(php_stream_xport_sendto(stream, data, datalen, (int)flags, target_addr_len ? &sa : NULL, sl)); + RETVAL_LONG(php_stream_xport_sendto(stream, data, datalen, (int)flags, target_addr_len ? &sa : NULL, sl)); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -407,9 +431,10 @@ PHP_FUNCTION(stream_socket_recvfrom) read_buf = zend_string_alloc(to_read, 0); + php_stream_error_operation_begin(); recvd = php_stream_xport_recvfrom(stream, ZSTR_VAL(read_buf), to_read, (int)flags, NULL, NULL, - zremote ? &remote_addr : NULL - ); + zremote ? &remote_addr : NULL); + php_stream_error_operation_end_for_stream(stream); if (recvd >= 0) { if (zremote && remote_addr) { @@ -447,6 +472,8 @@ PHP_FUNCTION(stream_get_contents) RETURN_THROWS(); } + php_stream_error_operation_begin(); + if (desiredpos >= 0) { int seek_res = 0; zend_off_t position; @@ -461,6 +488,7 @@ PHP_FUNCTION(stream_get_contents) } if (seek_res != 0) { + php_stream_error_operation_end_for_stream(stream); php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", desiredpos); RETURN_FALSE; @@ -468,10 +496,11 @@ PHP_FUNCTION(stream_get_contents) } if ((contents = php_stream_copy_to_mem(stream, maxlen, 0))) { - RETURN_STR(contents); + RETVAL_STR(contents); } else { - RETURN_EMPTY_STRING(); + RETVAL_EMPTY_STRING(); } + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -482,28 +511,37 @@ PHP_FUNCTION(stream_copy_to_stream) zend_long maxlen, pos = 0; bool maxlen_is_null = 1; size_t len; + zval *zcontext = NULL; + php_stream_context *context = NULL; - ZEND_PARSE_PARAMETERS_START(2, 4) + ZEND_PARSE_PARAMETERS_START(2, 5) PHP_Z_PARAM_STREAM(src) PHP_Z_PARAM_STREAM(dest) Z_PARAM_OPTIONAL Z_PARAM_LONG_OR_NULL(maxlen, maxlen_is_null) Z_PARAM_LONG(pos) + Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); if (maxlen_is_null) { maxlen = PHP_STREAM_COPY_ALL; } + php_stream_error_operation_begin(); + context = php_stream_context_from_zval(zcontext, 0); + if (pos > 0 && php_stream_seek(src, pos, SEEK_SET) < 0) { + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", pos); RETURN_FALSE; } if (php_stream_copy_to_stream_ex(src, dest, maxlen, &len) != SUCCESS) { - RETURN_FALSE; + RETVAL_FALSE; + } else { + RETVAL_LONG(len); } - RETURN_LONG(len); + php_stream_error_operation_end(context); } /* }}} */ @@ -518,11 +556,13 @@ PHP_FUNCTION(stream_get_meta_data) array_init(return_value); + php_stream_error_operation_begin(); if (!php_stream_populate_meta_data(stream, return_value)) { add_assoc_bool(return_value, "timed_out", 0); add_assoc_bool(return_value, "blocked", 1); add_assoc_bool(return_value, "eof", php_stream_eof(stream)); } + php_stream_error_operation_end_for_stream(stream); if (!Z_ISUNDEF(stream->wrapperdata)) { Z_ADDREF_P(&stream->wrapperdata); @@ -594,6 +634,18 @@ PHP_FUNCTION(stream_get_wrappers) } /* }}} */ +PHP_FUNCTION(stream_last_errors) +{ + ZEND_PARSE_PARAMETERS_NONE(); + php_stream_error_get_last(return_value); +} + +PHP_FUNCTION(stream_clear_errors) +{ + ZEND_PARSE_PARAMETERS_NONE(); + php_stream_error_clear_stored(); +} + /* {{{ stream_select related functions */ static int stream_array_to_fd_set(const HashTable *stream_array, fd_set *fds, php_socket_t *max_fd) { @@ -726,7 +778,7 @@ static int stream_array_emulate_read_fd_set(zval *stream_array) /* {{{ Runs the select() system call on the sets of streams with a timeout specified by tv_sec and tv_usec */ PHP_FUNCTION(stream_select) { - zval *r_array, *w_array, *e_array; + zval *r_array, *w_array, *e_array, *zcontext = NULL; struct timeval tv, *tv_p = NULL; fd_set rfds, wfds, efds; php_socket_t max_fd = 0; @@ -735,20 +787,25 @@ PHP_FUNCTION(stream_select) bool secnull; bool usecnull = 1; int set_count, max_set_count = 0; + php_stream_context *context = NULL; - ZEND_PARSE_PARAMETERS_START(4, 5) + ZEND_PARSE_PARAMETERS_START(4, 6) Z_PARAM_ARRAY_EX2(r_array, 1, 1, 0) Z_PARAM_ARRAY_EX2(w_array, 1, 1, 0) Z_PARAM_ARRAY_EX2(e_array, 1, 1, 0) Z_PARAM_LONG_OR_NULL(sec, secnull) Z_PARAM_OPTIONAL Z_PARAM_LONG_OR_NULL(usec, usecnull) + Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); + php_stream_error_operation_begin(); + context = php_stream_context_from_zval(zcontext, 0); + if (r_array != NULL) { set_count = stream_array_to_fd_set(Z_ARR_P(r_array), &rfds, &max_fd); if (set_count > max_set_count) @@ -771,6 +828,7 @@ PHP_FUNCTION(stream_select) } if (!sets) { + php_stream_error_operation_end(context); zend_value_error("No stream arrays were passed"); RETURN_THROWS(); } @@ -781,6 +839,7 @@ PHP_FUNCTION(stream_select) if (secnull && !usecnull) { if (usec != 0) { + php_stream_error_operation_end(context); zend_argument_value_error(5, "must be null when argument #4 ($seconds) is null"); RETURN_THROWS(); } @@ -789,9 +848,11 @@ PHP_FUNCTION(stream_select) /* If seconds is not set to null, build the timeval, else we wait indefinitely */ if (!secnull) { if (sec < 0) { + php_stream_error_operation_end(context); zend_argument_value_error(4, "must be greater than or equal to 0"); RETURN_THROWS(); } else if (usec < 0) { + php_stream_error_operation_end(context); zend_argument_value_error(5, "must be greater than or equal to 0"); RETURN_THROWS(); } @@ -808,6 +869,7 @@ PHP_FUNCTION(stream_select) if (r_array != NULL) { retval = stream_array_emulate_read_fd_set(r_array); if (retval > 0) { + php_stream_error_operation_end(context); if (w_array != NULL) { zval_ptr_dtor(w_array); ZVAL_EMPTY_ARRAY(w_array); @@ -821,6 +883,7 @@ PHP_FUNCTION(stream_select) } retval = php_select(max_fd+1, &rfds, &wfds, &efds, tv_p); + php_stream_error_operation_end(context); if (retval == -1) { php_error_docref(NULL, E_WARNING, "Unable to select [%d]: %s (max_fd=" PHP_SOCKET_FMT ")", @@ -1147,6 +1210,24 @@ PHP_FUNCTION(stream_context_get_default) } /* }}} */ +/* Check if options contain stream error handling settings */ +static bool php_stream_context_options_has_error_settings(const HashTable *options) +{ + zval *stream_options = zend_hash_str_find(options, ZEND_STRL("stream")); + if (!stream_options) { + return false; + } + + ZVAL_DEREF(stream_options); + if (Z_TYPE_P(stream_options) != IS_ARRAY) { + return false; + } + + return zend_hash_str_exists(Z_ARRVAL_P(stream_options), ZEND_STRL("error_mode")) + || zend_hash_str_exists(Z_ARRVAL_P(stream_options), ZEND_STRL("error_store")) + || zend_hash_str_exists(Z_ARRVAL_P(stream_options), ZEND_STRL("error_handler")); +} + /* {{{ Set default file/stream context, returns the context as a resource */ PHP_FUNCTION(stream_context_set_default) { @@ -1162,6 +1243,11 @@ PHP_FUNCTION(stream_context_set_default) } context = FG(default_context); + if (php_stream_context_options_has_error_settings(options)) { + zend_value_error("Stream error handling options cannot be set on the default context"); + RETURN_THROWS(); + } + if (parse_context_options(context, options) == FAILURE) { RETURN_THROWS(); } @@ -1341,11 +1427,13 @@ PHP_FUNCTION(stream_get_line) max_length = PHP_SOCK_CHUNK_SIZE; } + php_stream_error_operation_begin(); if ((buf = php_stream_get_record(stream, max_length, str, str_len))) { - RETURN_STR(buf); + RETVAL_STR(buf); } else { - RETURN_FALSE; + RETVAL_FALSE; } + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -1361,7 +1449,9 @@ PHP_FUNCTION(stream_set_blocking) Z_PARAM_BOOL(block) ZEND_PARSE_PARAMETERS_END(); - RETURN_BOOL(-1 != php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, block, NULL)); + php_stream_error_operation_begin(); + RETVAL_BOOL(-1 != php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, block, NULL)); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -1402,7 +1492,9 @@ PHP_FUNCTION(stream_set_timeout) } #endif - RETURN_BOOL(PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &t)); + php_stream_error_operation_begin(); + RETVAL_BOOL(PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &t)); + php_stream_error_operation_end_for_stream(stream); } #endif /* HAVE_SYS_TIME_H || defined(PHP_WIN32) */ /* }}} */ @@ -1422,12 +1514,14 @@ PHP_FUNCTION(stream_set_write_buffer) buff = arg2; + php_stream_error_operation_begin(); /* if buff is 0 then set to non-buffered */ if (buff == 0) { ret = php_stream_set_option(stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_NONE, NULL); } else { ret = php_stream_set_option(stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_FULL, &buff); } + php_stream_error_operation_end_for_stream(stream); RETURN_LONG(ret == 0 ? 0 : EOF); } @@ -1458,7 +1552,9 @@ PHP_FUNCTION(stream_set_chunk_size) RETURN_THROWS(); } + php_stream_error_operation_begin(); ret = php_stream_set_option(stream, PHP_STREAM_OPTION_SET_CHUNK_SIZE, (int)csize, NULL); + php_stream_error_operation_end_for_stream(stream); RETURN_LONG(ret > 0 ? (zend_long)ret : (zend_long)EOF); } @@ -1479,12 +1575,14 @@ PHP_FUNCTION(stream_set_read_buffer) buff = arg2; + php_stream_error_operation_begin(); /* if buff is 0 then set to non-buffered */ if (buff == 0) { ret = php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_NONE, NULL); } else { ret = php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_FULL, &buff); } + php_stream_error_operation_end_for_stream(stream); RETURN_LONG(ret == 0 ? 0 : EOF); } @@ -1506,11 +1604,14 @@ PHP_FUNCTION(stream_socket_enable_crypto) PHP_Z_PARAM_STREAM_OR_NULL(sessstream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); + if (enable) { if (cryptokindnull) { zval *val; if (!GET_CTX_OPT(stream, "ssl", "crypto_method", val)) { + php_stream_error_operation_end_for_stream(stream); zend_argument_value_error(3, "must be specified when enabling encryption"); RETURN_THROWS(); } @@ -1519,11 +1620,13 @@ PHP_FUNCTION(stream_socket_enable_crypto) } if (php_stream_xport_crypto_setup(stream, cryptokind, sessstream) < 0) { + php_stream_error_operation_end_for_stream(stream); RETURN_FALSE; } } ret = php_stream_xport_crypto_enable(stream, enable); + php_stream_error_operation_end_for_stream(stream); switch (ret) { case -1: RETURN_FALSE; @@ -1559,12 +1662,15 @@ PHP_FUNCTION(stream_resolve_include_path) /* {{{ */ PHP_FUNCTION(stream_is_local) { - zval *zstream; + zval *zstream, *zcontext = NULL; php_stream *stream = NULL; php_stream_wrapper *wrapper = NULL; + php_stream_context *context = NULL; - ZEND_PARSE_PARAMETERS_START(1, 1) + ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_ZVAL(zstream) + Z_PARAM_OPTIONAL + Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); if (Z_TYPE_P(zstream) == IS_RESOURCE) { @@ -1575,7 +1681,10 @@ PHP_FUNCTION(stream_is_local) RETURN_THROWS(); } + php_stream_error_operation_begin(); + context = php_stream_context_from_zval(zcontext, 0); wrapper = php_stream_locate_url_wrapper(Z_STRVAL_P(zstream), NULL, 0); + php_stream_error_operation_end(context); } RETURN_BOOL(wrapper && wrapper->is_url == 0); @@ -1591,7 +1700,9 @@ PHP_FUNCTION(stream_supports_lock) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); - RETURN_BOOL(php_stream_supports_lock(stream)); + php_stream_error_operation_begin(); + RETVAL_BOOL(php_stream_supports_lock(stream)); + php_stream_error_operation_end_for_stream(stream); } /* {{{ Check if a stream is a TTY. */ @@ -1604,6 +1715,8 @@ PHP_FUNCTION(stream_isatty) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); + /* get the fd. * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag when casting. * It is only used here so that the buffered data warning is not displayed. @@ -1613,8 +1726,10 @@ PHP_FUNCTION(stream_isatty) } else if (php_stream_can_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL) == SUCCESS) { php_stream_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void*)&fileno, 0); } else { + php_stream_error_operation_end_for_stream(stream); RETURN_FALSE; } + php_stream_error_operation_end_for_stream(stream); #ifdef PHP_WIN32 /* Check if the Windows standard handle is redirected to file */ @@ -1644,6 +1759,8 @@ PHP_FUNCTION(sapi_windows_vt100_support) Z_PARAM_BOOL_OR_NULL(enable, enable_is_null) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); + /* get the fd. * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag when casting. * It is only used here so that the buffered data warning is not displayed. @@ -1653,6 +1770,7 @@ PHP_FUNCTION(sapi_windows_vt100_support) } else if (php_stream_can_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL) == SUCCESS) { php_stream_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void*)&fileno, 0); } else { + php_stream_error_operation_end_for_stream(stream); if (!enable_is_null) { php_error_docref( NULL, @@ -1662,6 +1780,7 @@ PHP_FUNCTION(sapi_windows_vt100_support) } RETURN_FALSE; } + php_stream_error_operation_end_for_stream(stream); /* Check if the file descriptor is a console */ if (!php_win32_console_fileno_is_console(fileno)) { @@ -1701,7 +1820,9 @@ PHP_FUNCTION(stream_socket_shutdown) RETURN_THROWS(); } - RETURN_BOOL(php_stream_xport_shutdown(stream, (stream_shutdown_t)how) == 0); + php_stream_error_operation_begin(); + RETVAL_BOOL(php_stream_xport_shutdown(stream, (stream_shutdown_t)how) == 0); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ #endif 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 diff --git a/ext/standard/tests/streams/gh14506.phpt b/ext/standard/tests/streams/gh14506.phpt index f83eba4f1ff30..3d82350221d55 100644 --- a/ext/standard/tests/streams/gh14506.phpt +++ b/ext/standard/tests/streams/gh14506.phpt @@ -86,10 +86,10 @@ Warning: fclose(): cannot close the provided stream, as it must not be manually Warning: fclose(): cannot close the provided stream, as it must not be manually closed 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 - Warning: fclose(): cannot close the provided stream, as it must not be manually closed 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 + Warning: stream_select(): Cannot represent a stream of type user-space as a select()able descriptor in %s on line %d No stream arrays were passed fclose(): Argument #1 ($stream) must be an open stream resource diff --git a/ext/standard/tests/streams/stream_errors_error_code_helpers.phpt b/ext/standard/tests/streams/stream_errors_error_code_helpers.phpt new file mode 100644 index 0000000000000..ae71af2f79d51 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_error_code_helpers.phpt @@ -0,0 +1,29 @@ +--TEST-- +Stream errors - StreamErrorCode helper methods +--FILE-- + [ + 'error_mode' => StreamErrorMode::Silent, + ] +]); + +// Generate a network error +$stream = fopen('php://nonexistent', 'r', false, $context); + +$errors = stream_last_errors(); +if (!empty($errors)) { + $error = $errors[0]; + echo "Is I/O error: " . ($error->code->isIoError() ? 'yes' : 'no') . "\n"; + echo "Is filesystem error: " . ($error->code->isFileSystemError() ? 'yes' : 'no') . "\n"; + echo "Is network error: " . ($error->code->isNetworkError() ? 'yes' : 'no') . "\n"; + echo "Is wrapper error: " . ($error->code->isWrapperError() ? 'yes' : 'no') . "\n"; +} + +?> +--EXPECTF-- +Is I/O error: no +Is filesystem error: yes +Is network error: no +Is wrapper error: no diff --git a/ext/standard/tests/streams/stream_errors_error_has_code.phpt b/ext/standard/tests/streams/stream_errors_error_has_code.phpt new file mode 100644 index 0000000000000..6cd6b70411a6f --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_error_has_code.phpt @@ -0,0 +1,30 @@ +--TEST-- +Stream errors - multiple operations stored +--FILE-- + [ + 'error_mode' => StreamErrorMode::Silent, + 'error_store' => StreamErrorStore::All, + ] +]); + +// First operation +$stream1 = fopen('php://nonexistent1', 'r', false, $context); +$error1 = stream_last_errors()[0]; + +// Second operation +$stream2 = fopen('php://nonexistent2', 'r', false, $context); +$error2 = stream_last_errors()[0]; + +// Should get the most recent error (second operation) +if ($error2) { + echo "Got most recent error\n"; + echo "Param contains 'nonexistent2': " . (strpos($error2->param ?? '', 'nonexistent2') !== false ? 'yes' : 'no') . "\n"; +} + +?> +--EXPECT-- +Got most recent error +Param contains 'nonexistent2': yes 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..37f5d39bfa759 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_exception_mode_terminal.phpt @@ -0,0 +1,31 @@ +--TEST-- +Stream errors - exception mode for terminal errors +--FILE-- + [ + 'error_mode' => StreamErrorMode::Exception, + ] +]); + +try { + $stream = fopen('php://nonexistent', 'r', false, $context); +} catch (StreamException $e) { + echo "Caught: " . $e->getMessage() . "\n"; + echo "Code: " . $e->getCode() . "\n"; + + $errors = $e->getErrors(); + if (!empty($errors)) { + $error = $errors[0]; + echo "Wrapper: " . $error->wrapperName . "\n"; + echo "Error code name: " . $error->code->name . "\n"; + } +} + +?> +--EXPECTF-- +Caught: Failed to open stream: operation failed +Code: 20 +Wrapper: PHP +Error code name: OpenFailed diff --git a/ext/standard/tests/streams/stream_errors_invalid_types.phpt b/ext/standard/tests/streams/stream_errors_invalid_types.phpt new file mode 100644 index 0000000000000..1b3ab5e8931e3 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_invalid_types.phpt @@ -0,0 +1,37 @@ +--TEST-- +Stream errors - invalid enum type throws TypeError +--FILE-- + [ + 'error_mode' => 'invalid', + ] + ]); + + fopen('php://nonexistent', 'r', false, $context); +} catch (TypeError $e) { + echo "Caught TypeError for error_mode\n"; +} + +try { + $context = stream_context_create([ + 'stream' => [ + 'error_store' => 123, + ] + ]); + + fopen('php://nonexistent', 'r', false, $context); +} catch (TypeError $e) { + echo "Caught TypeError for error_store\n"; +} + +?> +--EXPECTF-- + +Warning: fopen(php://nonexistent): Failed to open stream: operation failed in %s on line %d +Caught TypeError for error_mode + +Warning: fopen(php://nonexistent): Failed to open stream: operation failed in %s on line %d +Caught TypeError for error_store 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..49c18616dad3f --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_mix_modes_storage.phpt @@ -0,0 +1,149 @@ +--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, $contextOptions) { + stream_clear_errors(); + $context = stream_context_create($contextOptions); + $stream = fopen('test://foo', 'r', false, $context); + try { + echo $title . "\n"; + $readin = fopen('php://stdin', 'r', false, $context); + $data = fread($stream, 10); + + $read = [$readin, $stream]; + $write = NULL; + $except = NULL; + stream_select($read, $write, $except, 0, 0, $context); + } catch (StreamException $e) { + echo 'EXCEPTION: ' . $e->getMessage() . "\n"; + } + + $errors = stream_last_errors(); + if ($errors) { + $first = $errors[0]; + echo "Error details:\n"; + echo "- Message: $first->message\n"; + echo "- Code: " . $first->code->name . "\n"; + echo "- Wrapper: $first->wrapperName\n"; + echo "- Terminating: " . ($first->terminating ? 'yes' : 'no') . "\n"; + echo "- Count: " . count($errors) . "\n"; + + foreach ($errors as $idx => $error) { + echo " [$idx] " . $error->code->name . ": " . $error->message . "\n"; + } + } else { + echo "No errors stored\n"; + } + echo "\n"; +} + +stream_test_errors('ALL', [ + 'stream' => [ + 'error_mode' => StreamErrorMode::Silent, + 'error_store' => StreamErrorStore::All, + ] +]); + +stream_test_errors('NON TERMINATING', [ + 'stream' => [ + 'error_mode' => StreamErrorMode::Silent, + 'error_store' => StreamErrorStore::NonTerminating, + ] +]); + +stream_test_errors('TERMINATING', [ + 'stream' => [ + 'error_mode' => StreamErrorMode::Silent, + 'error_store' => StreamErrorStore::Terminating, + ] +]); + +stream_test_errors('AUTO EXCEPTION', [ + 'stream' => [ + 'error_mode' => StreamErrorMode::Exception, + 'error_store' => StreamErrorStore::Auto, + ] +]); + +stream_test_errors('AUTO ERROR', [ + 'stream' => [ + 'error_mode' => StreamErrorMode::Error, + 'error_store' => StreamErrorStore::Auto, + ] +]); + +?> +--EXPECTF-- +ALL +Error details: +- Message: TestStream::stream_cast is not implemented! +- Code: NotImplemented +- Wrapper: user-space +- Terminating: yes +- Count: 2 + [0] NotImplemented: TestStream::stream_cast is not implemented! + [1] CastNotSupported: Cannot represent a stream of type user-space as a select()able descriptor + +NON TERMINATING +Error details: +- Message: TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost +- Code: UserspaceInvalidReturn +- Wrapper: user-space +- Terminating: no +- Count: 1 + [0] UserspaceInvalidReturn: TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost + +TERMINATING +Error details: +- Message: TestStream::stream_cast is not implemented! +- Code: NotImplemented +- Wrapper: user-space +- Terminating: yes +- Count: 2 + [0] NotImplemented: TestStream::stream_cast is not implemented! + [1] CastNotSupported: Cannot represent a stream of type user-space as a select()able descriptor + +AUTO EXCEPTION +EXCEPTION: TestStream::stream_cast is not implemented! +Error details: +- Message: TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost +- Code: UserspaceInvalidReturn +- Wrapper: user-space +- Terminating: no +- Count: 1 + [0] UserspaceInvalidReturn: TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost + +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 +No errors stored 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..2575a8a795f99 --- /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' => StreamErrorMode::Error, + 'error_store' => StreamErrorStore::Auto, + ] +]); + +@fopen('php://nonexistent', 'r', false, $context1); +$errors1 = stream_last_errors(); +echo "ERROR mode AUTO: " . (!empty($errors1) ? "has error" : "no error") . "\n"; + +// AUTO with EXCEPTION mode should store NON_TERM +$context2 = stream_context_create([ + 'stream' => [ + 'error_mode' => StreamErrorMode::Exception, + 'error_store' => StreamErrorStore::Auto, + ] +]); + +try { + fopen('php://nonexistent2', 'r', false, $context2); +} catch (StreamException $e) {} + +$errors2 = stream_last_errors(); +echo "EXCEPTION mode AUTO: " . (!empty($errors2) ? "has error" : "no error") . "\n"; + +// AUTO with SILENT mode should store ALL +$context3 = stream_context_create([ + 'stream' => [ + 'error_mode' => StreamErrorMode::Silent, + 'error_store' => StreamErrorStore::Auto, + ] +]); + +fopen('php://nonexistent3', 'r', false, $context3); +$errors3 = stream_last_errors(); +echo "SILENT mode AUTO: " . (!empty($errors3) ? "has error" : "no error") . "\n"; + +?> +--EXPECTF-- +ERROR mode AUTO: no error +EXCEPTION mode AUTO: %s +SILENT mode AUTO: has error diff --git a/ext/standard/tests/streams/stream_errors_set_default_context_error.phpt b/ext/standard/tests/streams/stream_errors_set_default_context_error.phpt new file mode 100644 index 0000000000000..46fc0f7fd34b9 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_set_default_context_error.phpt @@ -0,0 +1,18 @@ +--TEST-- +Stream errors - prohibit setting error mode in default context +--FILE-- + [ + 'error_mode' => StreamErrorMode::Exception, + ] + ]); +} catch (\ValueError $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECT-- +Stream error handling options cannot be set on the default context 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..456575affc3a1 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_silent_with_handler.phpt @@ -0,0 +1,37 @@ +--TEST-- +Stream errors - custom error handler +--FILE-- + [ + 'error_mode' => StreamErrorMode::Silent, + 'error_handler' => function(array $errors) use (&$handler_called) { + $handler_called = true; + echo "Handler called\n"; + foreach ($errors as $error) { + echo "Wrapper: " . $error->wrapperName . "\n"; + echo "Code: " . $error->code->name . "\n"; + echo "Message: " . $error->message . "\n"; + echo "Param: " . ($error->param ?? 'null') . "\n"; + echo "Terminating: " . ($error->terminating ? 'yes' : 'no') . "\n"; + } + } + ] +]); + +$stream = fopen('php://nonexistent', 'r', false, $context); + +var_dump($handler_called); + +?> +--EXPECT-- +Handler called +Wrapper: PHP +Code: OpenFailed +Message: Failed to open stream: operation failed +Param: php://nonexistent +Terminating: yes +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..9e356216b0dd9 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_silent_with_storage.phpt @@ -0,0 +1,29 @@ +--TEST-- +Stream errors - silent mode with error storage +--FILE-- + [ + 'error_mode' => StreamErrorMode::Silent, + ] +]); + +$stream = fopen('php://nonexistent', 'r', false, $context); +var_dump($stream); + +$errors = stream_last_errors(); +foreach ($errors as $error) { + echo "Has error: yes\n"; + echo "Error code: " . $error->code->name . "\n"; + echo "Error wrapper: " . $error->wrapperName . "\n"; + echo "Error message: " . $error->message . "\n"; +} + +?> +--EXPECT-- +bool(false) +Has error: yes +Error code: OpenFailed +Error wrapper: PHP +Error message: Failed to open stream: operation failed 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..8155ce60bfee9 --- /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' => StreamErrorMode::Silent, + 'error_store' => StreamErrorStore::None, + ] +]); + +$stream = fopen('php://nonexistent', 'r', false, $context); + +$errors = stream_last_errors(); +echo "Has error: " . (!empty($error) ? "yes" : "no") . "\n"; + +?> +--EXPECT-- +Has error: no 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..42de8cc3b17df --- /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' => StreamErrorMode::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) diff --git a/main/php_streams.h b/main/php_streams.h index 1c52539cfcaee..601223bd9a5fd 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" @@ -642,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/cast.c b/main/streams/cast.c index 4dc8ddb5f6a30..638fa28d0d859 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, CastFailed, "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) && castas != PHP_STREAM_AS_FD_FOR_SELECT) { if (show_err) { - php_error_docref(NULL, E_WARNING, "Cannot cast a filtered stream on this system"); + php_stream_warn(stream, CastNotSupported, + "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, CastNotSupported, + "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, BufferedDataLost, + ZEND_LONG_FMT " bytes of buffered data lost during stream conversion!", + (zend_long)(stream->writepos - stream->readpos)); } if (castas == PHP_STREAM_AS_STDIO && ret) { diff --git a/main/streams/filter.c b/main/streams/filter.c index d0c1fdc8e788c..d63be91d27054 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, FilterFailed, + "Filter failed to process pre-buffered data"); return FAILURE; case PSFS_FEED_ME: /* We don't actually need data yet, diff --git a/main/streams/memory.c b/main/streams/memory.c index 9cb94542df6f5..1f5580de044ac 100644 --- a/main/streams/memory.c +++ b/main/streams/memory.c @@ -364,7 +364,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, + PermissionDenied, + "Unable to create temporary file, Check permissions in temporary files directory."); return 0; } php_stream_write(file, ZSTR_VAL(membuf), ZSTR_LEN(membuf)); @@ -491,7 +493,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, + PermissionDenied, "Unable to create temporary file."); return FAILURE; } @@ -641,7 +644,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con } if ((comma = (char *) memchr(path, ',', dlen)) == NULL) { - php_stream_wrapper_log_error(wrapper, options, "rfc2397: no comma in URL"); + php_stream_wrapper_log_warn(wrapper, context, options, + InvalidUrl, "rfc2397: no comma in URL"); return NULL; } @@ -653,7 +657,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, + InvalidUrl, "rfc2397: illegal media type"); return NULL; } @@ -668,7 +673,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, + InvalidUrl, "rfc2397: illegal media type"); return NULL; } /* get parameters and potentially ';base64' */ @@ -681,7 +687,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, + InvalidParam, "rfc2397: illegal parameter"); return NULL; } base64 = 1; @@ -701,7 +708,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, + InvalidUrl, "rfc2397: illegal URL"); return NULL; } } else { @@ -717,7 +725,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, + DecodingFailed, "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 new file mode 100644 index 0000000000000..c55c06e37f25a --- /dev/null +++ b/main/streams/php_stream_errors.h @@ -0,0 +1,239 @@ +/* + +----------------------------------------------------------------------+ + | 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 + +#include "php.h" +#include "php_streams.h" +#include "stream_errors_decl.h" + +BEGIN_EXTERN_C() + +/* Error mode context options (internal C constants) */ +#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 (internal C constants) */ +#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 + +/* Maximum operation nesting depth */ +#define PHP_STREAM_ERROR_MAX_DEPTH 1000 +/* Operations pool size to prevent extra allocations */ +#define PHP_STREAM_ERROR_OPERATION_POOL_SIZE 8 + +/* Shorthand for error code enum values */ +#define PHP_STREAM_EC(name) ZEND_ENUM_StreamErrorCode_##name + +/* Error code range boundaries (case_id based, ranges are [start, end)) */ +#define STREAM_EC_IO_START PHP_STREAM_EC(ReadFailed) +#define STREAM_EC_IO_END PHP_STREAM_EC(Disabled) +#define STREAM_EC_FS_START PHP_STREAM_EC(Disabled) +#define STREAM_EC_FS_END PHP_STREAM_EC(NotImplemented) +#define STREAM_EC_WRAPPER_START PHP_STREAM_EC(NotImplemented) +#define STREAM_EC_WRAPPER_END PHP_STREAM_EC(FilterNotFound) +#define STREAM_EC_FILTER_START PHP_STREAM_EC(FilterNotFound) +#define STREAM_EC_FILTER_END PHP_STREAM_EC(CastFailed) +#define STREAM_EC_CAST_START PHP_STREAM_EC(CastFailed) +#define STREAM_EC_CAST_END PHP_STREAM_EC(NetworkSendFailed) +#define STREAM_EC_NETWORK_START PHP_STREAM_EC(NetworkSendFailed) +#define STREAM_EC_NETWORK_END PHP_STREAM_EC(ArchivingFailed) +#define STREAM_EC_ENCODING_START PHP_STREAM_EC(ArchivingFailed) +#define STREAM_EC_ENCODING_END PHP_STREAM_EC(AllocationFailed) +#define STREAM_EC_RESOURCE_START PHP_STREAM_EC(AllocationFailed) +#define STREAM_EC_RESOURCE_END PHP_STREAM_EC(LockFailed) +#define STREAM_EC_LOCK_START PHP_STREAM_EC(LockFailed) +#define STREAM_EC_LOCK_END PHP_STREAM_EC(UserspaceNotImplemented) +#define STREAM_EC_USERSPACE_START PHP_STREAM_EC(UserspaceNotImplemented) +#define STREAM_EC_USERSPACE_END (PHP_STREAM_EC(UserspaceCallFailed) + 1) + +/* Wrapper name for PHP errors */ +#define PHP_STREAM_ERROR_WRAPPER_DEFAULT_NAME ":na" +#define PHP_STREAM_ERROR_WRAPPER_NAME(_wrapper) \ + (_wrapper ? _wrapper->wops->label : PHP_STREAM_ERROR_WRAPPER_DEFAULT_NAME) + +/* Error entry in chain (internal linked list) */ +typedef struct _php_stream_error_entry { + zend_string *message; + zend_enum_StreamErrorCode code; + char *wrapper_name; + char *param; + char *docref; + int severity; + bool terminating; + struct _php_stream_error_entry *next; +} php_stream_error_entry; + +/* Active error operation */ +typedef struct _php_stream_error_operation { + php_stream_error_entry *first_error; + php_stream_error_entry *last_error; + uint32_t error_count; +} php_stream_error_operation; + +/* Stored completed operation */ +typedef struct _php_stream_stored_error { + php_stream_error_entry *first_error; + uint32_t error_count; + struct _php_stream_stored_error *next; +} php_stream_stored_error; + +typedef struct { + php_stream_error_operation *current_operation; + uint32_t operation_depth; + php_stream_stored_error *stored_errors; + uint32_t stored_count; + php_stream_error_operation operation_pool[PHP_STREAM_ERROR_OPERATION_POOL_SIZE]; + php_stream_error_operation *overflow_operations; + uint32_t overflow_capacity; +} php_stream_error_state; + +/* Error operation management */ +PHPAPI php_stream_error_operation *php_stream_error_operation_begin(void); +PHPAPI void php_stream_error_operation_end(php_stream_context *context); +PHPAPI void php_stream_error_operation_end_for_stream(php_stream *stream); +PHPAPI void php_stream_error_operation_abort(void); + +/* State cleanup function */ +PHPAPI void php_stream_error_state_cleanup(void); + +/* Retrieve last stored errors as array of StreamError objects */ +PHPAPI void php_stream_error_get_last(zval *return_value); + +/* Clear all stored errors */ +PHPAPI void php_stream_error_clear_stored(void); + +/* Wrapper error reporting functions */ +PHPAPI void php_stream_wrapper_error_with_name(const char *wrapper_name, + php_stream_context *context, const char *docref, int options, int severity, + bool terminating, zend_enum_StreamErrorCode 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 terminating, + zend_enum_StreamErrorCode 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 terminating, + zend_enum_StreamErrorCode 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 terminating, zend_enum_StreamErrorCode 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 terminating, zend_enum_StreamErrorCode code, const char *fmt, ...) + ZEND_ATTRIBUTE_FORMAT(printf, 6, 7); + +/* Legacy wrapper error log functions */ +PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminating, + zend_enum_StreamErrorCode 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 terminating, + zend_enum_StreamErrorCode code, const char *param, const char *fmt, ...) + ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); + +PHPAPI void php_stream_display_wrapper_name_errors(const char *wrapper_name, + php_stream_context *context, zend_enum_StreamErrorCode code, const char *path, + const char *caption); + +PHPAPI void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, + php_stream_context *context, zend_enum_StreamErrorCode code, const char *path, + const char *caption); + +PHPAPI void php_stream_tidy_wrapper_name_error_log(const char *wrapper_name); +PHPAPI void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper); + +/* Convenience macros - code argument is the bare case name (e.g. RenameFailed) */ +#define php_stream_wrapper_warn(wrapper, context, options, code, ...) \ + php_stream_wrapper_error(wrapper, context, NULL, options, E_WARNING, true, \ + PHP_STREAM_EC(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, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_wrapper_warn_nt(wrapper, context, options, code, ...) \ + php_stream_wrapper_error(wrapper, context, NULL, options, E_WARNING, false, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_wrapper_notice(wrapper, context, options, code, ...) \ + php_stream_wrapper_error(wrapper, context, NULL, options, E_NOTICE, false, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_wrapper_warn_param(wrapper, context, options, code, param, ...) \ + php_stream_wrapper_error_param( \ + wrapper, context, NULL, options, E_WARNING, true, \ + PHP_STREAM_EC(code), param, __VA_ARGS__) + +#define php_stream_wrapper_warn_param_nt(wrapper, context, options, code, param, ...) \ + php_stream_wrapper_error_param( \ + wrapper, context, NULL, options, E_WARNING, false, \ + PHP_STREAM_EC(code), param, __VA_ARGS__) + +#define php_stream_wrapper_warn_param2(wrapper, context, options, code, param1, param2, ...) \ + php_stream_wrapper_error_param2( \ + wrapper, context, NULL, options, E_WARNING, true, \ + PHP_STREAM_EC(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, NULL, options, E_WARNING, false, \ + PHP_STREAM_EC(code), param1, param2, __VA_ARGS__) + +#define php_stream_warn(stream, code, ...) \ + php_stream_error(stream, NULL, E_WARNING, true, PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_warn_nt(stream, code, ...) \ + php_stream_error(stream, NULL, E_WARNING, false, PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_warn_docref(stream, docref, code, ...) \ + php_stream_error(stream, docref, E_WARNING, true, PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_notice(stream, code, ...) \ + php_stream_error(stream, NULL, E_NOTICE, false, PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_fatal(stream, code, ...) \ + php_stream_error(stream, NULL, E_ERROR, true, PHP_STREAM_EC(code), __VA_ARGS__) + +/* Legacy log variants */ +#define php_stream_wrapper_log_warn(wrapper, context, options, code, ...) \ + php_stream_wrapper_log_error(wrapper, context, options, E_WARNING, true, \ + PHP_STREAM_EC(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, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_wrapper_log_notice(wrapper, context, options, code, ...) \ + php_stream_wrapper_log_error(wrapper, context, options, E_NOTICE, false, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +END_EXTERN_C() + +#endif /* PHP_STREAM_ERRORS_H */ diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 74d8877a7f381..76800d222e403 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, WriteFailed, + "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, ReadFailed, + "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, + SeekNotSupported, "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, + InvalidMode, "`%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, + UnlinkFailed, 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, NULL, options, E_WARNING, + !success, PHP_STREAM_EC(ChownFailed), 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, NULL, options, E_WARNING, + !success, PHP_STREAM_EC(ChownFailed), 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, StatFailed, + 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, CopyFailed, + 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, + RenameFailed, 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, + MkdirFailed, "%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, + InvalidPath, "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, + MkdirFailed, + "%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, + MkdirFailed, + "%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, + NotFound, 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, + RmdirFailed, 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, + NotFound, 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, + PermissionDenied, 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, + MetaFailed, 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, + MetaFailed, 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, + MetaFailed, 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, + PathTooLong, + "%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, + PathTooLong, + "%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.c b/main/streams/stream_errors.c new file mode 100644 index 0000000000000..cec595ea32b0f --- /dev/null +++ b/main/streams/stream_errors.c @@ -0,0 +1,998 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + +----------------------------------------------------------------------+ + */ + +#define ZEND_ENUM_StreamErrorCode_USE_NAME_TABLE +#include "php.h" +#include "php_globals.h" +#include "php_streams.h" +#include "php_stream_errors.h" +#include "zend_enum.h" +#include "zend_exceptions.h" +#include "ext/standard/file.h" +#include "stream_errors_arginfo.h" + +/* Class entries */ +static zend_class_entry *php_ce_stream_error_code; +static zend_class_entry *php_ce_stream_error_mode; +static zend_class_entry *php_ce_stream_error_store; +static zend_class_entry *php_ce_stream_error; +static zend_class_entry *php_ce_stream_exception; + +/* Forward declarations */ +static void php_stream_error_entry_free(php_stream_error_entry *entry); +static bool php_stream_error_code_in_range(zval *this_zv, int start, int end); + +/* Helper to create a single StreamError object from an entry */ +static void php_stream_error_create_object(zval *zv, php_stream_error_entry *entry) +{ + object_init_ex(zv, php_ce_stream_error); + + const char *case_name = NULL; + if (entry->code > 0 && entry->code <= ZEND_ENUM_StreamErrorCode_CASE_COUNT) { + case_name = zend_enum_StreamErrorCode_case_names[entry->code]; + } + if (!case_name) { + case_name = "Generic"; + } + + zend_object *enum_obj = zend_enum_get_case_cstr(php_ce_stream_error_code, case_name); + ZEND_ASSERT(enum_obj != NULL); + + zval code_enum; + ZVAL_OBJ_COPY(&code_enum, enum_obj); + + zend_update_property(php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("code"), &code_enum); + zval_ptr_dtor(&code_enum); + + zend_update_property_str( + php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("message"), entry->message); + + zend_update_property_string(php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("wrapperName"), + entry->wrapper_name ? entry->wrapper_name : ""); + + zend_update_property_long( + php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("severity"), entry->severity); + + zend_update_property_bool( + php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("terminating"), entry->terminating); + + if (entry->param) { + zend_update_property_string( + php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("param"), entry->param); + } else { + zend_update_property_null(php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("param")); + } +} + +/* Create array of StreamError objects from error chain */ +PHPAPI void php_stream_error_create_array(zval *zv, php_stream_error_entry *first) +{ + array_init(zv); + + php_stream_error_entry *entry = first; + while (entry) { + zval error_obj; + php_stream_error_create_object(&error_obj, entry); + zend_hash_next_index_insert_new(Z_ARRVAL_P(zv), &error_obj); + entry = entry->next; + } +} + +/* 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) { + return PHP_STREAM_ERROR_MODE_ERROR; + } + + if (Z_TYPE_P(option) != IS_OBJECT + || !instanceof_function(Z_OBJCE_P(option), php_ce_stream_error_mode)) { + zend_type_error("stream context option 'error_mode' must be of type StreamErrorMode"); + return PHP_STREAM_ERROR_MODE_ERROR; + } + + switch ((zend_enum_StreamErrorMode) zend_enum_fetch_case_id(Z_OBJ_P(option))) { + case ZEND_ENUM_StreamErrorMode_Error: + return PHP_STREAM_ERROR_MODE_ERROR; + case ZEND_ENUM_StreamErrorMode_Exception: + return PHP_STREAM_ERROR_MODE_EXCEPTION; + case ZEND_ENUM_StreamErrorMode_Silent: + return PHP_STREAM_ERROR_MODE_SILENT; + } + + 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) { + return php_stream_auto_decide_error_store_mode(error_mode); + } + + if (Z_TYPE_P(option) != IS_OBJECT + || !instanceof_function(Z_OBJCE_P(option), php_ce_stream_error_store)) { + zend_type_error("stream context option 'error_store' must be of type StreamErrorStore"); + return php_stream_auto_decide_error_store_mode(error_mode); + } + + switch ((zend_enum_StreamErrorStore) zend_enum_fetch_case_id(Z_OBJ_P(option))) { + case ZEND_ENUM_StreamErrorStore_Auto: + return php_stream_auto_decide_error_store_mode(error_mode); + case ZEND_ENUM_StreamErrorStore_None: + return PHP_STREAM_ERROR_STORE_NONE; + case ZEND_ENUM_StreamErrorStore_NonTerminating: + return PHP_STREAM_ERROR_STORE_NON_TERM; + case ZEND_ENUM_StreamErrorStore_Terminating: + return PHP_STREAM_ERROR_STORE_TERMINAL; + case ZEND_ENUM_StreamErrorStore_All: + return PHP_STREAM_ERROR_STORE_ALL; + } + + return php_stream_auto_decide_error_store_mode(error_mode); +} + +/* Helper functions */ + +static bool php_stream_has_terminating_error(php_stream_error_operation *op) +{ + php_stream_error_entry *entry = op->first_error; + while (entry) { + if (entry->terminating) { + return true; + } + entry = entry->next; + } + return false; +} + +static inline php_stream_error_operation *php_stream_get_operation_at_depth(uint32_t depth) +{ + php_stream_error_state *state = &FG(stream_error_state); + + if (depth < PHP_STREAM_ERROR_OPERATION_POOL_SIZE) { + return &state->operation_pool[depth]; + } else { + uint32_t overflow_index = depth - PHP_STREAM_ERROR_OPERATION_POOL_SIZE; + ZEND_ASSERT(overflow_index < state->overflow_capacity); + return &state->overflow_operations[overflow_index]; + } +} + +static inline php_stream_error_operation *php_stream_get_parent_operation(void) +{ + php_stream_error_state *state = &FG(stream_error_state); + + if (state->operation_depth <= 1) { + return NULL; + } + + return php_stream_get_operation_at_depth(state->operation_depth - 2); +} + +/* Clean up functions */ + +static void php_stream_error_entry_free(php_stream_error_entry *entry) +{ + while (entry) { + php_stream_error_entry *next = entry->next; + zend_string_release(entry->message); + efree(entry->wrapper_name); + efree(entry->param); + efree(entry); + entry = next; + } +} + +PHPAPI void php_stream_error_state_cleanup(void) +{ + php_stream_error_state *state = &FG(stream_error_state); + + while (state->current_operation) { + php_stream_error_operation *op = state->current_operation; + state->operation_depth--; + state->current_operation = php_stream_get_parent_operation(); + + php_stream_error_entry_free(op->first_error); + + op->first_error = NULL; + op->last_error = NULL; + op->error_count = 0; + } + + php_stream_stored_error *stored = state->stored_errors; + while (stored) { + php_stream_stored_error *next = stored->next; + php_stream_error_entry_free(stored->first_error); + efree(stored); + stored = next; + } + + state->stored_errors = NULL; + state->stored_count = 0; + state->operation_depth = 0; + + if (state->overflow_operations) { + efree(state->overflow_operations); + state->overflow_operations = NULL; + state->overflow_capacity = 0; + } +} + +PHPAPI void php_stream_error_get_last(zval *return_value) +{ + php_stream_error_state *state = &FG(stream_error_state); + + if (!state->stored_errors) { + ZVAL_EMPTY_ARRAY(return_value); + return; + } + + php_stream_error_create_array(return_value, state->stored_errors->first_error); +} + +PHPAPI void php_stream_error_clear_stored(void) +{ + php_stream_error_state *state = &FG(stream_error_state); + + php_stream_stored_error *stored = state->stored_errors; + while (stored) { + php_stream_stored_error *next = stored->next; + php_stream_error_entry_free(stored->first_error); + efree(stored); + stored = next; + } + + state->stored_errors = NULL; + state->stored_count = 0; +} + +/* Error operation stack management */ + +PHPAPI php_stream_error_operation *php_stream_error_operation_begin(void) +{ + php_stream_error_state *state = &FG(stream_error_state); + + if (state->operation_depth >= PHP_STREAM_ERROR_MAX_DEPTH) { + php_error_docref(NULL, E_WARNING, + "Stream error operation depth exceeded (%u), possible infinite recursion", + state->operation_depth); + return NULL; + } + + php_stream_error_operation *op; + + if (state->operation_depth < PHP_STREAM_ERROR_OPERATION_POOL_SIZE) { + op = &state->operation_pool[state->operation_depth]; + } else { + uint32_t overflow_index = state->operation_depth - PHP_STREAM_ERROR_OPERATION_POOL_SIZE; + + if (overflow_index >= state->overflow_capacity) { + uint32_t new_capacity + = state->overflow_capacity == 0 ? 8 : state->overflow_capacity * 2; + php_stream_error_operation *new_overflow = erealloc( + state->overflow_operations, sizeof(php_stream_error_operation) * new_capacity); + state->overflow_operations = new_overflow; + state->overflow_capacity = new_capacity; + } + + op = &state->overflow_operations[overflow_index]; + } + + op->first_error = NULL; + op->last_error = NULL; + op->error_count = 0; + + state->current_operation = op; + state->operation_depth++; + + return op; +} + +static void php_stream_error_add(zend_enum_StreamErrorCode code, const char *wrapper_name, + zend_string *message, const char *docref, char *param, int severity, bool terminating) +{ + php_stream_error_operation *op = FG(stream_error_state).current_operation; + ZEND_ASSERT(op != NULL); + + php_stream_error_entry *entry = emalloc(sizeof(php_stream_error_entry)); + entry->message = message; + entry->code = code; + entry->wrapper_name = wrapper_name ? estrdup(wrapper_name) : NULL; + entry->param = param; + entry->docref = docref ? estrdup(docref) : NULL; + entry->severity = severity; + entry->terminating = terminating; + entry->next = NULL; + + if (op->last_error) { + op->last_error->next = entry; + } else { + op->first_error = entry; + } + op->last_error = entry; + op->error_count++; +} + +/* Error reporting */ + +static void php_stream_call_error_handler(zval *handler, zval *errors_array) +{ + zend_fcall_info_cache fcc; + char *is_callable_error = NULL; + + 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); + } + return; + } + + zval retval; + + call_user_function(NULL, NULL, handler, &retval, 1, errors_array); + + zval_ptr_dtor(&retval); +} + +static void php_stream_throw_exception_with_errors(php_stream_error_operation *op) +{ + if (!op->first_error) { + return; + } + + zval ex; + object_init_ex(&ex, php_ce_stream_exception); + + /* Set message from first error */ + zend_update_property_string(php_ce_stream_exception, Z_OBJ(ex), ZEND_STRL("message"), + ZSTR_VAL(op->first_error->message)); + + /* Set code from first error */ + zend_update_property_long(php_ce_stream_exception, Z_OBJ(ex), ZEND_STRL("code"), + (zend_long) op->first_error->code); + + /* Build errors array and set it */ + zval errors_array; + php_stream_error_create_array(&errors_array, op->first_error); + zend_update_property(php_ce_stream_exception, Z_OBJ(ex), ZEND_STRL("errors"), &errors_array); + zval_ptr_dtor(&errors_array); + + zend_throw_exception_object(&ex); +} + +static void php_stream_report_errors(php_stream_context *context, php_stream_error_operation *op, + int error_mode, bool is_terminating) +{ + switch (error_mode) { + case PHP_STREAM_ERROR_MODE_ERROR: { + php_stream_error_entry *entry = op->first_error; + while (entry) { + if (entry->param) { + php_error_docref1(entry->docref, entry->param, entry->severity, "%s", + ZSTR_VAL(entry->message)); + } else { + php_error_docref( + entry->docref, entry->severity, "%s", ZSTR_VAL(entry->message)); + } + entry = entry->next; + } + break; + } + + case PHP_STREAM_ERROR_MODE_EXCEPTION: { + if (is_terminating) { + php_stream_throw_exception_with_errors(op); + } + break; + } + + case PHP_STREAM_ERROR_MODE_SILENT: + break; + } + + /* Call user error handler if set */ + zval *handler + = context ? php_stream_context_get_option(context, "stream", "error_handler") : NULL; + + if (handler) { + zval errors_array; + php_stream_error_create_array(&errors_array, op->first_error); + + php_stream_call_error_handler(handler, &errors_array); + + zval_ptr_dtor(&errors_array); + } +} + +/* Error storage */ + +PHPAPI void php_stream_error_operation_end(php_stream_context *context) +{ + php_stream_error_state *state = &FG(stream_error_state); + php_stream_error_operation *op = state->current_operation; + + if (!op) { + return; + } + + state->operation_depth--; + state->current_operation = php_stream_get_parent_operation(); + + if (op->error_count > 0) { + if (context == NULL) { + context = FG(default_context); + } + + int error_mode = php_stream_get_error_mode(context); + int store_mode = php_stream_get_error_store_mode(context, error_mode); + + bool is_terminating = php_stream_has_terminating_error(op); + + php_stream_report_errors(context, op, error_mode, is_terminating); + + if (store_mode == PHP_STREAM_ERROR_STORE_NONE) { + php_stream_error_entry_free(op->first_error); + op->first_error = NULL; + } else { + php_stream_error_entry *entry = op->first_error; + php_stream_error_entry *prev = NULL; + php_stream_error_entry *to_store_first = NULL; + php_stream_error_entry *to_store_last = NULL; + uint32_t to_store_count = 0; + php_stream_error_entry *remaining_first = NULL; + + while (entry) { + php_stream_error_entry *next = entry->next; + bool should_store = false; + + if (store_mode == PHP_STREAM_ERROR_STORE_ALL) { + should_store = true; + } else if (store_mode == PHP_STREAM_ERROR_STORE_NON_TERM && !entry->terminating) { + should_store = true; + } else if (store_mode == PHP_STREAM_ERROR_STORE_TERMINAL && entry->terminating) { + should_store = true; + } + + if (should_store) { + entry->next = NULL; + if (to_store_last) { + to_store_last->next = entry; + } else { + to_store_first = entry; + } + to_store_last = entry; + to_store_count++; + } else { + entry->next = NULL; + if (prev) { + prev->next = entry; + } else { + remaining_first = entry; + } + prev = entry; + } + + entry = next; + } + + if (to_store_first) { + php_stream_stored_error *stored = emalloc(sizeof(php_stream_stored_error)); + stored->first_error = to_store_first; + stored->error_count = to_store_count; + stored->next = state->stored_errors; + + state->stored_errors = stored; + state->stored_count++; + } + + if (remaining_first) { + php_stream_error_entry_free(remaining_first); + } + + op->first_error = NULL; + } + } + + op->first_error = NULL; + op->last_error = NULL; + op->error_count = 0; +} + +PHPAPI void php_stream_error_operation_end_for_stream(php_stream *stream) +{ + php_stream_error_state *state = &FG(stream_error_state); + php_stream_error_operation *op = state->current_operation; + + if (!op) { + return; + } + + if (op->error_count == 0) { + state->operation_depth--; + state->current_operation = php_stream_get_parent_operation(); + + op->first_error = NULL; + op->last_error = NULL; + return; + } + + php_stream_context *context = PHP_STREAM_CONTEXT(stream); + php_stream_error_operation_end(context); +} + +PHPAPI void php_stream_error_operation_abort(void) +{ + php_stream_error_state *state = &FG(stream_error_state); + php_stream_error_operation *op = state->current_operation; + + if (!op) { + return; + } + + state->operation_depth--; + state->current_operation = php_stream_get_parent_operation(); + + php_stream_error_entry_free(op->first_error); + op->first_error = NULL; + op->last_error = NULL; + op->error_count = 0; +} + +/* Wrapper error reporting */ + +static void php_stream_wrapper_error_internal(const char *wrapper_name, php_stream_context *context, + const char *docref, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, char *param, zend_string *message) +{ + bool implicit_operation = (FG(stream_error_state).current_operation == NULL); + if (implicit_operation) { + php_stream_error_operation_begin(); + } + + php_stream_error_add(code, wrapper_name, message, docref, param, severity, terminating); + + if (implicit_operation) { + php_stream_error_operation_end(context); + } +} + +PHPAPI void php_stream_wrapper_error_with_name(const char *wrapper_name, + php_stream_context *context, const char *docref, int options, int severity, + bool terminating, zend_enum_StreamErrorCode code, const char *fmt, ...) +{ + if (!(options & REPORT_ERRORS)) { + return; + } + + va_list args; + va_start(args, fmt); + zend_string *message = vstrpprintf(0, fmt, args); + va_end(args); + + php_stream_wrapper_error_internal( + wrapper_name, context, docref, options, severity, terminating, code, NULL, message); +} + +PHPAPI void php_stream_wrapper_error(php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *fmt, ...) +{ + if (!(options & REPORT_ERRORS)) { + return; + } + + va_list args; + va_start(args, fmt); + zend_string *message = vstrpprintf(0, fmt, args); + va_end(args); + + const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper); + + php_stream_wrapper_error_internal( + wrapper_name, context, docref, options, severity, terminating, code, NULL, message); +} + +PHPAPI void php_stream_wrapper_error_param(php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *param, const char *fmt, ...) +{ + if (!(options & REPORT_ERRORS)) { + return; + } + + va_list args; + va_start(args, fmt); + zend_string *message = vstrpprintf(0, fmt, args); + va_end(args); + + const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper); + char *param_copy = param ? estrdup(param) : NULL; + + php_stream_wrapper_error_internal(wrapper_name, context, docref, options, severity, terminating, + code, param_copy, message); +} + +PHPAPI void php_stream_wrapper_error_param2(php_stream_wrapper *wrapper, + php_stream_context *context, const char *docref, int options, int severity, + bool terminating, zend_enum_StreamErrorCode code, const char *param1, const char *param2, + const char *fmt, ...) +{ + if (!(options & REPORT_ERRORS)) { + return; + } + + char *combined_param; + spprintf(&combined_param, 0, "%s,%s", param1, param2); + + va_list args; + va_start(args, fmt); + zend_string *message = vstrpprintf(0, fmt, args); + va_end(args); + + const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper); + + php_stream_wrapper_error_internal(wrapper_name, context, docref, options, severity, terminating, + code, combined_param, message); +} + +/* Stream error reporting */ + +PHPAPI void php_stream_error(php_stream *stream, const char *docref, int severity, + bool terminating, zend_enum_StreamErrorCode code, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + zend_string *message = vstrpprintf(0, fmt, args); + va_end(args); + + const char *wrapper_name = stream->wrapper ? stream->wrapper->wops->label : "stream"; + + php_stream_context *context = PHP_STREAM_CONTEXT(stream); + + php_stream_wrapper_error_internal(wrapper_name, context, docref, REPORT_ERRORS, severity, + terminating, code, NULL, message); +} + +/* Legacy wrapper error logging */ + +static void php_stream_error_entry_dtor_legacy(void *error) +{ + php_stream_error_entry *entry = *(php_stream_error_entry **) error; + zend_string_release(entry->message); + efree(entry->wrapper_name); + efree(entry->param); + efree(entry->docref); + 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); +} + +static void php_stream_wrapper_log_store_error(zend_string *message, zend_enum_StreamErrorCode code, + const char *wrapper_name, const char *param, int severity, bool terminating) +{ + char *param_copy = param ? estrdup(param) : NULL; + + php_stream_error_entry *entry = ecalloc(1, sizeof(php_stream_error_entry)); + entry->message = message; + entry->code = code; + entry->wrapper_name = wrapper_name ? estrdup(wrapper_name) : NULL; + entry->param = param_copy; + entry->severity = severity; + entry->terminating = terminating; + + 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), 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_legacy, 0); + 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); +} + +static void php_stream_wrapper_log_error_internal(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, char *param, const char *fmt, va_list args) +{ + zend_string *message = vstrpprintf(0, fmt, args); + const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper); + + if (options & REPORT_ERRORS) { + php_stream_wrapper_error_internal( + wrapper_name, context, NULL, options, severity, terminating, code, param, message); + } else { + php_stream_wrapper_log_store_error( + message, code, wrapper_name, param, severity, terminating); + } +} + +PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + php_stream_wrapper_log_error_internal( + wrapper, context, options, severity, terminating, 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 terminating, + zend_enum_StreamErrorCode code, const char *param, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + char *param_copy = param ? estrdup(param) : NULL; + php_stream_wrapper_log_error_internal( + wrapper, context, options, severity, terminating, code, param_copy, fmt, args); + va_end(args); +} + +static zend_llist *php_stream_get_wrapper_errors_list(const char *wrapper_name) +{ + if (!FG(wrapper_logged_errors)) { + return NULL; + } + return (zend_llist *) zend_hash_str_find_ptr( + FG(wrapper_logged_errors), wrapper_name, strlen(wrapper_name)); +} + +PHPAPI void php_stream_display_wrapper_name_errors(const char *wrapper_name, + php_stream_context *context, zend_enum_StreamErrorCode code, const char *path, + const char *caption) +{ + char *msg; + char errstr[256]; + int free_msg = 0; + + if (EG(exception)) { + return; + } + + char *tmp = estrdup(path); + if (strcmp(wrapper_name, PHP_STREAM_ERROR_WRAPPER_DEFAULT_NAME)) { + zend_llist *err_list = php_stream_get_wrapper_errors_list(wrapper_name); + 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++) { + l += ZSTR_LEN((*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 (!strcmp(wrapper_name, php_plain_files_wrapper.wops->label)) { + 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); + + zend_string *message = strpprintf(0, "%s: %s", caption, msg); + + php_stream_wrapper_error_internal(wrapper_name, context, NULL, REPORT_ERRORS, E_WARNING, true, + code, tmp, message); + + if (free_msg) { + efree(msg); + } +} + +PHPAPI void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, + php_stream_context *context, zend_enum_StreamErrorCode code, const char *path, + const char *caption) +{ + if (wrapper) { + const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper); + php_stream_display_wrapper_name_errors(wrapper_name, context, code, path, caption); + } +} + +PHPAPI void php_stream_tidy_wrapper_name_error_log(const char *wrapper_name) +{ + if (FG(wrapper_logged_errors)) { + zend_hash_str_del(FG(wrapper_logged_errors), wrapper_name, strlen(wrapper_name)); + } +} + +PHPAPI void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper) +{ + if (wrapper) { + const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper); + php_stream_tidy_wrapper_name_error_log(wrapper_name); + } +} + +/* StreamException methods */ + +PHP_METHOD(StreamException, getErrors) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zval *errors = zend_read_property( + php_ce_stream_exception, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errors"), 1, NULL); + + RETURN_COPY(errors); +} + +/* StreamErrorCode range helpers - case_id based */ + +static bool php_stream_error_code_in_range(zval *this_zv, int start, int end) +{ + int case_id = zend_enum_fetch_case_id(Z_OBJ_P(this_zv)); + return case_id >= start && case_id < end; +} + +PHP_METHOD(StreamErrorCode, isIoError) +{ + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_BOOL(php_stream_error_code_in_range(ZEND_THIS, STREAM_EC_IO_START, STREAM_EC_IO_END)); +} + +PHP_METHOD(StreamErrorCode, isFileSystemError) +{ + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_BOOL(php_stream_error_code_in_range(ZEND_THIS, STREAM_EC_FS_START, STREAM_EC_FS_END)); +} + +PHP_METHOD(StreamErrorCode, isWrapperError) +{ + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_BOOL(php_stream_error_code_in_range( + ZEND_THIS, STREAM_EC_WRAPPER_START, STREAM_EC_WRAPPER_END)); +} + +PHP_METHOD(StreamErrorCode, isFilterError) +{ + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_BOOL(php_stream_error_code_in_range( + ZEND_THIS, STREAM_EC_FILTER_START, STREAM_EC_FILTER_END)); +} + +PHP_METHOD(StreamErrorCode, isCastError) +{ + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_BOOL(php_stream_error_code_in_range( + ZEND_THIS, STREAM_EC_CAST_START, STREAM_EC_CAST_END)); +} + +PHP_METHOD(StreamErrorCode, isNetworkError) +{ + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_BOOL(php_stream_error_code_in_range( + ZEND_THIS, STREAM_EC_NETWORK_START, STREAM_EC_NETWORK_END)); +} + +PHP_METHOD(StreamErrorCode, isEncodingError) +{ + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_BOOL(php_stream_error_code_in_range( + ZEND_THIS, STREAM_EC_ENCODING_START, STREAM_EC_ENCODING_END)); +} + +PHP_METHOD(StreamErrorCode, isResourceError) +{ + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_BOOL(php_stream_error_code_in_range( + ZEND_THIS, STREAM_EC_RESOURCE_START, STREAM_EC_RESOURCE_END)); +} + +PHP_METHOD(StreamErrorCode, isLockError) +{ + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_BOOL(php_stream_error_code_in_range( + ZEND_THIS, STREAM_EC_LOCK_START, STREAM_EC_LOCK_END)); +} + +PHP_METHOD(StreamErrorCode, isUserspaceError) +{ + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_BOOL(php_stream_error_code_in_range( + ZEND_THIS, STREAM_EC_USERSPACE_START, STREAM_EC_USERSPACE_END)); +} + +/* Module init */ + +PHP_MINIT_FUNCTION(stream_errors) +{ + php_ce_stream_error_code = register_class_StreamErrorCode(); + php_ce_stream_error_mode = register_class_StreamErrorMode(); + php_ce_stream_error_store = register_class_StreamErrorStore(); + + php_ce_stream_error = register_class_StreamError(); + php_ce_stream_exception = register_class_StreamException(zend_ce_exception); + + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(stream_errors) +{ + return SUCCESS; +} diff --git a/main/streams/stream_errors.stub.php b/main/streams/stream_errors.stub.php new file mode 100644 index 0000000000000..a3508f1eb76c0 --- /dev/null +++ b/main/streams/stream_errors.stub.php @@ -0,0 +1,163 @@ + */ + private array $errors = []; + + /** @return array */ + public function getErrors(): array {} +} diff --git a/main/streams/stream_errors_arginfo.h b/main/streams/stream_errors_arginfo.h new file mode 100644 index 0000000000000..cfa4e5a44b539 --- /dev/null +++ b/main/streams/stream_errors_arginfo.h @@ -0,0 +1,304 @@ +/* This is a generated file, edit stream_errors.stub.php instead. + * Stub hash: 65d3a47368993358ac8a4a41bc93dbff5677d3f5 + * Has decl header: yes */ + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_StreamErrorCode_isIoError, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_StreamErrorCode_isFileSystemError arginfo_class_StreamErrorCode_isIoError + +#define arginfo_class_StreamErrorCode_isWrapperError arginfo_class_StreamErrorCode_isIoError + +#define arginfo_class_StreamErrorCode_isFilterError arginfo_class_StreamErrorCode_isIoError + +#define arginfo_class_StreamErrorCode_isCastError arginfo_class_StreamErrorCode_isIoError + +#define arginfo_class_StreamErrorCode_isNetworkError arginfo_class_StreamErrorCode_isIoError + +#define arginfo_class_StreamErrorCode_isEncodingError arginfo_class_StreamErrorCode_isIoError + +#define arginfo_class_StreamErrorCode_isResourceError arginfo_class_StreamErrorCode_isIoError + +#define arginfo_class_StreamErrorCode_isLockError arginfo_class_StreamErrorCode_isIoError + +#define arginfo_class_StreamErrorCode_isUserspaceError arginfo_class_StreamErrorCode_isIoError + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_StreamException_getErrors, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +static ZEND_METHOD(StreamErrorCode, isIoError); +static ZEND_METHOD(StreamErrorCode, isFileSystemError); +static ZEND_METHOD(StreamErrorCode, isWrapperError); +static ZEND_METHOD(StreamErrorCode, isFilterError); +static ZEND_METHOD(StreamErrorCode, isCastError); +static ZEND_METHOD(StreamErrorCode, isNetworkError); +static ZEND_METHOD(StreamErrorCode, isEncodingError); +static ZEND_METHOD(StreamErrorCode, isResourceError); +static ZEND_METHOD(StreamErrorCode, isLockError); +static ZEND_METHOD(StreamErrorCode, isUserspaceError); +static ZEND_METHOD(StreamException, getErrors); + +static const zend_function_entry class_StreamErrorCode_methods[] = { + ZEND_ME(StreamErrorCode, isIoError, arginfo_class_StreamErrorCode_isIoError, ZEND_ACC_PUBLIC) + ZEND_ME(StreamErrorCode, isFileSystemError, arginfo_class_StreamErrorCode_isFileSystemError, ZEND_ACC_PUBLIC) + ZEND_ME(StreamErrorCode, isWrapperError, arginfo_class_StreamErrorCode_isWrapperError, ZEND_ACC_PUBLIC) + ZEND_ME(StreamErrorCode, isFilterError, arginfo_class_StreamErrorCode_isFilterError, ZEND_ACC_PUBLIC) + ZEND_ME(StreamErrorCode, isCastError, arginfo_class_StreamErrorCode_isCastError, ZEND_ACC_PUBLIC) + ZEND_ME(StreamErrorCode, isNetworkError, arginfo_class_StreamErrorCode_isNetworkError, ZEND_ACC_PUBLIC) + ZEND_ME(StreamErrorCode, isEncodingError, arginfo_class_StreamErrorCode_isEncodingError, ZEND_ACC_PUBLIC) + ZEND_ME(StreamErrorCode, isResourceError, arginfo_class_StreamErrorCode_isResourceError, ZEND_ACC_PUBLIC) + ZEND_ME(StreamErrorCode, isLockError, arginfo_class_StreamErrorCode_isLockError, ZEND_ACC_PUBLIC) + ZEND_ME(StreamErrorCode, isUserspaceError, arginfo_class_StreamErrorCode_isUserspaceError, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static const zend_function_entry class_StreamException_methods[] = { + ZEND_ME(StreamException, getErrors, arginfo_class_StreamException_getErrors, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static zend_class_entry *register_class_StreamErrorCode(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("StreamErrorCode", IS_UNDEF, class_StreamErrorCode_methods); + + zend_enum_add_case_cstr(class_entry, "None", NULL); + + zend_enum_add_case_cstr(class_entry, "Generic", NULL); + + zend_enum_add_case_cstr(class_entry, "ReadFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "WriteFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "SeekFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "SeekNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "FlushFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "TruncateFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "ConnectFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "BindFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "ListenFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "NotWritable", NULL); + + zend_enum_add_case_cstr(class_entry, "NotReadable", NULL); + + zend_enum_add_case_cstr(class_entry, "Disabled", NULL); + + zend_enum_add_case_cstr(class_entry, "NotFound", NULL); + + zend_enum_add_case_cstr(class_entry, "PermissionDenied", NULL); + + zend_enum_add_case_cstr(class_entry, "AlreadyExists", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidPath", NULL); + + zend_enum_add_case_cstr(class_entry, "PathTooLong", NULL); + + zend_enum_add_case_cstr(class_entry, "OpenFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "CreateFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "DupFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "UnlinkFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "RenameFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "MkdirFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "RmdirFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "StatFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "MetaFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "ChmodFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "ChownFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "CopyFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "TouchFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidMode", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidMeta", NULL); + + zend_enum_add_case_cstr(class_entry, "ModeNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "Readonly", NULL); + + zend_enum_add_case_cstr(class_entry, "RecursionDetected", NULL); + + zend_enum_add_case_cstr(class_entry, "NotImplemented", NULL); + + zend_enum_add_case_cstr(class_entry, "NoOpener", NULL); + + zend_enum_add_case_cstr(class_entry, "PersistentNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "WrapperNotFound", NULL); + + zend_enum_add_case_cstr(class_entry, "WrapperDisabled", NULL); + + zend_enum_add_case_cstr(class_entry, "ProtocolUnsupported", NULL); + + zend_enum_add_case_cstr(class_entry, "WrapperRegistrationFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "WrapperUnregistrationFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "WrapperRestorationFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "FilterNotFound", NULL); + + zend_enum_add_case_cstr(class_entry, "FilterFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "CastFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "CastNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "MakeSeekableFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "BufferedDataLost", NULL); + + zend_enum_add_case_cstr(class_entry, "NetworkSendFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "NetworkRecvFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "SslNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "ResumptionFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "SocketPathTooLong", NULL); + + zend_enum_add_case_cstr(class_entry, "OobNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "ProtocolError", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidUrl", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidResponse", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidHeader", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidParam", NULL); + + zend_enum_add_case_cstr(class_entry, "RedirectLimit", NULL); + + zend_enum_add_case_cstr(class_entry, "AuthFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "ArchivingFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "EncodingFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "DecodingFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidFormat", NULL); + + zend_enum_add_case_cstr(class_entry, "AllocationFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "TemporaryFileFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "LockFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "LockNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "UserspaceNotImplemented", NULL); + + zend_enum_add_case_cstr(class_entry, "UserspaceInvalidReturn", NULL); + + zend_enum_add_case_cstr(class_entry, "UserspaceCallFailed", NULL); + + return class_entry; +} + +static zend_class_entry *register_class_StreamErrorMode(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("StreamErrorMode", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "Error", NULL); + + zend_enum_add_case_cstr(class_entry, "Exception", NULL); + + zend_enum_add_case_cstr(class_entry, "Silent", NULL); + + return class_entry; +} + +static zend_class_entry *register_class_StreamErrorStore(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("StreamErrorStore", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "Auto", NULL); + + zend_enum_add_case_cstr(class_entry, "None", NULL); + + zend_enum_add_case_cstr(class_entry, "NonTerminating", NULL); + + zend_enum_add_case_cstr(class_entry, "Terminating", NULL); + + zend_enum_add_case_cstr(class_entry, "All", NULL); + + return class_entry; +} + +static zend_class_entry *register_class_StreamError(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "StreamError", NULL); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_READONLY_CLASS); + + zval property_code_default_value; + ZVAL_UNDEF(&property_code_default_value); + zend_string *property_code_class_StreamErrorCode = zend_string_init("StreamErrorCode", sizeof("StreamErrorCode")-1, 1); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_CODE), &property_code_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_code_class_StreamErrorCode, 0, 0)); + + zval property_message_default_value; + ZVAL_UNDEF(&property_message_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_MESSAGE), &property_message_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + + zval property_wrapperName_default_value; + ZVAL_UNDEF(&property_wrapperName_default_value); + zend_string *property_wrapperName_name = zend_string_init("wrapperName", sizeof("wrapperName") - 1, true); + zend_declare_typed_property(class_entry, property_wrapperName_name, &property_wrapperName_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release_ex(property_wrapperName_name, true); + + zval property_severity_default_value; + ZVAL_UNDEF(&property_severity_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_SEVERITY), &property_severity_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + + zval property_terminating_default_value; + ZVAL_UNDEF(&property_terminating_default_value); + zend_string *property_terminating_name = zend_string_init("terminating", sizeof("terminating") - 1, true); + zend_declare_typed_property(class_entry, property_terminating_name, &property_terminating_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release_ex(property_terminating_name, true); + + zval property_param_default_value; + ZVAL_UNDEF(&property_param_default_value); + zend_string *property_param_name = zend_string_init("param", sizeof("param") - 1, true); + zend_declare_typed_property(class_entry, property_param_name, &property_param_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + zend_string_release_ex(property_param_name, true); + + return class_entry; +} + +static zend_class_entry *register_class_StreamException(zend_class_entry *class_entry_Exception) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "StreamException", class_StreamException_methods); + class_entry = zend_register_internal_class_with_flags(&ce, class_entry_Exception, 0); + + zval property_errors_default_value; + ZVAL_EMPTY_ARRAY(&property_errors_default_value); + zend_string *property_errors_name = zend_string_init("errors", sizeof("errors") - 1, true); + zend_declare_typed_property(class_entry, property_errors_name, &property_errors_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); + zend_string_release_ex(property_errors_name, true); + + return class_entry; +} diff --git a/main/streams/stream_errors_decl.h b/main/streams/stream_errors_decl.h new file mode 100644 index 0000000000000..e29bd21f376e5 --- /dev/null +++ b/main/streams/stream_errors_decl.h @@ -0,0 +1,183 @@ +/* This is a generated file, edit stream_errors.stub.php instead. + * Stub hash: 65d3a47368993358ac8a4a41bc93dbff5677d3f5 */ + +#ifndef ZEND_STREAM_ERRORS_DECL_65d3a47368993358ac8a4a41bc93dbff5677d3f5_H +#define ZEND_STREAM_ERRORS_DECL_65d3a47368993358ac8a4a41bc93dbff5677d3f5_H + +typedef enum zend_enum_StreamErrorCode { + ZEND_ENUM_StreamErrorCode_None = 1, + ZEND_ENUM_StreamErrorCode_Generic = 2, + ZEND_ENUM_StreamErrorCode_ReadFailed = 3, + ZEND_ENUM_StreamErrorCode_WriteFailed = 4, + ZEND_ENUM_StreamErrorCode_SeekFailed = 5, + ZEND_ENUM_StreamErrorCode_SeekNotSupported = 6, + ZEND_ENUM_StreamErrorCode_FlushFailed = 7, + ZEND_ENUM_StreamErrorCode_TruncateFailed = 8, + ZEND_ENUM_StreamErrorCode_ConnectFailed = 9, + ZEND_ENUM_StreamErrorCode_BindFailed = 10, + ZEND_ENUM_StreamErrorCode_ListenFailed = 11, + ZEND_ENUM_StreamErrorCode_NotWritable = 12, + ZEND_ENUM_StreamErrorCode_NotReadable = 13, + ZEND_ENUM_StreamErrorCode_Disabled = 14, + ZEND_ENUM_StreamErrorCode_NotFound = 15, + ZEND_ENUM_StreamErrorCode_PermissionDenied = 16, + ZEND_ENUM_StreamErrorCode_AlreadyExists = 17, + ZEND_ENUM_StreamErrorCode_InvalidPath = 18, + ZEND_ENUM_StreamErrorCode_PathTooLong = 19, + ZEND_ENUM_StreamErrorCode_OpenFailed = 20, + ZEND_ENUM_StreamErrorCode_CreateFailed = 21, + ZEND_ENUM_StreamErrorCode_DupFailed = 22, + ZEND_ENUM_StreamErrorCode_UnlinkFailed = 23, + ZEND_ENUM_StreamErrorCode_RenameFailed = 24, + ZEND_ENUM_StreamErrorCode_MkdirFailed = 25, + ZEND_ENUM_StreamErrorCode_RmdirFailed = 26, + ZEND_ENUM_StreamErrorCode_StatFailed = 27, + ZEND_ENUM_StreamErrorCode_MetaFailed = 28, + ZEND_ENUM_StreamErrorCode_ChmodFailed = 29, + ZEND_ENUM_StreamErrorCode_ChownFailed = 30, + ZEND_ENUM_StreamErrorCode_CopyFailed = 31, + ZEND_ENUM_StreamErrorCode_TouchFailed = 32, + ZEND_ENUM_StreamErrorCode_InvalidMode = 33, + ZEND_ENUM_StreamErrorCode_InvalidMeta = 34, + ZEND_ENUM_StreamErrorCode_ModeNotSupported = 35, + ZEND_ENUM_StreamErrorCode_Readonly = 36, + ZEND_ENUM_StreamErrorCode_RecursionDetected = 37, + ZEND_ENUM_StreamErrorCode_NotImplemented = 38, + ZEND_ENUM_StreamErrorCode_NoOpener = 39, + ZEND_ENUM_StreamErrorCode_PersistentNotSupported = 40, + ZEND_ENUM_StreamErrorCode_WrapperNotFound = 41, + ZEND_ENUM_StreamErrorCode_WrapperDisabled = 42, + ZEND_ENUM_StreamErrorCode_ProtocolUnsupported = 43, + ZEND_ENUM_StreamErrorCode_WrapperRegistrationFailed = 44, + ZEND_ENUM_StreamErrorCode_WrapperUnregistrationFailed = 45, + ZEND_ENUM_StreamErrorCode_WrapperRestorationFailed = 46, + ZEND_ENUM_StreamErrorCode_FilterNotFound = 47, + ZEND_ENUM_StreamErrorCode_FilterFailed = 48, + ZEND_ENUM_StreamErrorCode_CastFailed = 49, + ZEND_ENUM_StreamErrorCode_CastNotSupported = 50, + ZEND_ENUM_StreamErrorCode_MakeSeekableFailed = 51, + ZEND_ENUM_StreamErrorCode_BufferedDataLost = 52, + ZEND_ENUM_StreamErrorCode_NetworkSendFailed = 53, + ZEND_ENUM_StreamErrorCode_NetworkRecvFailed = 54, + ZEND_ENUM_StreamErrorCode_SslNotSupported = 55, + ZEND_ENUM_StreamErrorCode_ResumptionFailed = 56, + ZEND_ENUM_StreamErrorCode_SocketPathTooLong = 57, + ZEND_ENUM_StreamErrorCode_OobNotSupported = 58, + ZEND_ENUM_StreamErrorCode_ProtocolError = 59, + ZEND_ENUM_StreamErrorCode_InvalidUrl = 60, + ZEND_ENUM_StreamErrorCode_InvalidResponse = 61, + ZEND_ENUM_StreamErrorCode_InvalidHeader = 62, + ZEND_ENUM_StreamErrorCode_InvalidParam = 63, + ZEND_ENUM_StreamErrorCode_RedirectLimit = 64, + ZEND_ENUM_StreamErrorCode_AuthFailed = 65, + ZEND_ENUM_StreamErrorCode_ArchivingFailed = 66, + ZEND_ENUM_StreamErrorCode_EncodingFailed = 67, + ZEND_ENUM_StreamErrorCode_DecodingFailed = 68, + ZEND_ENUM_StreamErrorCode_InvalidFormat = 69, + ZEND_ENUM_StreamErrorCode_AllocationFailed = 70, + ZEND_ENUM_StreamErrorCode_TemporaryFileFailed = 71, + ZEND_ENUM_StreamErrorCode_LockFailed = 72, + ZEND_ENUM_StreamErrorCode_LockNotSupported = 73, + ZEND_ENUM_StreamErrorCode_UserspaceNotImplemented = 74, + ZEND_ENUM_StreamErrorCode_UserspaceInvalidReturn = 75, + ZEND_ENUM_StreamErrorCode_UserspaceCallFailed = 76, +} zend_enum_StreamErrorCode; + +#define ZEND_ENUM_StreamErrorCode_CASE_COUNT 76 + +#ifdef ZEND_ENUM_StreamErrorCode_USE_NAME_TABLE +static const char *zend_enum_StreamErrorCode_case_names[ZEND_ENUM_StreamErrorCode_CASE_COUNT + 1] = { + [ZEND_ENUM_StreamErrorCode_None] = "None", + [ZEND_ENUM_StreamErrorCode_Generic] = "Generic", + [ZEND_ENUM_StreamErrorCode_ReadFailed] = "ReadFailed", + [ZEND_ENUM_StreamErrorCode_WriteFailed] = "WriteFailed", + [ZEND_ENUM_StreamErrorCode_SeekFailed] = "SeekFailed", + [ZEND_ENUM_StreamErrorCode_SeekNotSupported] = "SeekNotSupported", + [ZEND_ENUM_StreamErrorCode_FlushFailed] = "FlushFailed", + [ZEND_ENUM_StreamErrorCode_TruncateFailed] = "TruncateFailed", + [ZEND_ENUM_StreamErrorCode_ConnectFailed] = "ConnectFailed", + [ZEND_ENUM_StreamErrorCode_BindFailed] = "BindFailed", + [ZEND_ENUM_StreamErrorCode_ListenFailed] = "ListenFailed", + [ZEND_ENUM_StreamErrorCode_NotWritable] = "NotWritable", + [ZEND_ENUM_StreamErrorCode_NotReadable] = "NotReadable", + [ZEND_ENUM_StreamErrorCode_Disabled] = "Disabled", + [ZEND_ENUM_StreamErrorCode_NotFound] = "NotFound", + [ZEND_ENUM_StreamErrorCode_PermissionDenied] = "PermissionDenied", + [ZEND_ENUM_StreamErrorCode_AlreadyExists] = "AlreadyExists", + [ZEND_ENUM_StreamErrorCode_InvalidPath] = "InvalidPath", + [ZEND_ENUM_StreamErrorCode_PathTooLong] = "PathTooLong", + [ZEND_ENUM_StreamErrorCode_OpenFailed] = "OpenFailed", + [ZEND_ENUM_StreamErrorCode_CreateFailed] = "CreateFailed", + [ZEND_ENUM_StreamErrorCode_DupFailed] = "DupFailed", + [ZEND_ENUM_StreamErrorCode_UnlinkFailed] = "UnlinkFailed", + [ZEND_ENUM_StreamErrorCode_RenameFailed] = "RenameFailed", + [ZEND_ENUM_StreamErrorCode_MkdirFailed] = "MkdirFailed", + [ZEND_ENUM_StreamErrorCode_RmdirFailed] = "RmdirFailed", + [ZEND_ENUM_StreamErrorCode_StatFailed] = "StatFailed", + [ZEND_ENUM_StreamErrorCode_MetaFailed] = "MetaFailed", + [ZEND_ENUM_StreamErrorCode_ChmodFailed] = "ChmodFailed", + [ZEND_ENUM_StreamErrorCode_ChownFailed] = "ChownFailed", + [ZEND_ENUM_StreamErrorCode_CopyFailed] = "CopyFailed", + [ZEND_ENUM_StreamErrorCode_TouchFailed] = "TouchFailed", + [ZEND_ENUM_StreamErrorCode_InvalidMode] = "InvalidMode", + [ZEND_ENUM_StreamErrorCode_InvalidMeta] = "InvalidMeta", + [ZEND_ENUM_StreamErrorCode_ModeNotSupported] = "ModeNotSupported", + [ZEND_ENUM_StreamErrorCode_Readonly] = "Readonly", + [ZEND_ENUM_StreamErrorCode_RecursionDetected] = "RecursionDetected", + [ZEND_ENUM_StreamErrorCode_NotImplemented] = "NotImplemented", + [ZEND_ENUM_StreamErrorCode_NoOpener] = "NoOpener", + [ZEND_ENUM_StreamErrorCode_PersistentNotSupported] = "PersistentNotSupported", + [ZEND_ENUM_StreamErrorCode_WrapperNotFound] = "WrapperNotFound", + [ZEND_ENUM_StreamErrorCode_WrapperDisabled] = "WrapperDisabled", + [ZEND_ENUM_StreamErrorCode_ProtocolUnsupported] = "ProtocolUnsupported", + [ZEND_ENUM_StreamErrorCode_WrapperRegistrationFailed] = "WrapperRegistrationFailed", + [ZEND_ENUM_StreamErrorCode_WrapperUnregistrationFailed] = "WrapperUnregistrationFailed", + [ZEND_ENUM_StreamErrorCode_WrapperRestorationFailed] = "WrapperRestorationFailed", + [ZEND_ENUM_StreamErrorCode_FilterNotFound] = "FilterNotFound", + [ZEND_ENUM_StreamErrorCode_FilterFailed] = "FilterFailed", + [ZEND_ENUM_StreamErrorCode_CastFailed] = "CastFailed", + [ZEND_ENUM_StreamErrorCode_CastNotSupported] = "CastNotSupported", + [ZEND_ENUM_StreamErrorCode_MakeSeekableFailed] = "MakeSeekableFailed", + [ZEND_ENUM_StreamErrorCode_BufferedDataLost] = "BufferedDataLost", + [ZEND_ENUM_StreamErrorCode_NetworkSendFailed] = "NetworkSendFailed", + [ZEND_ENUM_StreamErrorCode_NetworkRecvFailed] = "NetworkRecvFailed", + [ZEND_ENUM_StreamErrorCode_SslNotSupported] = "SslNotSupported", + [ZEND_ENUM_StreamErrorCode_ResumptionFailed] = "ResumptionFailed", + [ZEND_ENUM_StreamErrorCode_SocketPathTooLong] = "SocketPathTooLong", + [ZEND_ENUM_StreamErrorCode_OobNotSupported] = "OobNotSupported", + [ZEND_ENUM_StreamErrorCode_ProtocolError] = "ProtocolError", + [ZEND_ENUM_StreamErrorCode_InvalidUrl] = "InvalidUrl", + [ZEND_ENUM_StreamErrorCode_InvalidResponse] = "InvalidResponse", + [ZEND_ENUM_StreamErrorCode_InvalidHeader] = "InvalidHeader", + [ZEND_ENUM_StreamErrorCode_InvalidParam] = "InvalidParam", + [ZEND_ENUM_StreamErrorCode_RedirectLimit] = "RedirectLimit", + [ZEND_ENUM_StreamErrorCode_AuthFailed] = "AuthFailed", + [ZEND_ENUM_StreamErrorCode_ArchivingFailed] = "ArchivingFailed", + [ZEND_ENUM_StreamErrorCode_EncodingFailed] = "EncodingFailed", + [ZEND_ENUM_StreamErrorCode_DecodingFailed] = "DecodingFailed", + [ZEND_ENUM_StreamErrorCode_InvalidFormat] = "InvalidFormat", + [ZEND_ENUM_StreamErrorCode_AllocationFailed] = "AllocationFailed", + [ZEND_ENUM_StreamErrorCode_TemporaryFileFailed] = "TemporaryFileFailed", + [ZEND_ENUM_StreamErrorCode_LockFailed] = "LockFailed", + [ZEND_ENUM_StreamErrorCode_LockNotSupported] = "LockNotSupported", + [ZEND_ENUM_StreamErrorCode_UserspaceNotImplemented] = "UserspaceNotImplemented", + [ZEND_ENUM_StreamErrorCode_UserspaceInvalidReturn] = "UserspaceInvalidReturn", + [ZEND_ENUM_StreamErrorCode_UserspaceCallFailed] = "UserspaceCallFailed", +}; +#endif + +typedef enum zend_enum_StreamErrorMode { + ZEND_ENUM_StreamErrorMode_Error = 1, + ZEND_ENUM_StreamErrorMode_Exception = 2, + ZEND_ENUM_StreamErrorMode_Silent = 3, +} zend_enum_StreamErrorMode; + +typedef enum zend_enum_StreamErrorStore { + ZEND_ENUM_StreamErrorStore_Auto = 1, + ZEND_ENUM_StreamErrorStore_None = 2, + ZEND_ENUM_StreamErrorStore_NonTerminating = 3, + ZEND_ENUM_StreamErrorStore_Terminating = 4, + ZEND_ENUM_StreamErrorStore_All = 5, +} zend_enum_StreamErrorStore; + +#endif /* ZEND_STREAM_ERRORS_DECL_65d3a47368993358ac8a4a41bc93dbff5677d3f5_H */ diff --git a/main/streams/streams.c b/main/streams/streams.c index 32c7ba99f58c2..56eb749419d97 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -140,141 +140,6 @@ PHPAPI int php_stream_from_persistent_id(const char *persistent_id, php_stream * return PHP_STREAM_PERSISTENT_NOT_EXIST; } -/* }}} */ - -static zend_llist *php_get_wrapper_errors_list(php_stream_wrapper *wrapper) -{ - if (!FG(wrapper_errors)) { - return NULL; - } else { - return (zend_llist*) zend_hash_str_find_ptr(FG(wrapper_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_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; - const char **err_buf_p; - zend_llist_position pos; - - if (PG(html_errors)) { - brlen = 7; - br = "
\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 */ @@ -513,6 +378,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; @@ -1320,7 +1190,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, NotWritable, "Stream is not writable"); return (ssize_t) -1; } @@ -1454,7 +1324,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, SeekNotSupported, + "Stream does not support seeking"); return -1; } @@ -1878,11 +1749,13 @@ 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; } + + php_stream_error_state_cleanup(); } zend_result php_init_stream_wrappers(int module_number) @@ -2049,7 +1922,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, + ProtocolUnsupported, + "Remote host file access not supported, %s", path); } return NULL; } @@ -2088,7 +1963,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, + Disabled, + "file:// wrapper is disabled in the server configuration"); } return NULL; } @@ -2186,10 +2063,12 @@ 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, + NoOpener, "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, PHP_STREAM_EC(OpenFailed), + path, "Failed to open directory"); } php_stream_tidy_wrapper_error_log(wrapper); @@ -2256,16 +2135,25 @@ 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, + ProtocolUnsupported, + "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); } return NULL; } + /* wrapper name needs to be stored as wrapper can be removed in opener (user stream) */ + char *wrapper_name = pestrdup(PHP_STREAM_ERROR_WRAPPER_NAME(wrapper), persistent); 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, + NoOpener, "wrapper does not support stream open"); } else { stream = wrapper->wops->stream_opener(wrapper, @@ -2276,7 +2164,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, + PersistentNotSupported, "wrapper does not support persistent streams"); php_stream_close(stream); stream = NULL; @@ -2300,6 +2189,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)) { @@ -2312,6 +2204,7 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod if (resolved_path) { zend_string_release_ex(resolved_path, 0); } + pefree(wrapper_name, persistent); return stream; case PHP_STREAM_RELEASED: if (newstream->orig_path) { @@ -2321,6 +2214,7 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod if (resolved_path) { zend_string_release_ex(resolved_path, 0); } + pefree(wrapper_name, persistent); return newstream; default: php_stream_close(stream); @@ -2328,8 +2222,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, + SeekNotSupported, tmp, + "could not make seekable - %s", tmp); efree(tmp); options &= ~REPORT_ERRORS; @@ -2347,13 +2242,15 @@ 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_name_errors(wrapper_name, context, PHP_STREAM_EC(OpenFailed), + path, "Failed to open stream"); if (opened_path && *opened_path) { zend_string_release_ex(*opened_path, 0); *opened_path = NULL; } } - php_stream_tidy_wrapper_error_log(wrapper); + php_stream_tidy_wrapper_name_error_log(wrapper_name); + pefree(wrapper_name, persistent); if (resolved_path) { zend_string_release_ex(resolved_path, 0); } diff --git a/main/streams/transports.c b/main/streams/transports.c index 83297d9a06ceb..64f52e31d13de 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(WrapperNotFound, 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, + WrapperNotFound, "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(ConnectFailed, 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(BindFailed, 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(ListenFailed, 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", SslNotSupported, + "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", SslNotSupported, + "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, FilterFailed, + "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, FilterFailed, + "Cannot write OOB data, or data to a targeted address on a filtered stream"); return -1; } diff --git a/main/streams/userspace.c b/main/streams/userspace.c index f5e25aa96c772..ec116a0839bb0 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, + RecursionDetected, "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,NotImplemented, + "\"%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, + UserspaceCallFailed, + "\"%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, + RecursionDetected, "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, NotImplemented, + "\"%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, + UserspaceCallFailed, + "\"%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, + WrapperRegistrationFailed, + "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, + WrapperRegistrationFailed, + "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, + WrapperUnregistrationFailed, + "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, + WrapperNotFound, + "%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, + WrapperRestorationFailed, + "%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, + WrapperRestorationFailed, + "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, NotImplemented, + "%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, UserspaceInvalidReturn, + "%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, NotImplemented, + "%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, UserspaceInvalidReturn, + "%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, NotImplemented, "%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, NotImplemented, + "%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, NotImplemented, + "%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,7 @@ 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, NotImplemented, "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; @@ -877,15 +902,15 @@ 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, UserspaceInvalidReturn, + "%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 +945,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, NotImplemented, + "%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 +958,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, UserspaceInvalidReturn, + // "%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 +994,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, NotImplemented, + "%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 +1004,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, UserspaceInvalidReturn, + "%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 +1038,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, NotImplemented, "%s::" USERSTREAM_SET_OPTION " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; @@ -1038,19 +1063,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 +1108,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, NotImplemented, + "%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 +1147,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, NotImplemented, + "%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 +1186,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, NotImplemented, + "%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 +1224,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, NotImplemented, + "%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 +1264,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, + InvalidMeta, + "Unknown option %d for " USERSTREAM_METADATA, option); return ret; } @@ -1258,7 +1289,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, NotImplemented, + "%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 +1328,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, NotImplemented, + "%s::" USERSTREAM_STATURL " is not implemented!", ZSTR_VAL(uwrap->ce->name)); return -1; } if (UNEXPECTED(Z_ISUNDEF(zretval))) { @@ -1332,8 +1364,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, NotImplemented, + "%s::" USERSTREAM_DIR_READ " is not implemented!", + ZSTR_VAL(us->wrapper->ce->name)); return -1; } if (UNEXPECTED(Z_ISUNDEF(retval))) { @@ -1418,7 +1451,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, NotImplemented, + "%s::" USERSTREAM_CAST " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); } goto out; @@ -1432,14 +1466,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, UserspaceInvalidReturn, + "%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, UserspaceInvalidReturn, + "%s::" USERSTREAM_CAST " must not return itself", ZSTR_VAL(us->wrapper->ce->name)); } intstream = NULL; diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c index f21944313d32a..540fb9ad3a65a 100644 --- a/main/streams/xp_socket.c +++ b/main/streams/xp_socket.c @@ -116,9 +116,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, NetworkSendFailed, + "Send of %zu bytes failed with errno=%d %s", count, err, estr); efree(estr); } } @@ -454,8 +453,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, NetworkSendFailed, "%s", err); efree(err); } return PHP_STREAM_OPTION_RETURN_OK; @@ -595,7 +593,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; @@ -614,9 +613,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, InvalidPath, + "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); @@ -697,7 +696,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); int result = bind(sock->socket, (const struct sockaddr *)&unix_addr, (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen); @@ -833,7 +832,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, diff --git a/win32/build/config.w32 b/win32/build/config.w32 index aefcfb5f82474..6cd6907f28250 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");