diff --git a/NEWS b/NEWS index 54a45028c6a14..e75b26e7d1436 100644 --- a/NEWS +++ b/NEWS @@ -153,6 +153,8 @@ PHP NEWS . Fixed bug GH-21221 (Prevent closing of innerstream of php://temp stream). (ilutov) . Improved stream_socket_server() bind failure error reporting. (ilutov) + . Fixed bug #49874 (ftell() and fseek() inconsistency when using stream + filters). (Jakub Zelenka) - Zip: . Fixed ZipArchive callback being called after executor has shut down. diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 57bef65c25ee9..1072d822ee492 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -74,6 +74,15 @@ PHP 8.6 INTERNALS UPGRADE NOTES longer is a pointer, but a directly embedded HashTable struct. . Added a C23_ENUM() helper macro to define forward-compatible fixed-size enums. + . Extended php_stream_filter_ops with seek method. + . The INI_STR(), INI_INT(), INI_FLT(), and INI_BOOL() macros have been + removed. Instead new zend_ini_{bool|long|double|str|string}_literal() + macros have been added. This fixes an internal naming inconsistency as + "str" usually means zend_string*, and "string" means char*. + However INI_STR() returned a char* + . The INI_ORIG_{INT|STR|FLT|BOOL}() macros have been removed as they are + unused. If this behaviour is required fall back to the zend_ini_* + functions. ======================== 2. Build system changes diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index d571a622e476b..248b4f401a71a 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -572,9 +572,9 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer) zend_fiber *fiber = EG(active_fiber); /* Determine the current error_reporting ini setting. */ - zend_long error_reporting = INI_INT("error_reporting"); - /* If error_reporting is 0 and not explicitly set to 0, INI_STR returns a null pointer. */ - if (!error_reporting && !INI_STR("error_reporting")) { + zend_long error_reporting = zend_ini_long_literal("error_reporting"); + /* If error_reporting is 0 and not explicitly set to 0, zend_ini_str returns a null pointer. */ + if (!error_reporting && !zend_ini_str_literal("error_reporting")) { error_reporting = E_ALL; } diff --git a/Zend/zend_highlight.c b/Zend/zend_highlight.c index 5c3cd136d5807..c5fc874b9cb5f 100644 --- a/Zend/zend_highlight.c +++ b/Zend/zend_highlight.c @@ -79,8 +79,8 @@ ZEND_API void zend_highlight(zend_syntax_highlighter_ini *syntax_highlighter_ini { zval token; int token_type; - char *last_color = syntax_highlighter_ini->highlight_html; - char *next_color; + const char *last_color = syntax_highlighter_ini->highlight_html; + const char *next_color; zend_printf("
", last_color);
/* highlight stuff coming back from zendlex() */
diff --git a/Zend/zend_highlight.h b/Zend/zend_highlight.h
index 04688d65132b2..adc1d3c8c81e0 100644
--- a/Zend/zend_highlight.h
+++ b/Zend/zend_highlight.h
@@ -30,11 +30,11 @@
typedef struct _zend_syntax_highlighter_ini {
- char *highlight_html;
- char *highlight_comment;
- char *highlight_default;
- char *highlight_string;
- char *highlight_keyword;
+ const char *highlight_html;
+ const char *highlight_comment;
+ const char *highlight_default;
+ const char *highlight_string;
+ const char *highlight_keyword;
} zend_syntax_highlighter_ini;
diff --git a/Zend/zend_ini.c b/Zend/zend_ini.c
index d8f1d4f50f95d..85739415feb32 100644
--- a/Zend/zend_ini.c
+++ b/Zend/zend_ini.c
@@ -491,7 +491,7 @@ ZEND_API double zend_ini_double(const char *name, size_t name_length, bool orig)
}
/* }}} */
-ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, bool orig, bool *exists) /* {{{ */
+ZEND_API const char *zend_ini_string_ex(const char *name, size_t name_length, bool orig, bool *exists) /* {{{ */
{
zend_string *str = zend_ini_str_ex(name, name_length, orig, exists);
@@ -499,7 +499,7 @@ ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, bool ori
}
/* }}} */
-ZEND_API char *zend_ini_string(const char *name, size_t name_length, bool orig) /* {{{ */
+ZEND_API const char *zend_ini_string(const char *name, size_t name_length, bool orig) /* {{{ */
{
zend_string *str = zend_ini_str(name, name_length, orig);
diff --git a/Zend/zend_ini.h b/Zend/zend_ini.h
index d8d7f599f461e..d2419bb160fda 100644
--- a/Zend/zend_ini.h
+++ b/Zend/zend_ini.h
@@ -88,13 +88,19 @@ ZEND_API void display_ini_entries(zend_module_entry *module);
ZEND_API zend_long zend_ini_long(const char *name, size_t name_length, bool orig);
ZEND_API double zend_ini_double(const char *name, size_t name_length, bool orig);
-ZEND_API char *zend_ini_string(const char *name, size_t name_length, bool orig);
-ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, bool orig, bool *exists);
+ZEND_API const char *zend_ini_string(const char *name, size_t name_length, bool orig);
+ZEND_API const char *zend_ini_string_ex(const char *name, size_t name_length, bool orig, bool *exists);
ZEND_API zend_string *zend_ini_str(const char *name, size_t name_length, bool orig);
ZEND_API zend_string *zend_ini_str_ex(const char *name, size_t name_length, bool orig, bool *exists);
ZEND_API zend_string *zend_ini_get_value(zend_string *name);
ZEND_API bool zend_ini_parse_bool(const zend_string *str);
+#define zend_ini_bool_literal(name) zend_ini_parse_bool(zend_ini_str((name), sizeof("" name) - 1, false))
+#define zend_ini_long_literal(name) zend_ini_long((name), sizeof("" name) - 1, false)
+#define zend_ini_double_literal(name) zend_ini_double((name), sizeof("" name) - 1, false)
+#define zend_ini_str_literal(name) zend_ini_str((name), sizeof("" name) - 1, false)
+#define zend_ini_string_literal(name) zend_ini_string((name), sizeof("" name) - 1, false)
+
/**
* Parses an ini quantity
*
@@ -191,16 +197,6 @@ END_EXTERN_C()
ZEND_INI_ENTRY3_EX(name, default_value, modifiable, on_modify, (void *) XtOffsetOf(struct_type, property_name), (void *) &struct_ptr, NULL, zend_ini_boolean_displayer_cb)
#endif
-#define INI_INT(name) zend_ini_long((name), strlen(name), 0)
-#define INI_FLT(name) zend_ini_double((name), strlen(name), 0)
-#define INI_STR(name) zend_ini_string_ex((name), strlen(name), 0, NULL)
-#define INI_BOOL(name) ((bool) INI_INT(name))
-
-#define INI_ORIG_INT(name) zend_ini_long((name), strlen(name), 1)
-#define INI_ORIG_FLT(name) zend_ini_double((name), strlen(name), 1)
-#define INI_ORIG_STR(name) zend_ini_string((name), strlen(name), 1)
-#define INI_ORIG_BOOL(name) ((bool) INI_ORIG_INT(name))
-
#define REGISTER_INI_ENTRIES() zend_register_ini_entries_ex(ini_entries, module_number, type)
#define UNREGISTER_INI_ENTRIES() zend_unregister_ini_entries_ex(module_number, type)
#define DISPLAY_INI_ENTRIES() display_ini_entries(zend_module)
diff --git a/Zend/zend_multibyte.c b/Zend/zend_multibyte.c
index f61ed79fd1f7a..fc130162f0837 100644
--- a/Zend/zend_multibyte.c
+++ b/Zend/zend_multibyte.c
@@ -114,8 +114,8 @@ ZEND_API zend_result zend_multibyte_set_functions(const zend_multibyte_functions
* populated, we need to reinitialize script_encoding here.
*/
{
- const char *value = zend_ini_string("zend.script_encoding", sizeof("zend.script_encoding") - 1, 0);
- zend_multibyte_set_script_encoding_by_string(value, strlen(value));
+ const zend_string *value = zend_ini_str_literal("zend.script_encoding");
+ zend_multibyte_set_script_encoding_by_string(ZSTR_VAL(value), ZSTR_LEN(value));
}
return SUCCESS;
}
diff --git a/ext/bz2/bz2_filter.c b/ext/bz2/bz2_filter.c
index 27059391dacb9..69ee483d21f12 100644
--- a/ext/bz2/bz2_filter.c
+++ b/ext/bz2/bz2_filter.c
@@ -42,6 +42,10 @@ typedef struct _php_bz2_filter_data {
unsigned int is_flushed : 1; /* only for compression */
int persistent;
+
+ /* Configuration for reset - immutable */
+ int blockSize100k; /* compress only */
+ int workFactor; /* compress only */
} php_bz2_filter_data;
/* }}} */
@@ -178,6 +182,36 @@ static php_stream_filter_status_t php_bz2_decompress_filter(
return exit_status;
}
+static zend_result php_bz2_decompress_seek(
+ php_stream *stream,
+ php_stream_filter *thisfilter,
+ zend_off_t offset,
+ int whence
+ )
+{
+ if (!Z_PTR(thisfilter->abstract)) {
+ return FAILURE;
+ }
+
+ php_bz2_filter_data *data = Z_PTR(thisfilter->abstract);
+
+ /* End current decompression if running */
+ if (data->status == PHP_BZ2_RUNNING) {
+ BZ2_bzDecompressEnd(&(data->strm));
+ }
+
+ /* Reset stream state */
+ data->strm.next_in = data->inbuf;
+ data->strm.avail_in = 0;
+ data->strm.next_out = data->outbuf;
+ data->strm.avail_out = data->outbuf_len;
+ data->status = PHP_BZ2_UNINITIALIZED;
+
+ /* Note: We don't reinitialize here - it will be done on first use in the filter function */
+
+ return SUCCESS;
+}
+
static void php_bz2_decompress_dtor(php_stream_filter *thisfilter)
{
if (thisfilter && Z_PTR(thisfilter->abstract)) {
@@ -193,6 +227,7 @@ static void php_bz2_decompress_dtor(php_stream_filter *thisfilter)
static const php_stream_filter_ops php_bz2_decompress_ops = {
php_bz2_decompress_filter,
+ php_bz2_decompress_seek,
php_bz2_decompress_dtor,
"bzip2.decompress"
};
@@ -288,6 +323,41 @@ static php_stream_filter_status_t php_bz2_compress_filter(
return exit_status;
}
+static zend_result php_bz2_compress_seek(
+ php_stream *stream,
+ php_stream_filter *thisfilter,
+ zend_off_t offset,
+ int whence
+ )
+{
+ int status;
+
+ if (!Z_PTR(thisfilter->abstract)) {
+ return FAILURE;
+ }
+
+ php_bz2_filter_data *data = Z_PTR(thisfilter->abstract);
+
+ /* End current compression */
+ BZ2_bzCompressEnd(&(data->strm));
+
+ /* Reset stream state */
+ data->strm.next_in = data->inbuf;
+ data->strm.avail_in = 0;
+ data->strm.next_out = data->outbuf;
+ data->strm.avail_out = data->outbuf_len;
+ data->is_flushed = 1;
+
+ /* Reinitialize compression with saved configuration */
+ status = BZ2_bzCompressInit(&(data->strm), data->blockSize100k, 0, data->workFactor);
+ if (status != BZ_OK) {
+ php_error_docref(NULL, E_WARNING, "bzip2.compress: failed to reset compression state");
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
+
static void php_bz2_compress_dtor(php_stream_filter *thisfilter)
{
if (Z_PTR(thisfilter->abstract)) {
@@ -301,6 +371,7 @@ static void php_bz2_compress_dtor(php_stream_filter *thisfilter)
static const php_stream_filter_ops php_bz2_compress_ops = {
php_bz2_compress_filter,
+ php_bz2_compress_seek,
php_bz2_compress_dtor,
"bzip2.compress"
};
@@ -309,7 +380,7 @@ static const php_stream_filter_ops php_bz2_compress_ops = {
/* {{{ bzip2.* common factory */
-static php_stream_filter *php_bz2_filter_create(const char *filtername, zval *filterparams, uint8_t persistent)
+static php_stream_filter *php_bz2_filter_create(const char *filtername, zval *filterparams, bool persistent)
{
const php_stream_filter_ops *fops = NULL;
php_bz2_filter_data *data;
@@ -388,6 +459,10 @@ static php_stream_filter *php_bz2_filter_create(const char *filtername, zval *fi
}
}
+ /* Save configuration for reset */
+ data->blockSize100k = blockSize100k;
+ data->workFactor = workFactor;
+
status = BZ2_bzCompressInit(&(data->strm), blockSize100k, 0, workFactor);
data->is_flushed = 1;
fops = &php_bz2_compress_ops;
@@ -403,7 +478,7 @@ static php_stream_filter *php_bz2_filter_create(const char *filtername, zval *fi
return NULL;
}
- return php_stream_filter_alloc(fops, data, persistent);
+ return php_stream_filter_alloc(fops, data, persistent, PSFS_SEEKABLE_START);
}
const php_stream_filter_factory php_bz2_filter_factory = {
diff --git a/ext/bz2/tests/bz2_filter_seek_compress.phpt b/ext/bz2/tests/bz2_filter_seek_compress.phpt
new file mode 100644
index 0000000000000..0656b244484d9
--- /dev/null
+++ b/ext/bz2/tests/bz2_filter_seek_compress.phpt
@@ -0,0 +1,55 @@
+--TEST--
+bzip2.compress filter with seek to start
+--EXTENSIONS--
+bz2
+--FILE--
+ $size1 ? "YES" : "NO") . "\n";
+
+$result = fseek($fp, 50, SEEK_SET);
+echo "Seek to middle: " . ($result === 0 ? "SUCCESS" : "FAILURE") . "\n";
+
+fclose($fp);
+
+$fp = fopen($file, 'r');
+stream_filter_append($fp, 'bzip2.decompress', STREAM_FILTER_READ);
+$content = stream_get_contents($fp);
+fclose($fp);
+
+echo "Decompressed content matches text2: " . ($content === $text2 ? "YES" : "NO") . "\n";
+?>
+--CLEAN--
+
+--EXPECTF--
+Size after first write: 40
+Seek to start: SUCCESS
+Size after second write: 98
+Second write is larger: YES
+
+Warning: fseek(): Stream filter bzip2.compress is seekable only to start position in %s on line %d
+Seek to middle: FAILURE
+Decompressed content matches text2: YES
diff --git a/ext/bz2/tests/bz2_filter_seek_decompress.phpt b/ext/bz2/tests/bz2_filter_seek_decompress.phpt
new file mode 100644
index 0000000000000..8ccd224b1a406
--- /dev/null
+++ b/ext/bz2/tests/bz2_filter_seek_decompress.phpt
@@ -0,0 +1,43 @@
+--TEST--
+bzip2.decompress filter with seek to start
+--EXTENSIONS--
+bz2
+--FILE--
+
+--CLEAN--
+
+--EXPECTF--
+First read (20 bytes): I am the very model
+Seek to start: SUCCESS
+Content after seek matches: YES
+
+Warning: fseek(): Stream filter bzip2.decompress is seekable only to start position in %s on line %d
+Seek to middle: FAILURE
diff --git a/ext/com_dotnet/com_dotnet.c b/ext/com_dotnet/com_dotnet.c
index f8b4a828e154a..e4ab984858312 100644
--- a/ext/com_dotnet/com_dotnet.c
+++ b/ext/com_dotnet/com_dotnet.c
@@ -127,7 +127,6 @@ static HRESULT dotnet_bind_runtime(LPVOID FAR *ppv)
typedef HRESULT (STDAPICALLTYPE *cbtr_t)(LPCWSTR pwszVersion, LPCWSTR pwszBuildFlavor, REFCLSID rclsid, REFIID riid, LPVOID FAR *ppv);
cbtr_t CorBindToRuntime;
OLECHAR *oleversion;
- char *version;
mscoree = LoadLibraryA("mscoree.dll");
if (mscoree == NULL) {
@@ -140,11 +139,11 @@ static HRESULT dotnet_bind_runtime(LPVOID FAR *ppv)
return S_FALSE;
}
- version = INI_STR("com.dotnet_version");
- if (version == NULL || *version == '\0') {
+ const zend_string *version = zend_ini_str_literal("com.dotnet_version");
+ if (version == NULL || ZSTR_LEN(version) == 0) {
oleversion = NULL;
} else {
- oleversion = php_com_string_to_olestring(version, strlen(version), COMG(code_page));
+ oleversion = php_com_string_to_olestring(ZSTR_VAL(version), ZSTR_LEN(version), COMG(code_page));
}
hr = CorBindToRuntime(oleversion, NULL, &CLSID_CorRuntimeHost, &IID_ICorRuntimeHost, ppv);
diff --git a/ext/curl/interface.c b/ext/curl/interface.c
index 6328538241fe6..57bc2f1ede3b9 100644
--- a/ext/curl/interface.c
+++ b/ext/curl/interface.c
@@ -1100,8 +1100,6 @@ static void create_certinfo(struct curl_certinfo *ci, zval *listcode)
Set default options for a handle */
static void _php_curl_set_default_options(php_curl *ch)
{
- char *cainfo;
-
curl_easy_setopt(ch->cp, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0L);
curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str);
@@ -1114,9 +1112,9 @@ static void _php_curl_set_default_options(php_curl *ch)
curl_easy_setopt(ch->cp, CURLOPT_DNS_CACHE_TIMEOUT, 120L);
curl_easy_setopt(ch->cp, CURLOPT_MAXREDIRS, 20L); /* prevent infinite redirects */
- cainfo = INI_STR("openssl.cafile");
+ const char *cainfo = zend_ini_string_literal("openssl.cafile");
if (!(cainfo && cainfo[0] != '\0')) {
- cainfo = INI_STR("curl.cainfo");
+ cainfo = zend_ini_string_literal("curl.cainfo");
}
if (cainfo && cainfo[0] != '\0') {
curl_easy_setopt(ch->cp, CURLOPT_CAINFO, cainfo);
diff --git a/ext/date/php_date.c b/ext/date/php_date.c
index dd80e15e9cfbe..e5b094acdb3cd 100644
--- a/ext/date/php_date.c
+++ b/ext/date/php_date.c
@@ -5478,18 +5478,18 @@ static void php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAMETERS, bool calc_s
ZEND_PARSE_PARAMETERS_END();
if (latitude_is_null) {
- latitude = INI_FLT("date.default_latitude");
+ latitude = zend_ini_double_literal("date.default_latitude");
}
if (longitude_is_null) {
- longitude = INI_FLT("date.default_longitude");
+ longitude = zend_ini_double_literal("date.default_longitude");
}
if (zenith_is_null) {
if (calc_sunset) {
- zenith = INI_FLT("date.sunset_zenith");
+ zenith = zend_ini_double_literal("date.sunset_zenith");
} else {
- zenith = INI_FLT("date.sunrise_zenith");
+ zenith = zend_ini_double_literal("date.sunrise_zenith");
}
}
diff --git a/ext/gd/gd.c b/ext/gd/gd.c
index 33b397b4f699b..1bd1a26f05199 100644
--- a/ext/gd/gd.c
+++ b/ext/gd/gd.c
@@ -1595,7 +1595,7 @@ static void _php_image_create_from(INTERNAL_FUNCTION_PARAMETERS, int image_type,
#ifdef HAVE_GD_JPG
case PHP_GDIMG_TYPE_JPG:
- ignore_warning = INI_INT("gd.jpeg_ignore_warning");
+ ignore_warning = zend_ini_bool_literal("gd.jpeg_ignore_warning");
im = gdImageCreateFromJpegEx(fp, ignore_warning);
break;
#endif
diff --git a/ext/gd/tests/bug79945.phpt b/ext/gd/tests/bug79945.phpt
index 5db958e36d358..46dac358d8e21 100644
--- a/ext/gd/tests/bug79945.phpt
+++ b/ext/gd/tests/bug79945.phpt
@@ -9,17 +9,17 @@ if (!(imagetypes() & IMG_PNG)) {
}
set_error_handler(function($errno, $errstr) {
if (str_contains($errstr, 'Cannot cast a filtered stream on this system')) {
- die('skip: fopencookie not support on this system');
+ die('skip: fopencookie not supported on this system');
}
});
-imagecreatefrompng('php://filter/read=convert.base64-encode/resource=' . __DIR__ . '/test.png');
+imagecreatefrompng('php://filter/read=string.rot13/resource=' . __DIR__ . '/test.png');
restore_error_handler();
?>
--FILE--
--CLEAN--
--EXPECTF--
-Warning: imagecreatefrompng(): "php://filter/read=convert.base64-encode/resource=%s" is not a valid PNG file in %s on line %d
+Warning: imagecreatefrompng(): "php://filter/read=string.rot13/resource=%s" is not a valid PNG file in %s on line %d
diff --git a/ext/iconv/iconv.c b/ext/iconv/iconv.c
index ed7d332397dbc..02cffcb4b859b 100644
--- a/ext/iconv/iconv.c
+++ b/ext/iconv/iconv.c
@@ -2585,6 +2585,33 @@ static php_stream_filter_status_t php_iconv_stream_filter_do_filter(
}
/* }}} */
+/* {{{ php_iconv_stream_filter_seek */
+static zend_result php_iconv_stream_filter_seek(
+ php_stream *stream,
+ php_stream_filter *filter,
+ zend_off_t offset,
+ int whence)
+{
+ php_iconv_stream_filter *self = (php_iconv_stream_filter *)Z_PTR(filter->abstract);
+
+ /* Reset stub buffer */
+ self->stub_len = 0;
+
+ /* Reset iconv conversion state by closing and reopening the converter */
+ iconv_close(self->cd);
+
+ self->cd = iconv_open(self->to_charset, self->from_charset);
+ if ((iconv_t)-1 == self->cd) {
+ php_error_docref(NULL, E_WARNING,
+ "iconv stream filter (\"%s\"=>\"%s\"): failed to reset conversion state",
+ self->from_charset, self->to_charset);
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
/* {{{ php_iconv_stream_filter_cleanup */
static void php_iconv_stream_filter_cleanup(php_stream_filter *filter)
{
@@ -2595,12 +2622,13 @@ static void php_iconv_stream_filter_cleanup(php_stream_filter *filter)
static const php_stream_filter_ops php_iconv_stream_filter_ops = {
php_iconv_stream_filter_do_filter,
+ php_iconv_stream_filter_seek,
php_iconv_stream_filter_cleanup,
"convert.iconv.*"
};
/* {{{ php_iconv_stream_filter_create */
-static php_stream_filter *php_iconv_stream_filter_factory_create(const char *name, zval *params, uint8_t persistent)
+static php_stream_filter *php_iconv_stream_filter_factory_create(const char *name, zval *params, bool persistent)
{
php_iconv_stream_filter *inst;
const char *from_charset = NULL, *to_charset = NULL;
@@ -2632,7 +2660,8 @@ static php_stream_filter *php_iconv_stream_filter_factory_create(const char *nam
return NULL;
}
- return php_stream_filter_alloc(&php_iconv_stream_filter_ops, inst, persistent);
+ return php_stream_filter_alloc(&php_iconv_stream_filter_ops, inst, persistent,
+ PSFS_SEEKABLE_START);
}
/* }}} */
diff --git a/ext/iconv/tests/iconv_stream_filter_seek.phpt b/ext/iconv/tests/iconv_stream_filter_seek.phpt
new file mode 100644
index 0000000000000..97519ec7853d3
--- /dev/null
+++ b/ext/iconv/tests/iconv_stream_filter_seek.phpt
@@ -0,0 +1,43 @@
+--TEST--
+iconv stream filter with seek to start
+--EXTENSIONS--
+iconv
+--FILE--
+
+--CLEAN--
+
+--EXPECTF--
+First read (20 bytes): Hello, this is a tes
+Seek to start: SUCCESS
+Content after seek matches: YES
+
+Warning: fseek(): Stream filter convert.iconv.* is seekable only to start position in %s on line %d
+Seek to middle: FAILURE
diff --git a/ext/openssl/openssl_backend_common.c b/ext/openssl/openssl_backend_common.c
index 6f257af49819e..533d2f2c61f1b 100644
--- a/ext/openssl/openssl_backend_common.c
+++ b/ext/openssl/openssl_backend_common.c
@@ -504,10 +504,8 @@ void php_openssl_set_cert_locations(zval *return_value)
add_assoc_string(return_value, "default_cert_dir_env", (char *) X509_get_default_cert_dir_env());
add_assoc_string(return_value, "default_private_dir", (char *) X509_get_default_private_dir());
add_assoc_string(return_value, "default_default_cert_area", (char *) X509_get_default_cert_area());
- add_assoc_string(return_value, "ini_cafile",
- zend_ini_string("openssl.cafile", sizeof("openssl.cafile")-1, 0));
- add_assoc_string(return_value, "ini_capath",
- zend_ini_string("openssl.capath", sizeof("openssl.capath")-1, 0));
+ add_assoc_str(return_value, "ini_cafile", zend_string_copy(zend_ini_str_literal("openssl.cafile")));
+ add_assoc_str(return_value, "ini_capath", zend_string_copy(zend_ini_str_literal("openssl.capath")));
}
X509 *php_openssl_x509_from_str(
diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c
index 34b4acd9f1c74..8f9395e460223 100644
--- a/ext/openssl/xp_ssl.c
+++ b/ext/openssl/xp_ssl.c
@@ -880,16 +880,16 @@ static long php_openssl_load_stream_cafile(X509_STORE *cert_store, const char *c
static zend_result php_openssl_enable_peer_verification(SSL_CTX *ctx, php_stream *stream) /* {{{ */
{
zval *val = NULL;
- char *cafile = NULL;
- char *capath = NULL;
+ const char *cafile = NULL;
+ const char *capath = NULL;
php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
GET_VER_OPT_STRING("cafile", cafile);
GET_VER_OPT_STRING("capath", capath);
if (cafile == NULL) {
- cafile = zend_ini_string("openssl.cafile", sizeof("openssl.cafile")-1, 0);
- cafile = strlen(cafile) ? cafile : NULL;
+ const zend_string *cafile_str = zend_ini_str_literal("openssl.cafile");
+ cafile = ZSTR_LEN(cafile_str) ? ZSTR_VAL(cafile_str) : NULL;
} else if (!sslsock->is_client) {
/* Servers need to load and assign CA names from the cafile */
STACK_OF(X509_NAME) *cert_names = SSL_load_client_CA_file(cafile);
@@ -902,8 +902,8 @@ static zend_result php_openssl_enable_peer_verification(SSL_CTX *ctx, php_stream
}
if (capath == NULL) {
- capath = zend_ini_string("openssl.capath", sizeof("openssl.capath")-1, 0);
- capath = strlen(capath) ? capath : NULL;
+ const zend_string *capath_str = zend_ini_str_literal("openssl.capath");
+ capath = ZSTR_LEN(capath_str) ? ZSTR_VAL(capath_str) : NULL;
}
if (cafile || capath) {
diff --git a/ext/session/session.c b/ext/session/session.c
index c04e19e25edc7..51ab3922e0bdf 100644
--- a/ext/session/session.c
+++ b/ext/session/session.c
@@ -1654,7 +1654,7 @@ PHPAPI zend_result php_session_start(void)
return FAILURE;
case php_session_disabled: {
- const char *value = zend_ini_string(ZEND_STRL("session.save_handler"), false);
+ const char *value = zend_ini_string_literal("session.save_handler");
if (!PS(mod) && value) {
PS(mod) = _php_find_ps_module(value);
if (!PS(mod)) {
@@ -1662,7 +1662,7 @@ PHPAPI zend_result php_session_start(void)
return FAILURE;
}
}
- value = zend_ini_string(ZEND_STRL("session.serialize_handler"), false);
+ value = zend_ini_string_literal("session.serialize_handler");
if (!PS(serializer) && value) {
PS(serializer) = _php_find_ps_serializer(value);
if (!PS(serializer)) {
@@ -2768,14 +2768,14 @@ static zend_result php_rinit_session(bool auto_start)
PS(mod) = NULL;
{
- const char *value = zend_ini_string(ZEND_STRL("session.save_handler"), false);
+ const char *value = zend_ini_string_literal("session.save_handler");
if (value) {
PS(mod) = _php_find_ps_module(value);
}
}
if (PS(serializer) == NULL) {
- const char *value = zend_ini_string(ZEND_STRL("session.serialize_handler"), false);
+ const char *value = zend_ini_string_literal("session.serialize_handler");
if (value) {
PS(serializer) = _php_find_ps_serializer(value);
}
diff --git a/ext/soap/soap.c b/ext/soap/soap.c
index 178660ec4cd62..59e2bd4168052 100644
--- a/ext/soap/soap.c
+++ b/ext/soap/soap.c
@@ -1695,7 +1695,7 @@ PHP_METHOD(SoapServer, handle)
sapi_add_header("Content-Type: text/xml; charset=utf-8", sizeof("Content-Type: text/xml; charset=utf-8")-1, 1);
}
- if (INI_INT("zlib.output_compression")) {
+ if (zend_ini_long_literal("zlib.output_compression")) {
sapi_add_header("Connection: close", sizeof("Connection: close")-1, 1);
} else {
snprintf(cont_len, sizeof(cont_len), "Content-Length: %d", size);
@@ -1857,7 +1857,7 @@ static void soap_server_fault_ex(sdlFunctionPtr function, zval* fault, soapHeade
if (use_http_error_status) {
sapi_add_header("HTTP/1.1 500 Internal Server Error", sizeof("HTTP/1.1 500 Internal Server Error")-1, 1);
}
- if (INI_INT("zlib.output_compression")) {
+ if (zend_ini_long_literal("zlib.output_compression")) {
sapi_add_header("Connection: close", sizeof("Connection: close")-1, 1);
} else {
snprintf(cont_len, sizeof(cont_len), "Content-Length: %d", size);
diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c
index fbbeeb0b433f4..a7417f2a39036 100644
--- a/ext/standard/basic_functions.c
+++ b/ext/standard/basic_functions.c
@@ -1717,11 +1717,11 @@ PHPAPI bool append_user_shutdown_function(php_shutdown_function_entry *shutdown_
ZEND_API void php_get_highlight_struct(zend_syntax_highlighter_ini *syntax_highlighter_ini) /* {{{ */
{
- syntax_highlighter_ini->highlight_comment = INI_STR("highlight.comment");
- syntax_highlighter_ini->highlight_default = INI_STR("highlight.default");
- syntax_highlighter_ini->highlight_html = INI_STR("highlight.html");
- syntax_highlighter_ini->highlight_keyword = INI_STR("highlight.keyword");
- syntax_highlighter_ini->highlight_string = INI_STR("highlight.string");
+ syntax_highlighter_ini->highlight_comment = zend_ini_string_literal("highlight.comment");
+ syntax_highlighter_ini->highlight_default = zend_ini_string_literal("highlight.default");
+ syntax_highlighter_ini->highlight_html = zend_ini_string_literal("highlight.html");
+ syntax_highlighter_ini->highlight_keyword = zend_ini_string_literal("highlight.keyword");
+ syntax_highlighter_ini->highlight_string = zend_ini_string_literal("highlight.string");
}
/* }}} */
@@ -2029,17 +2029,16 @@ PHP_FUNCTION(ini_restore)
PHP_FUNCTION(set_include_path)
{
zend_string *new_value;
- char *old_value;
zend_string *key;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_PATH_STR(new_value)
ZEND_PARSE_PARAMETERS_END();
- old_value = zend_ini_string("include_path", sizeof("include_path") - 1, 0);
+ zend_string *old_value = zend_ini_str_literal("include_path");
/* copy to return here, because alter might free it! */
if (old_value) {
- RETVAL_STRING(old_value);
+ RETVAL_STR_COPY(old_value);
} else {
RETVAL_FALSE;
}
@@ -2059,7 +2058,7 @@ PHP_FUNCTION(get_include_path)
{
ZEND_PARSE_PARAMETERS_NONE();
- zend_string *str = zend_ini_str("include_path", sizeof("include_path") - 1, 0);
+ zend_string *str = zend_ini_str_literal("include_path");
if (str == NULL) {
RETURN_FALSE;
diff --git a/ext/standard/browscap.c b/ext/standard/browscap.c
index 5009c7793688c..ec9e3a59e4b85 100644
--- a/ext/standard/browscap.c
+++ b/ext/standard/browscap.c
@@ -337,7 +337,7 @@ static void php_browscap_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callb
) {
zend_error(E_CORE_ERROR, "Invalid browscap ini file: "
"'Parent' value cannot be same as the section name: %s "
- "(in file %s)", ZSTR_VAL(ctx->current_section_name), INI_STR("browscap"));
+ "(in file %s)", ZSTR_VAL(ctx->current_section_name), zend_ini_string_literal("browscap"));
return;
}
@@ -399,7 +399,7 @@ static void php_browscap_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callb
}
/* }}} */
-static zend_result browscap_read_file(char *filename, browser_data *browdata, bool persistent) /* {{{ */
+static zend_result browscap_read_file(const char *filename, browser_data *browdata, bool persistent) /* {{{ */
{
zend_file_handle fh;
browscap_parser_ctx ctx = {0};
@@ -499,7 +499,7 @@ PHP_INI_MH(OnChangeBrowscap)
PHP_MINIT_FUNCTION(browscap) /* {{{ */
{
- char *browscap = INI_STR("browscap");
+ const char *browscap = zend_ini_string_literal("browscap");
#ifdef ZTS
ts_allocate_id(&browscap_globals_id, sizeof(browser_data), (ts_allocate_ctor) browscap_globals_ctor, NULL);
diff --git a/ext/standard/config.m4 b/ext/standard/config.m4
index ef6b3c5a01018..1d64c7ff69681 100644
--- a/ext/standard/config.m4
+++ b/ext/standard/config.m4
@@ -328,7 +328,7 @@ dnl
PHP_CHECK_FUNC(res_search, resolv, socket)
-AC_CHECK_FUNCS([posix_spawn_file_actions_addchdir_np elf_aux_info])
+AC_CHECK_FUNCS([posix_spawn_file_actions_addchdir posix_spawn_file_actions_addchdir_np elf_aux_info])
dnl
dnl Obsolete check for strptime() declaration. The strptime, where available,
diff --git a/ext/standard/dl.c b/ext/standard/dl.c
index 31adbceac8c29..209b0c1d3b01c 100644
--- a/ext/standard/dl.c
+++ b/ext/standard/dl.c
@@ -114,11 +114,11 @@ PHPAPI int php_load_extension(const char *filename, int type, int start_now)
zend_module_entry *module_entry;
zend_module_entry *(*get_module)(void);
int error_type, slash_suffix = 0;
- char *extension_dir;
+ const char *extension_dir;
char *err1, *err2;
if (type == MODULE_PERSISTENT) {
- extension_dir = INI_STR("extension_dir");
+ extension_dir = zend_ini_string_literal("extension_dir");
} else {
extension_dir = PG(extension_dir);
}
diff --git a/ext/standard/filters.c b/ext/standard/filters.c
index 0faa0eda6863a..c909211788b03 100644
--- a/ext/standard/filters.c
+++ b/ext/standard/filters.c
@@ -59,12 +59,13 @@ static php_stream_filter_status_t strfilter_rot13_filter(
static const php_stream_filter_ops strfilter_rot13_ops = {
strfilter_rot13_filter,
NULL,
+ NULL,
"string.rot13"
};
-static php_stream_filter *strfilter_rot13_create(const char *filtername, zval *filterparams, uint8_t persistent)
+static php_stream_filter *strfilter_rot13_create(const char *filtername, zval *filterparams, bool persistent)
{
- return php_stream_filter_alloc(&strfilter_rot13_ops, NULL, persistent);
+ return php_stream_filter_alloc(&strfilter_rot13_ops, NULL, persistent, PSFS_SEEKABLE_ALWAYS);
}
static const php_stream_filter_factory strfilter_rot13_factory = {
@@ -135,23 +136,25 @@ static php_stream_filter_status_t strfilter_tolower_filter(
static const php_stream_filter_ops strfilter_toupper_ops = {
strfilter_toupper_filter,
NULL,
+ NULL,
"string.toupper"
};
static const php_stream_filter_ops strfilter_tolower_ops = {
strfilter_tolower_filter,
NULL,
+ NULL,
"string.tolower"
};
-static php_stream_filter *strfilter_toupper_create(const char *filtername, zval *filterparams, uint8_t persistent)
+static php_stream_filter *strfilter_toupper_create(const char *filtername, zval *filterparams, bool persistent)
{
- return php_stream_filter_alloc(&strfilter_toupper_ops, NULL, persistent);
+ return php_stream_filter_alloc(&strfilter_toupper_ops, NULL, persistent, PSFS_SEEKABLE_ALWAYS);
}
-static php_stream_filter *strfilter_tolower_create(const char *filtername, zval *filterparams, uint8_t persistent)
+static php_stream_filter *strfilter_tolower_create(const char *filtername, zval *filterparams, bool persistent)
{
- return php_stream_filter_alloc(&strfilter_tolower_ops, NULL, persistent);
+ return php_stream_filter_alloc(&strfilter_tolower_ops, NULL, persistent, PSFS_SEEKABLE_ALWAYS);
}
static const php_stream_filter_factory strfilter_toupper_factory = {
@@ -181,14 +184,17 @@ typedef struct _php_conv php_conv;
typedef php_conv_err_t (*php_conv_convert_func)(php_conv *, const char **, size_t *, char **, size_t *);
typedef void (*php_conv_dtor_func)(php_conv *);
+typedef php_conv_err_t (*php_conv_reset_func)(php_conv *);
struct _php_conv {
php_conv_convert_func convert_op;
php_conv_dtor_func dtor;
+ php_conv_reset_func reset_op;
};
#define php_conv_convert(a, b, c, d, e) ((php_conv *)(a))->convert_op((php_conv *)(a), (b), (c), (d), (e))
#define php_conv_dtor(a) ((php_conv *)a)->dtor((a))
+#define php_conv_reset(a) (((php_conv *)(a))->reset_op ? ((php_conv *)(a))->reset_op((php_conv *)(a)) : PHP_CONV_ERR_SUCCESS)
/* {{{ php_conv_base64_encode */
typedef struct _php_conv_base64_encode {
@@ -206,6 +212,7 @@ typedef struct _php_conv_base64_encode {
static php_conv_err_t php_conv_base64_encode_convert(php_conv_base64_encode *inst, const char **in_p, size_t *in_left, char **out_p, size_t *out_left);
static void php_conv_base64_encode_dtor(php_conv_base64_encode *inst);
+static php_conv_err_t php_conv_base64_encode_reset(php_conv_base64_encode *inst);
static const unsigned char b64_tbl_enc[256] = {
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
@@ -226,10 +233,19 @@ static const unsigned char b64_tbl_enc[256] = {
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
};
+static php_conv_err_t php_conv_base64_encode_reset(php_conv_base64_encode *inst)
+{
+ /* Reset only mutable state, preserve configuration (lbchars, line_len, etc.) */
+ inst->erem_len = 0;
+ inst->line_ccnt = inst->line_len;
+ return PHP_CONV_ERR_SUCCESS;
+}
+
static php_conv_err_t php_conv_base64_encode_ctor(php_conv_base64_encode *inst, unsigned int line_len, const char *lbchars, size_t lbchars_len, int lbchars_dup, bool persistent)
{
inst->_super.convert_op = (php_conv_convert_func) php_conv_base64_encode_convert;
inst->_super.dtor = (php_conv_dtor_func) php_conv_base64_encode_dtor;
+ inst->_super.reset_op = (php_conv_reset_func) php_conv_base64_encode_reset;
inst->erem_len = 0;
inst->line_ccnt = line_len;
inst->line_len = line_len;
@@ -449,6 +465,7 @@ typedef struct _php_conv_base64_decode {
static php_conv_err_t php_conv_base64_decode_convert(php_conv_base64_decode *inst, const char **in_p, size_t *in_left, char **out_p, size_t *out_left);
static void php_conv_base64_decode_dtor(php_conv_base64_decode *inst);
+static php_conv_err_t php_conv_base64_decode_reset(php_conv_base64_decode *inst);
static unsigned int b64_tbl_dec[256] = {
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
@@ -469,10 +486,21 @@ static unsigned int b64_tbl_dec[256] = {
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
};
+static php_conv_err_t php_conv_base64_decode_reset(php_conv_base64_decode *inst)
+{
+ /* Reset only mutable state */
+ inst->urem = 0;
+ inst->urem_nbits = 0;
+ inst->ustat = 0;
+ inst->eos = 0;
+ return PHP_CONV_ERR_SUCCESS;
+}
+
static void php_conv_base64_decode_ctor(php_conv_base64_decode *inst)
{
inst->_super.convert_op = (php_conv_convert_func) php_conv_base64_decode_convert;
inst->_super.dtor = (php_conv_dtor_func) php_conv_base64_decode_dtor;
+ inst->_super.reset_op = (php_conv_reset_func) php_conv_base64_decode_reset;
inst->urem = 0;
inst->urem_nbits = 0;
@@ -618,6 +646,7 @@ typedef struct _php_conv_qprint_encode {
static void php_conv_qprint_encode_dtor(php_conv_qprint_encode *inst);
static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *inst, const char **in_pp, size_t *in_left_p, char **out_pp, size_t *out_left_p);
+static php_conv_err_t php_conv_qprint_encode_reset(php_conv_qprint_encode *inst);
static void php_conv_qprint_encode_dtor(php_conv_qprint_encode *inst)
{
@@ -627,6 +656,15 @@ static void php_conv_qprint_encode_dtor(php_conv_qprint_encode *inst)
}
}
+static php_conv_err_t php_conv_qprint_encode_reset(php_conv_qprint_encode *inst)
+{
+ /* Reset only mutable state, preserve configuration */
+ inst->line_ccnt = inst->line_len;
+ inst->lb_ptr = 0;
+ inst->lb_cnt = 0;
+ return PHP_CONV_ERR_SUCCESS;
+}
+
#define NEXT_CHAR(ps, icnt, lb_ptr, lb_cnt, lbchars) \
((lb_ptr) < (lb_cnt) ? (lbchars)[(lb_ptr)] : *(ps))
@@ -832,6 +870,7 @@ static php_conv_err_t php_conv_qprint_encode_ctor(php_conv_qprint_encode *inst,
}
inst->_super.convert_op = (php_conv_convert_func) php_conv_qprint_encode_convert;
inst->_super.dtor = (php_conv_dtor_func) php_conv_qprint_encode_dtor;
+ inst->_super.reset_op = (php_conv_reset_func) php_conv_qprint_encode_reset;
inst->line_ccnt = line_len;
inst->line_len = line_len;
if (lbchars != NULL) {
@@ -862,6 +901,10 @@ typedef struct _php_conv_qprint_decode {
unsigned int lb_cnt;
} php_conv_qprint_decode;
+static void php_conv_qprint_decode_dtor(php_conv_qprint_decode *inst);
+static php_conv_err_t php_conv_qprint_decode_convert(php_conv_qprint_decode *inst, const char **in_pp, size_t *in_left_p, char **out_pp, size_t *out_left_p);
+static php_conv_err_t php_conv_qprint_decode_reset(php_conv_qprint_decode *inst);
+
static void php_conv_qprint_decode_dtor(php_conv_qprint_decode *inst)
{
assert(inst != NULL);
@@ -870,6 +913,16 @@ static void php_conv_qprint_decode_dtor(php_conv_qprint_decode *inst)
}
}
+static php_conv_err_t php_conv_qprint_decode_reset(php_conv_qprint_decode *inst)
+{
+ /* Reset only mutable state, preserve configuration */
+ inst->scan_stat = 0;
+ inst->next_char = 0;
+ inst->lb_ptr = 0;
+ inst->lb_cnt = 0;
+ return PHP_CONV_ERR_SUCCESS;
+}
+
static php_conv_err_t php_conv_qprint_decode_convert(php_conv_qprint_decode *inst, const char **in_pp, size_t *in_left_p, char **out_pp, size_t *out_left_p)
{
php_conv_err_t err = PHP_CONV_ERR_SUCCESS;
@@ -1040,10 +1093,12 @@ static php_conv_err_t php_conv_qprint_decode_convert(php_conv_qprint_decode *ins
return err;
}
+
static php_conv_err_t php_conv_qprint_decode_ctor(php_conv_qprint_decode *inst, const char *lbchars, size_t lbchars_len, int lbchars_dup, bool persistent)
{
inst->_super.convert_op = (php_conv_convert_func) php_conv_qprint_decode_convert;
inst->_super.dtor = (php_conv_dtor_func) php_conv_qprint_decode_dtor;
+ inst->_super.reset_op = (php_conv_reset_func) php_conv_qprint_decode_reset;
inst->scan_stat = 0;
inst->next_char = 0;
inst->lb_ptr = inst->lb_cnt = 0;
@@ -1540,6 +1595,29 @@ static php_stream_filter_status_t strfilter_convert_filter(
return PSFS_ERR_FATAL;
}
+static zend_result strfilter_convert_seek(
+ php_stream *stream,
+ php_stream_filter *thisfilter,
+ zend_off_t offset,
+ int whence
+ )
+{
+ php_convert_filter *inst = (php_convert_filter *)Z_PTR(thisfilter->abstract);
+
+ /* Reset stub buffer */
+ inst->stub_len = 0;
+
+ /* Reset the converter state - preserves all configuration/options */
+ if (inst->cd != NULL) {
+ php_conv_err_t err = php_conv_reset(inst->cd);
+ if (err != PHP_CONV_ERR_SUCCESS) {
+ return FAILURE;
+ }
+ }
+
+ return SUCCESS;
+}
+
static void strfilter_convert_dtor(php_stream_filter *thisfilter)
{
assert(Z_PTR(thisfilter->abstract) != NULL);
@@ -1550,11 +1628,12 @@ static void strfilter_convert_dtor(php_stream_filter *thisfilter)
static const php_stream_filter_ops strfilter_convert_ops = {
strfilter_convert_filter,
+ strfilter_convert_seek,
strfilter_convert_dtor,
"convert.*"
};
-static php_stream_filter *strfilter_convert_create(const char *filtername, zval *filterparams, uint8_t persistent)
+static php_stream_filter *strfilter_convert_create(const char *filtername, zval *filterparams, bool persistent)
{
php_convert_filter *inst;
@@ -1590,7 +1669,7 @@ static php_stream_filter *strfilter_convert_create(const char *filtername, zval
return NULL;
}
- return php_stream_filter_alloc(&strfilter_convert_ops, inst, persistent);
+ return php_stream_filter_alloc(&strfilter_convert_ops, inst, persistent, PSFS_SEEKABLE_START);
}
static const php_stream_filter_factory strfilter_convert_factory = {
@@ -1637,6 +1716,22 @@ static php_stream_filter_status_t consumed_filter_filter(
return PSFS_PASS_ON;
}
+static zend_result consumed_filter_seek(
+ php_stream *stream,
+ php_stream_filter *thisfilter,
+ zend_off_t offset,
+ int whence
+ )
+{
+ php_consumed_filter_data *data = (php_consumed_filter_data *)Z_PTR(thisfilter->abstract);
+
+ /* Reset consumed state */
+ data->consumed = 0;
+ data->offset = ~0;
+
+ return SUCCESS;
+}
+
static void consumed_filter_dtor(php_stream_filter *thisfilter)
{
if (thisfilter && Z_PTR(thisfilter->abstract)) {
@@ -1647,11 +1742,12 @@ static void consumed_filter_dtor(php_stream_filter *thisfilter)
static const php_stream_filter_ops consumed_filter_ops = {
consumed_filter_filter,
+ consumed_filter_seek,
consumed_filter_dtor,
"consumed"
};
-static php_stream_filter *consumed_filter_create(const char *filtername, zval *filterparams, uint8_t persistent)
+static php_stream_filter *consumed_filter_create(const char *filtername, zval *filterparams, bool persistent)
{
const php_stream_filter_ops *fops = NULL;
php_consumed_filter_data *data;
@@ -1667,7 +1763,7 @@ static php_stream_filter *consumed_filter_create(const char *filtername, zval *f
data->offset = ~0;
fops = &consumed_filter_ops;
- return php_stream_filter_alloc(fops, data, persistent);
+ return php_stream_filter_alloc(fops, data, persistent, PSFS_SEEKABLE_START);
}
static const php_stream_filter_factory consumed_filter_factory = {
@@ -1851,6 +1947,22 @@ static php_stream_filter_status_t php_chunked_filter(
return PSFS_PASS_ON;
}
+static zend_result php_chunked_seek(
+ php_stream *stream,
+ php_stream_filter *thisfilter,
+ zend_off_t offset,
+ int whence
+ )
+{
+ php_chunked_filter_data *data = (php_chunked_filter_data *)Z_PTR(thisfilter->abstract);
+
+ /* Reset chunked decoder state */
+ data->state = CHUNK_SIZE_START;
+ data->chunk_size = 0;
+
+ return SUCCESS;
+}
+
static void php_chunked_dtor(php_stream_filter *thisfilter)
{
if (thisfilter && Z_PTR(thisfilter->abstract)) {
@@ -1861,11 +1973,12 @@ static void php_chunked_dtor(php_stream_filter *thisfilter)
static const php_stream_filter_ops chunked_filter_ops = {
php_chunked_filter,
+ php_chunked_seek,
php_chunked_dtor,
"dechunk"
};
-static php_stream_filter *chunked_filter_create(const char *filtername, zval *filterparams, uint8_t persistent)
+static php_stream_filter *chunked_filter_create(const char *filtername, zval *filterparams, bool persistent)
{
const php_stream_filter_ops *fops = NULL;
php_chunked_filter_data *data;
@@ -1881,7 +1994,7 @@ static php_stream_filter *chunked_filter_create(const char *filtername, zval *fi
data->persistent = persistent;
fops = &chunked_filter_ops;
- return php_stream_filter_alloc(fops, data, persistent);
+ return php_stream_filter_alloc(fops, data, persistent, PSFS_SEEKABLE_START);
}
static const php_stream_filter_factory chunked_filter_factory = {
diff --git a/ext/standard/mail.c b/ext/standard/mail.c
index 395c7bb81d4f9..c909c0ed55dd7 100644
--- a/ext/standard/mail.c
+++ b/ext/standard/mail.c
@@ -336,8 +336,8 @@ PHP_FUNCTION(mail)
subject_r = subject;
}
- zend_string *force_extra_parameters = zend_ini_str_ex("mail.force_extra_parameters", strlen("mail.force_extra_parameters"), false, NULL);
- if (force_extra_parameters) {
+ zend_string *force_extra_parameters = zend_ini_str_literal("mail.force_extra_parameters");
+ if (force_extra_parameters && ZSTR_LEN(force_extra_parameters) > 0) {
extra_cmd = php_escape_shell_cmd(force_extra_parameters);
} else if (extra_cmd) {
extra_cmd = php_escape_shell_cmd(extra_cmd);
@@ -437,9 +437,9 @@ static int php_mail_detect_multiple_crlf(const char *hdr) {
PHPAPI bool php_mail(const char *to, const char *subject, const char *message, const char *headers, const zend_string *extra_cmd)
{
FILE *sendmail;
- char *sendmail_path = INI_STR("sendmail_path");
+ const char *sendmail_path = zend_ini_string_literal("sendmail_path");
char *sendmail_cmd = NULL;
- const zend_string *mail_log = zend_ini_str(ZEND_STRL("mail.log"), false);
+ const zend_string *mail_log = zend_ini_str_literal("mail.log");
const char *hdr = headers;
char *ahdr = NULL;
#if PHP_SIGCHILD
@@ -536,7 +536,7 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c
char *tsm_errmsg = NULL;
/* handle old style win smtp sending */
- if (TSendMail(INI_STR("SMTP"), &tsm_err, &tsm_errmsg, hdr, subject, to, message) == FAILURE) {
+ if (TSendMail(zend_ini_string_literal("SMTP"), &tsm_err, &tsm_errmsg, hdr, subject, to, message) == FAILURE) {
if (tsm_errmsg) {
php_error_docref(NULL, E_WARNING, "%s", tsm_errmsg);
efree(tsm_errmsg);
@@ -553,7 +553,7 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c
if (extra_cmd != NULL) {
spprintf(&sendmail_cmd, 0, "%s %s", sendmail_path, ZSTR_VAL(extra_cmd));
} else {
- sendmail_cmd = sendmail_path;
+ sendmail_cmd = (char*)sendmail_path;
}
#if PHP_SIGCHILD
@@ -576,7 +576,7 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c
sendmail = popen(sendmail_cmd, "w");
#endif
if (extra_cmd != NULL) {
- efree (sendmail_cmd);
+ efree(sendmail_cmd);
}
if (sendmail) {
@@ -701,7 +701,7 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c
/* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(mail)
{
- char *sendmail_path = INI_STR("sendmail_path");
+ const char *sendmail_path = zend_ini_string_literal("sendmail_path");
#ifdef PHP_WIN32
if (!sendmail_path) {
diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c
index 55f4dd4849587..a393a2a1f8848 100644
--- a/ext/standard/proc_open.c
+++ b/ext/standard/proc_open.c
@@ -35,7 +35,7 @@
#include
#endif
-#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP
+#if defined(HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP) || defined(HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR)
/* Only defined on glibc >= 2.29, FreeBSD CURRENT, musl >= 1.1.24,
* MacOS Catalina or later..
* It should be possible to modify this so it is also
@@ -45,6 +45,13 @@
*/
#include
#define USE_POSIX_SPAWN
+
+/* The non-_np variant is in macOS 26 (and _np deprecated) */
+#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR
+#define POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR posix_spawn_file_actions_addchdir
+#else
+#define POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR posix_spawn_file_actions_addchdir_np
+#endif
#endif
/* This symbol is defined in ext/standard/config.m4.
@@ -1394,9 +1401,9 @@ PHP_FUNCTION(proc_open)
}
if (cwd) {
- r = posix_spawn_file_actions_addchdir_np(&factions, cwd);
+ r = POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR(&factions, cwd);
if (r != 0) {
- php_error_docref(NULL, E_WARNING, "posix_spawn_file_actions_addchdir_np() failed: %s", strerror(r));
+ php_error_docref(NULL, E_WARNING, ZEND_TOSTR(POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR) "() failed: %s", strerror(r));
}
}
diff --git a/ext/standard/tests/filters/convert_filter_seek.phpt b/ext/standard/tests/filters/convert_filter_seek.phpt
new file mode 100644
index 0000000000000..6cbcd7b843f35
--- /dev/null
+++ b/ext/standard/tests/filters/convert_filter_seek.phpt
@@ -0,0 +1,75 @@
+--TEST--
+convert filters (base64, quoted-printable) with seek to start only
+--FILE--
+
+--CLEAN--
+
+--EXPECTF--
+Testing convert.base64-encode/decode
+First read (20 bytes): Hello World! This is
+Seek to start: SUCCESS
+Content matches: YES
+
+Warning: fseek(): Stream filter convert.* is seekable only to start position in %s on line %d
+Seek to middle: FAILURE
+
+Testing convert.quoted-printable-encode/decode
+First read (10 bytes): 4c696e65310d0a4c696e
+Seek to start: SUCCESS
+Content matches: YES
+
+Warning: fseek(): Stream filter convert.* is seekable only to start position in %s on line %d
+Seek to middle: FAILURE
diff --git a/ext/standard/tests/filters/php_user_filter_04.phpt b/ext/standard/tests/filters/php_user_filter_04.phpt
new file mode 100644
index 0000000000000..72f874f21ad40
--- /dev/null
+++ b/ext/standard/tests/filters/php_user_filter_04.phpt
@@ -0,0 +1,28 @@
+--TEST--
+php_user_filter with invalid seek signature
+--FILE--
+
+--EXPECTF--
+Fatal error: Declaration of InvalidSeekFilter::seek($offset): bool must be compatible with php_user_filter::seek(int $offset, int $whence): bool in %s on line %d
diff --git a/ext/standard/tests/filters/string_filters_seek.phpt b/ext/standard/tests/filters/string_filters_seek.phpt
new file mode 100644
index 0000000000000..60eab2ccb84ae
--- /dev/null
+++ b/ext/standard/tests/filters/string_filters_seek.phpt
@@ -0,0 +1,68 @@
+--TEST--
+string filters (rot13, toupper, tolower) with seek - fully seekable
+--FILE--
+ 'Uryyb Jbeyq! Guvf vf n grfg sbe fgevat svygre frrxvat.',
+ 'string.toupper' => 'HELLO WORLD! THIS IS A TEST FOR STRING FILTER SEEKING.',
+ 'string.tolower' => 'hello world! this is a test for string filter seeking.'
+];
+
+foreach ($filters as $filter) {
+ echo "Testing filter: $filter\n";
+
+ file_put_contents($file, $text);
+
+ $fp = fopen($file, 'r');
+ stream_filter_append($fp, $filter, STREAM_FILTER_READ);
+
+ $partial = fread($fp, 20);
+ echo "First read (20 bytes): $partial\n";
+
+ $result = fseek($fp, 0, SEEK_SET);
+ echo "Seek to start: " . ($result === 0 ? "SUCCESS" : "FAILURE") . "\n";
+
+ $full = fread($fp, strlen($text));
+ echo "Content matches: " . ($full === $expected[$filter] ? "YES" : "NO") . "\n";
+
+ $result = fseek($fp, 13, SEEK_SET);
+ echo "Seek to middle: " . ($result === 0 ? "SUCCESS" : "FAILURE") . "\n";
+
+ $from_middle = fread($fp, 10);
+ $expected_middle = substr($expected[$filter], 13, 10);
+ echo "Read from middle matches: " . ($from_middle === $expected_middle ? "YES" : "NO") . "\n";
+
+ fclose($fp);
+ echo "\n";
+}
+?>
+--CLEAN--
+
+--EXPECT--
+Testing filter: string.rot13
+First read (20 bytes): Uryyb Jbeyq! Guvf vf
+Seek to start: SUCCESS
+Content matches: YES
+Seek to middle: SUCCESS
+Read from middle matches: YES
+
+Testing filter: string.toupper
+First read (20 bytes): HELLO WORLD! THIS IS
+Seek to start: SUCCESS
+Content matches: YES
+Seek to middle: SUCCESS
+Read from middle matches: YES
+
+Testing filter: string.tolower
+First read (20 bytes): hello world! this is
+Seek to start: SUCCESS
+Content matches: YES
+Seek to middle: SUCCESS
+Read from middle matches: YES
diff --git a/ext/standard/tests/filters/user_filter_seek_01.phpt b/ext/standard/tests/filters/user_filter_seek_01.phpt
new file mode 100644
index 0000000000000..cb4e9fe72267f
--- /dev/null
+++ b/ext/standard/tests/filters/user_filter_seek_01.phpt
@@ -0,0 +1,81 @@
+--TEST--
+php_user_filter with seek method - always seekable (stateless filter)
+--FILE--
+data); $i++) {
+ $char = $bucket->data[$i];
+ if (ctype_alpha($char)) {
+ $base = ctype_upper($char) ? ord('A') : ord('a');
+ $rotated .= chr($base + (ord($char) - $base + $this->rotation) % 26);
+ } else {
+ $rotated .= $char;
+ }
+ }
+ $bucket->data = $rotated;
+ $consumed += $bucket->datalen;
+ stream_bucket_append($out, $bucket);
+ }
+ return PSFS_PASS_ON;
+ }
+
+ public function onCreate(): bool
+ {
+ if (isset($this->params['rotation'])) {
+ $this->rotation = (int)$this->params['rotation'];
+ }
+ return true;
+ }
+
+ public function onClose(): void {}
+
+ public function seek(int $offset, int $whence): bool
+ {
+ // Stateless filter - always seekable to any position
+ return true;
+ }
+}
+
+stream_filter_register('test.rotate', 'RotateFilter');
+
+$file = __DIR__ . '/user_filter_seek_001.txt';
+$text = 'Hello World';
+
+file_put_contents($file, $text);
+
+$fp = fopen($file, 'r');
+stream_filter_append($fp, 'test.rotate', STREAM_FILTER_READ, ['rotation' => 13]);
+
+$partial = fread($fp, 5);
+echo "First read: $partial\n";
+
+$result = fseek($fp, 0, SEEK_SET);
+echo "Seek to start: " . ($result === 0 ? "SUCCESS" : "FAILURE") . "\n";
+
+$full = fread($fp, strlen($text));
+echo "Full content: $full\n";
+
+$result = fseek($fp, 6, SEEK_SET);
+echo "Seek to middle: " . ($result === 0 ? "SUCCESS" : "FAILURE") . "\n";
+
+$from_middle = fread($fp, 5);
+echo "Read from middle: $from_middle\n";
+
+fclose($fp);
+unlink($file);
+
+?>
+--EXPECT--
+First read: Uryyb
+Seek to start: SUCCESS
+Full content: Uryyb Jbeyq
+Seek to middle: SUCCESS
+Read from middle: Jbeyq
diff --git a/ext/standard/tests/filters/user_filter_seek_02.phpt b/ext/standard/tests/filters/user_filter_seek_02.phpt
new file mode 100644
index 0000000000000..39f4c3c66243e
--- /dev/null
+++ b/ext/standard/tests/filters/user_filter_seek_02.phpt
@@ -0,0 +1,75 @@
+--TEST--
+php_user_filter with seek method - stateful filter
+--FILE--
+data); $i++) {
+ $modified .= $bucket->data[$i] . $this->count++;
+ }
+ $bucket->data = $modified;
+ $consumed += $bucket->datalen;
+ stream_bucket_append($out, $bucket);
+ }
+ return PSFS_PASS_ON;
+ }
+
+ public function onCreate(): bool
+ {
+ return true;
+ }
+
+ public function onClose(): void {}
+
+ public function seek(int $offset, int $whence): bool
+ {
+ if ($offset === 0 && $whence === SEEK_SET) {
+ $this->count = 0;
+ return true;
+ }
+ return false;
+ }
+}
+
+stream_filter_register('test.counting', 'CountingFilter');
+
+$file = __DIR__ . '/user_filter_seek_002.txt';
+$text = 'ABC';
+
+file_put_contents($file, $text);
+
+$fp = fopen($file, 'r');
+stream_filter_append($fp, 'test.counting', STREAM_FILTER_READ);
+
+$first = fread($fp, 10);
+echo "First read: $first\n";
+
+$result = fseek($fp, 0, SEEK_SET);
+echo "Seek to start: " . ($result === 0 ? "SUCCESS" : "FAILURE") . "\n";
+
+$second = fread($fp, 10);
+echo "Second read after seek: $second\n";
+echo "Counts reset: " . ($first === $second ? "YES" : "NO") . "\n";
+
+$result = fseek($fp, 1, SEEK_SET);
+echo "Seek to middle: " . ($result === 0 ? "SUCCESS" : "FAILURE") . "\n";
+
+fclose($fp);
+unlink($file);
+
+?>
+--EXPECTF--
+First read: A0B1C2
+Seek to start: SUCCESS
+Second read after seek: A0B1C2
+Counts reset: YES
+
+Warning: fseek(): Stream filter seeking for user-filter failed in %s on line %d
+Seek to middle: FAILURE
diff --git a/ext/standard/tests/mail/mail_basic2.phpt b/ext/standard/tests/mail/mail_basic2.phpt
index 44d8d86310b01..96a5a4a10a113 100644
--- a/ext/standard/tests/mail/mail_basic2.phpt
+++ b/ext/standard/tests/mail/mail_basic2.phpt
@@ -20,14 +20,17 @@ $message = 'A Message';
$additional_headers = 'KHeaders';
$additional_parameters = "-n";
$outFile = "/tmp/php_test_mailBasic2.out";
-@unlink($outFile);
echo "-- extra parameters --\n";
// Calling mail() with all possible arguments
var_dump( mail($to, $subject, $message, $additional_headers, $additional_parameters) );
echo file_get_contents($outFile);
-unlink($outFile);
+
+?>
+--CLEAN--
+
--EXPECTF--
*** Testing mail() : basic functionality ***
diff --git a/ext/standard/user_filters.c b/ext/standard/user_filters.c
index 610784e547e45..ac21e083efbb0 100644
--- a/ext/standard/user_filters.c
+++ b/ext/standard/user_filters.c
@@ -48,6 +48,16 @@ PHP_METHOD(php_user_filter, filter)
RETURN_LONG(PSFS_ERR_FATAL);
}
+PHP_METHOD(php_user_filter, seek)
+{
+ zend_long offset, whence;
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &offset, &whence) == FAILURE) {
+ RETURN_THROWS();
+ }
+
+ RETURN_TRUE;
+}
+
PHP_METHOD(php_user_filter, onCreate)
{
ZEND_PARSE_PARAMETERS_NONE();
@@ -123,6 +133,36 @@ static void userfilter_dtor(php_stream_filter *thisfilter)
zval_ptr_dtor(obj);
}
+static zend_result userfilter_assign_stream(php_stream *stream, zval *obj,
+ zend_string **stream_name_p, uint32_t orig_no_fclose)
+{
+ /* Give the userfilter class a hook back to the stream */
+ const zend_class_entry *old_scope = EG(fake_scope);
+ EG(fake_scope) = Z_OBJCE_P(obj);
+
+ zend_string *stream_name = ZSTR_INIT_LITERAL("stream", false);
+ bool stream_property_exists = Z_OBJ_HT_P(obj)->has_property(Z_OBJ_P(obj), stream_name, ZEND_PROPERTY_EXISTS, NULL);
+ if (stream_property_exists) {
+ zval stream_zval;
+ php_stream_to_zval(stream, &stream_zval);
+ zend_update_property_ex(Z_OBJCE_P(obj), Z_OBJ_P(obj), stream_name, &stream_zval);
+ /* If property update threw an exception, skip filter execution */
+ if (EG(exception)) {
+ EG(fake_scope) = old_scope;
+ zend_string_release(stream_name);
+ stream->flags &= ~PHP_STREAM_FLAG_NO_FCLOSE;
+ stream->flags |= orig_no_fclose;
+ return FAILURE;
+ }
+ *stream_name_p = stream_name;
+ } else {
+ zend_string_release(stream_name);
+ }
+ EG(fake_scope) = old_scope;
+
+ return SUCCESS;
+}
+
static php_stream_filter_status_t userfilter_filter(
php_stream *stream,
php_stream_filter *thisfilter,
@@ -148,31 +188,14 @@ static php_stream_filter_status_t userfilter_filter(
uint32_t orig_no_fclose = stream->flags & PHP_STREAM_FLAG_NO_FCLOSE;
stream->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
- /* Give the userfilter class a hook back to the stream */
- const zend_class_entry *old_scope = EG(fake_scope);
- EG(fake_scope) = Z_OBJCE_P(obj);
-
- zend_string *stream_name = ZSTR_INIT_LITERAL("stream", 0);
- bool stream_property_exists = Z_OBJ_HT_P(obj)->has_property(Z_OBJ_P(obj), stream_name, ZEND_PROPERTY_EXISTS, NULL);
- if (stream_property_exists) {
- zval stream_zval;
- php_stream_to_zval(stream, &stream_zval);
- zend_update_property_ex(Z_OBJCE_P(obj), Z_OBJ_P(obj), stream_name, &stream_zval);
- /* If property update threw an exception, skip filter execution */
- if (EG(exception)) {
- EG(fake_scope) = old_scope;
- if (buckets_in->head) {
- php_error_docref(NULL, E_WARNING, "Unprocessed filter buckets remaining on input brigade");
- }
- zend_string_release(stream_name);
- stream->flags &= ~PHP_STREAM_FLAG_NO_FCLOSE;
- stream->flags |= orig_no_fclose;
- return PSFS_ERR_FATAL;
+ zend_string *stream_name = NULL;
+ if (userfilter_assign_stream(stream, obj, &stream_name, orig_no_fclose) == FAILURE) {
+ if (buckets_in->head) {
+ php_error_docref(NULL, E_WARNING, "Unprocessed filter buckets remaining on input brigade");
}
+ return PSFS_ERR_FATAL;
}
- EG(fake_scope) = old_scope;
-
ZVAL_STRINGL(&func_name, "filter", sizeof("filter")-1);
/* Setup calling arguments */
@@ -217,12 +240,11 @@ static php_stream_filter_status_t userfilter_filter(
* Since the property accepted a resource assignment above, it must have
* no type hint or be typed as mixed, so we can safely assign null.
*/
- if (stream_property_exists) {
+ if (stream_name != NULL) {
zend_update_property_null(Z_OBJCE_P(obj), Z_OBJ_P(obj), ZSTR_VAL(stream_name), ZSTR_LEN(stream_name));
+ zend_string_release(stream_name);
}
- zend_string_release(stream_name);
-
zval_ptr_dtor(&args[3]);
zval_ptr_dtor(&args[2]);
zval_ptr_dtor(&args[1]);
@@ -234,14 +256,73 @@ static php_stream_filter_status_t userfilter_filter(
return ret;
}
+static zend_result userfilter_seek(
+ php_stream *stream,
+ php_stream_filter *thisfilter,
+ zend_off_t offset,
+ int whence
+ )
+{
+ zval *obj = &thisfilter->abstract;
+ zval retval;
+ zval args[2];
+
+ /* the userfilter object probably doesn't exist anymore */
+ if (CG(unclean_shutdown)) {
+ return FAILURE;
+ }
+
+ /* Check if the seek method exists */
+ zend_function *seek_method = zend_hash_str_find_ptr(&Z_OBJCE_P(obj)->function_table, ZEND_STRL("seek"));
+ if (seek_method == NULL) {
+ /* Method doesn't exist - consider this a successful seek for BC */
+ return SUCCESS;
+ }
+
+ /* Make sure the stream is not closed while the filter callback executes. */
+ uint32_t orig_no_fclose = stream->flags & PHP_STREAM_FLAG_NO_FCLOSE;
+ stream->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
+
+ zend_string *stream_name = NULL;
+ if (userfilter_assign_stream(stream, obj, &stream_name, orig_no_fclose) == FAILURE) {
+ return FAILURE;
+ }
+
+ /* Setup calling arguments */
+ ZVAL_LONG(&args[0], offset);
+ ZVAL_LONG(&args[1], whence);
+
+ zend_call_known_function(seek_method, Z_OBJ_P(obj), Z_OBJCE_P(obj), &retval, 2, args, NULL);
+
+ zend_result ret = FAILURE;
+ if (Z_TYPE(retval) != IS_UNDEF) {
+ ret = zend_is_true(&retval) ? SUCCESS : FAILURE;
+ zval_ptr_dtor(&retval);
+ }
+
+ /* filter resources are cleaned up by the stream destructor,
+ * keeping a reference to the stream resource here would prevent it
+ * from being destroyed properly */
+ if (stream_name != NULL) {
+ zend_update_property_null(Z_OBJCE_P(obj), Z_OBJ_P(obj), ZSTR_VAL(stream_name), ZSTR_LEN(stream_name));
+ zend_string_release(stream_name);
+ }
+
+ stream->flags &= ~PHP_STREAM_FLAG_NO_FCLOSE;
+ stream->flags |= orig_no_fclose;
+
+ return ret;
+}
+
static const php_stream_filter_ops userfilter_ops = {
userfilter_filter,
+ userfilter_seek,
userfilter_dtor,
"user-filter"
};
static php_stream_filter *user_filter_factory_create(const char *filtername,
- zval *filterparams, uint8_t persistent)
+ zval *filterparams, bool persistent)
{
struct php_user_filter_data *fdat = NULL;
php_stream_filter *filter;
@@ -304,7 +385,7 @@ static php_stream_filter *user_filter_factory_create(const char *filtername,
return NULL;
}
- filter = php_stream_filter_alloc(&userfilter_ops, NULL, 0);
+ filter = php_stream_filter_alloc(&userfilter_ops, NULL, false, PSFS_SEEKABLE_CHECK);
/* filtername */
add_property_string(&obj, "filtername", filtername);
diff --git a/ext/standard/user_filters.stub.php b/ext/standard/user_filters.stub.php
index acaa42bb33c84..475ec58e79e78 100644
--- a/ext/standard/user_filters.stub.php
+++ b/ext/standard/user_filters.stub.php
@@ -49,6 +49,9 @@ class php_user_filter
*/
public function filter($in, $out, &$consumed, bool $closing): int {}
+ /** @tentative-return-type */
+ public function seek(int $offset, int $whence): bool {}
+
/** @tentative-return-type */
public function onCreate(): bool {}
diff --git a/ext/standard/user_filters_arginfo.h b/ext/standard/user_filters_arginfo.h
index 1d3c7bb53586b..d530a5c000662 100644
--- a/ext/standard/user_filters_arginfo.h
+++ b/ext/standard/user_filters_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit user_filters.stub.php instead.
- * Stub hash: 33264435fe01a2cc9aa21a4a087dbbf3c4007206 */
+ * Stub hash: 593afbcb51bb35207b93ac7556b92ac845043116 */
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_php_user_filter_filter, 0, 4, IS_LONG, 0)
ZEND_ARG_INFO(0, in)
@@ -8,6 +8,11 @@ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_php_user_filter_
ZEND_ARG_TYPE_INFO(0, closing, _IS_BOOL, 0)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_php_user_filter_seek, 0, 2, _IS_BOOL, 0)
+ ZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0)
+ ZEND_ARG_TYPE_INFO(0, whence, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_php_user_filter_onCreate, 0, 0, _IS_BOOL, 0)
ZEND_END_ARG_INFO()
@@ -15,11 +20,13 @@ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_php_user_filter_
ZEND_END_ARG_INFO()
ZEND_METHOD(php_user_filter, filter);
+ZEND_METHOD(php_user_filter, seek);
ZEND_METHOD(php_user_filter, onCreate);
ZEND_METHOD(php_user_filter, onClose);
static const zend_function_entry class_php_user_filter_methods[] = {
ZEND_ME(php_user_filter, filter, arginfo_class_php_user_filter_filter, ZEND_ACC_PUBLIC)
+ ZEND_ME(php_user_filter, seek, arginfo_class_php_user_filter_seek, ZEND_ACC_PUBLIC)
ZEND_ME(php_user_filter, onCreate, arginfo_class_php_user_filter_onCreate, ZEND_ACC_PUBLIC)
ZEND_ME(php_user_filter, onClose, arginfo_class_php_user_filter_onClose, ZEND_ACC_PUBLIC)
ZEND_FE_END
diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c
index 9dcfb733a591f..6b5d1061d5124 100644
--- a/ext/tidy/tidy.c
+++ b/ext/tidy/tidy.c
@@ -873,7 +873,7 @@ static PHP_RINIT_FUNCTION(tidy)
static PHP_RSHUTDOWN_FUNCTION(tidy)
{
- TG(clean_output) = INI_ORIG_BOOL("tidy.clean_output");
+ TG(clean_output) = zend_ini_parse_bool(zend_ini_str(ZEND_STRL("tidy.clean_output"), /* orig */ true));
return SUCCESS;
}
diff --git a/ext/zlib/tests/zlib_filter_seek_deflate.phpt b/ext/zlib/tests/zlib_filter_seek_deflate.phpt
new file mode 100644
index 0000000000000..6acee8e4e8c81
--- /dev/null
+++ b/ext/zlib/tests/zlib_filter_seek_deflate.phpt
@@ -0,0 +1,55 @@
+--TEST--
+zlib.deflate filter with seek to start
+--EXTENSIONS--
+zlib
+--FILE--
+ $size1 ? "YES" : "NO") . "\n";
+
+$result = fseek($fp, 50, SEEK_SET);
+echo "Seek to middle: " . ($result === 0 ? "SUCCESS" : "FAILURE") . "\n";
+
+fclose($fp);
+
+$fp = fopen($file, 'r');
+stream_filter_append($fp, 'zlib.inflate', STREAM_FILTER_READ);
+$content = stream_get_contents($fp);
+fclose($fp);
+
+echo "Decompressed content matches text2: " . ($content === $text2 ? "YES" : "NO") . "\n";
+?>
+--CLEAN--
+
+--EXPECTF--
+Size after first write: %d
+Seek to start: SUCCESS
+Size after second write: %d
+Second write is larger: YES
+
+Warning: fseek(): Stream filter zlib.deflate is seekable only to start position in %s on line %d
+Seek to middle: FAILURE
+Decompressed content matches text2: YES
diff --git a/ext/zlib/tests/zlib_filter_seek_inflate.phpt b/ext/zlib/tests/zlib_filter_seek_inflate.phpt
new file mode 100644
index 0000000000000..2ca7508941ccc
--- /dev/null
+++ b/ext/zlib/tests/zlib_filter_seek_inflate.phpt
@@ -0,0 +1,45 @@
+--TEST--
+zlib.inflate filter with seek to start
+--EXTENSIONS--
+zlib
+--FILE--
+
+--CLEAN--
+
+--EXPECTF--
+First read (20 bytes): I am the very model
+Seek to start: SUCCESS
+Position after seek: 0
+Content after seek matches: YES
+
+Warning: fseek(): Stream filter zlib.inflate is seekable only to start position in %s on line %d
+Seek to middle: FAILURE
diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c
index ad2c9edb9ddaf..ef792b374b11d 100644
--- a/ext/zlib/zlib.c
+++ b/ext/zlib/zlib.c
@@ -1263,7 +1263,6 @@ ZEND_GET_MODULE(php_zlib)
static PHP_INI_MH(OnUpdate_zlib_output_compression)
{
int int_value;
- char *ini_value;
if (new_value == NULL) {
return FAILURE;
}
@@ -1275,9 +1274,9 @@ static PHP_INI_MH(OnUpdate_zlib_output_compression)
} else {
int_value = (int) zend_ini_parse_quantity_warn(new_value, entry->name);
}
- ini_value = zend_ini_string("output_handler", sizeof("output_handler") - 1, 0);
+ const zend_string *ini_value = zend_ini_str_literal("output_handler");
- if (ini_value && *ini_value && int_value) {
+ if (ini_value && ZSTR_LEN(ini_value) > 0 && int_value) {
php_error_docref("ref.outcontrol", E_CORE_ERROR, "Cannot use both zlib.output_compression and output_handler together!!");
return FAILURE;
}
diff --git a/ext/zlib/zlib_filter.c b/ext/zlib/zlib_filter.c
index e42132fd0008c..69c9a85465e8e 100644
--- a/ext/zlib/zlib_filter.c
+++ b/ext/zlib/zlib_filter.c
@@ -28,6 +28,7 @@ typedef struct _php_zlib_filter_data {
size_t outbuf_len;
int persistent;
bool finished; /* for zlib.deflate: signals that no flush is pending */
+ int windowBits;
} php_zlib_filter_data;
/* }}} */
@@ -143,6 +144,47 @@ static php_stream_filter_status_t php_zlib_inflate_filter(
return exit_status;
}
+static zend_result php_zlib_inflate_seek(
+ php_stream *stream,
+ php_stream_filter *thisfilter,
+ zend_off_t offset,
+ int whence
+ )
+{
+ int status;
+
+ if (!thisfilter || !Z_PTR(thisfilter->abstract)) {
+ return FAILURE;
+ }
+
+ php_zlib_filter_data *data = Z_PTR(thisfilter->abstract);
+
+ if (data->finished) {
+ /* Stream was ended, need to reinitialize */
+ status = inflateInit2(&(data->strm), data->windowBits);
+ if (status != Z_OK) {
+ php_error_docref(NULL, E_WARNING, "zlib.inflate: failed to reinitialize inflation state");
+ return FAILURE;
+ }
+ } else {
+ /* Stream is active, just reset it */
+ status = inflateReset(&(data->strm));
+ if (status != Z_OK) {
+ php_error_docref(NULL, E_WARNING, "zlib.inflate: failed to reset inflation state");
+ return FAILURE;
+ }
+ }
+
+ /* Reset our own state */
+ data->strm.next_in = data->inbuf;
+ data->strm.avail_in = 0;
+ data->strm.next_out = data->outbuf;
+ data->strm.avail_out = data->outbuf_len;
+ data->finished = false;
+
+ return SUCCESS;
+}
+
static void php_zlib_inflate_dtor(php_stream_filter *thisfilter)
{
if (thisfilter && Z_PTR(thisfilter->abstract)) {
@@ -158,6 +200,7 @@ static void php_zlib_inflate_dtor(php_stream_filter *thisfilter)
static const php_stream_filter_ops php_zlib_inflate_ops = {
php_zlib_inflate_filter,
+ php_zlib_inflate_seek,
php_zlib_inflate_dtor,
"zlib.inflate"
};
@@ -259,6 +302,38 @@ static php_stream_filter_status_t php_zlib_deflate_filter(
return exit_status;
}
+static zend_result php_zlib_deflate_seek(
+ php_stream *stream,
+ php_stream_filter *thisfilter,
+ zend_off_t offset,
+ int whence
+ )
+{
+ int status;
+
+ if (!thisfilter || !Z_PTR(thisfilter->abstract)) {
+ return FAILURE;
+ }
+
+ php_zlib_filter_data *data = Z_PTR(thisfilter->abstract);
+
+ /* Reset zlib deflation state */
+ status = deflateReset(&(data->strm));
+ if (status != Z_OK) {
+ php_error_docref(NULL, E_WARNING, "zlib.deflate: failed to reset deflation state");
+ return FAILURE;
+ }
+
+ /* Reset our own state */
+ data->strm.next_in = data->inbuf;
+ data->strm.avail_in = 0;
+ data->strm.next_out = data->outbuf;
+ data->strm.avail_out = data->outbuf_len;
+ data->finished = true;
+
+ return SUCCESS;
+}
+
static void php_zlib_deflate_dtor(php_stream_filter *thisfilter)
{
if (thisfilter && Z_PTR(thisfilter->abstract)) {
@@ -272,6 +347,7 @@ static void php_zlib_deflate_dtor(php_stream_filter *thisfilter)
static const php_stream_filter_ops php_zlib_deflate_ops = {
php_zlib_deflate_filter,
+ php_zlib_deflate_seek,
php_zlib_deflate_dtor,
"zlib.deflate"
};
@@ -280,7 +356,7 @@ static const php_stream_filter_ops php_zlib_deflate_ops = {
/* {{{ zlib.* common factory */
-static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *filterparams, uint8_t persistent)
+static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *filterparams, bool persistent)
{
const php_stream_filter_ops *fops = NULL;
php_zlib_filter_data *data;
@@ -315,6 +391,7 @@ static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *f
}
data->strm.data_type = Z_ASCII;
+ data->persistent = persistent;
if (strcasecmp(filtername, "zlib.inflate") == 0) {
int windowBits = -MAX_WBITS;
@@ -334,6 +411,9 @@ static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *f
}
}
+ /* Save configuration for reset */
+ data->windowBits = windowBits;
+
/* RFC 1951 Inflate */
data->finished = false;
status = inflateInit2(&(data->strm), windowBits);
@@ -401,6 +481,10 @@ static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *f
php_error_docref(NULL, E_WARNING, "Invalid filter parameter, ignored");
}
}
+
+ /* Save configuration for reset */
+ data->windowBits = windowBits;
+
status = deflateInit2(&(data->strm), level, Z_DEFLATED, windowBits, memLevel, 0);
data->finished = true;
fops = &php_zlib_deflate_ops;
@@ -416,7 +500,7 @@ static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *f
return NULL;
}
- return php_stream_filter_alloc(fops, data, persistent);
+ return php_stream_filter_alloc(fops, data, persistent, PSFS_SEEKABLE_START);
}
const php_stream_filter_factory php_zlib_filter_factory = {
diff --git a/main/main.c b/main/main.c
index bbd2d18ba187b..bb5b700fe7ae1 100644
--- a/main/main.c
+++ b/main/main.c
@@ -1471,8 +1471,8 @@ static ZEND_COLD void php_error_cb(int orig_type, zend_string *error_filename, c
if (PG(xmlrpc_errors)) {
php_printf("faultCode " ZEND_LONG_FMT " faultString %s:%s in %s on line %" PRIu32 "%s%s ", PG(xmlrpc_error_number), error_type_str, ZSTR_VAL(message), ZSTR_VAL(error_filename), error_lineno, ZSTR_LEN(backtrace) ? "\nStack trace:\n" : "", ZSTR_VAL(backtrace));
} else {
- char *prepend_string = INI_STR("error_prepend_string");
- char *append_string = INI_STR("error_append_string");
+ const char *prepend_string = zend_ini_string_literal("error_prepend_string");
+ const char *append_string = zend_ini_string_literal("error_append_string");
if (PG(html_errors)) {
if (type == E_ERROR || type == E_PARSE) {
@@ -2374,7 +2374,7 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi
}
/* disable certain functions as requested by php.ini */
- zend_disable_functions(INI_STR("disable_functions"));
+ zend_disable_functions(zend_ini_string_literal("disable_functions"));
/* make core report what it should */
if ((module = zend_hash_str_find_ptr(&module_registry, "core", sizeof("core")-1)) != NULL) {
@@ -2638,7 +2638,7 @@ PHPAPI bool php_execute_script_ex(zend_file_handle *primary_file, zval *retval)
#ifdef PHP_WIN32
zend_unset_timeout();
#endif
- zend_set_timeout(INI_INT("max_execution_time"), 0);
+ zend_set_timeout(zend_ini_long_literal("max_execution_time"), false);
}
if (prepend_file_p && result) {
diff --git a/main/php_ini.c b/main/php_ini.c
index e464c05d1fcc1..4bac70e88778f 100644
--- a/main/php_ini.c
+++ b/main/php_ini.c
@@ -334,7 +334,7 @@ static void php_load_zend_extension_cb(void *arg)
} else {
DL_HANDLE handle;
char *libpath;
- char *extension_dir = INI_STR("extension_dir");
+ const char *extension_dir = zend_ini_string_literal("extension_dir");
int slash_suffix = 0;
char *err1, *err2;
diff --git a/main/rfc1867.c b/main/rfc1867.c
index f6ffb6fabc7f1..161a0e4e487f5 100644
--- a/main/rfc1867.c
+++ b/main/rfc1867.c
@@ -665,8 +665,8 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler)
zend_llist header;
void *event_extra_data = NULL;
unsigned int llen = 0;
- zend_long upload_cnt = REQUEST_PARSE_BODY_OPTION_GET(max_file_uploads, INI_INT("max_file_uploads"));
- zend_long body_parts_cnt = REQUEST_PARSE_BODY_OPTION_GET(max_multipart_body_parts, INI_INT("max_multipart_body_parts"));
+ zend_long upload_cnt = REQUEST_PARSE_BODY_OPTION_GET(max_file_uploads, zend_ini_long_literal("max_file_uploads"));
+ zend_long body_parts_cnt = REQUEST_PARSE_BODY_OPTION_GET(max_multipart_body_parts, zend_ini_long_literal("max_multipart_body_parts"));
zend_long post_max_size = REQUEST_PARSE_BODY_OPTION_GET(post_max_size, SG(post_max_size));
zend_long max_input_vars = REQUEST_PARSE_BODY_OPTION_GET(max_input_vars, PG(max_input_vars));
zend_long upload_max_filesize = REQUEST_PARSE_BODY_OPTION_GET(upload_max_filesize, PG(upload_max_filesize));
diff --git a/main/streams/filter.c b/main/streams/filter.c
index d0c1fdc8e788c..2260bae82335e 100644
--- a/main/streams/filter.c
+++ b/main/streams/filter.c
@@ -216,7 +216,7 @@ PHPAPI void php_stream_bucket_unlink(php_stream_bucket *bucket)
* match. If that fails, we try "convert.charset.*", then "convert.*"
* This means that we don't need to clog up the hashtable with a zillion
* charsets (for example) but still be able to provide them all as filters */
-PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, zval *filterparams, uint8_t persistent)
+PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, zval *filterparams, bool persistent)
{
HashTable *filter_hash = (FG(stream_filters) ? FG(stream_filters) : &stream_filters_hash);
const php_stream_filter_factory *factory = NULL;
@@ -260,7 +260,8 @@ PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, zval
return filter;
}
-PHPAPI php_stream_filter *_php_stream_filter_alloc(const php_stream_filter_ops *fops, void *abstract, uint8_t persistent STREAMS_DC)
+PHPAPI php_stream_filter *_php_stream_filter_alloc(const php_stream_filter_ops *fops,
+ void *abstract, bool persistent, php_stream_filter_seekable_t seekable STREAMS_DC)
{
php_stream_filter *filter;
@@ -268,6 +269,7 @@ PHPAPI php_stream_filter *_php_stream_filter_alloc(const php_stream_filter_ops *
memset(filter, 0, sizeof(php_stream_filter));
filter->fops = fops;
+ filter->seekable = seekable;
Z_PTR(filter->abstract) = abstract;
filter->is_persistent = persistent;
diff --git a/main/streams/php_stream_filter_api.h b/main/streams/php_stream_filter_api.h
index e224b85b2d9a2..cb4fdb5fd7f79 100644
--- a/main/streams/php_stream_filter_api.h
+++ b/main/streams/php_stream_filter_api.h
@@ -61,6 +61,13 @@ typedef enum {
PSFS_PASS_ON /* filter generated output buckets; pass them on to next in chain */
} php_stream_filter_status_t;
+typedef enum {
+ PSFS_SEEKABLE_NEVER, /* seeking filter never possible */
+ PSFS_SEEKABLE_START, /* seeking possible only to start (position 0) */
+ PSFS_SEEKABLE_CHECK, /* seeking possible but it is always checked if callback function set */
+ PSFS_SEEKABLE_ALWAYS, /* seeking is always possible */
+} php_stream_filter_seekable_t;
+
/* Buckets API. */
BEGIN_EXTERN_C()
PHPAPI php_stream_bucket *php_stream_bucket_new(const php_stream *stream, char *buf, size_t buflen, uint8_t own_buf, uint8_t buf_persistent);
@@ -88,6 +95,13 @@ typedef struct _php_stream_filter_ops {
int flags
);
+ zend_result (*seek)(
+ php_stream *stream,
+ php_stream_filter *thisfilter,
+ zend_off_t offset,
+ int whence
+ );
+
void (*dtor)(php_stream_filter *thisfilter);
const char *label;
@@ -106,7 +120,8 @@ struct _php_stream_filter {
zval abstract; /* for use by filter implementation */
php_stream_filter *next;
php_stream_filter *prev;
- int is_persistent;
+ php_stream_filter_seekable_t seekable;
+ bool is_persistent;
/* link into stream and chain */
php_stream_filter_chain *chain;
@@ -127,10 +142,14 @@ PHPAPI zend_result php_stream_filter_append_ex(php_stream_filter_chain *chain, p
PHPAPI zend_result _php_stream_filter_flush(php_stream_filter *filter, bool finish);
PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, bool call_dtor);
PHPAPI void php_stream_filter_free(php_stream_filter *filter);
-PHPAPI php_stream_filter *_php_stream_filter_alloc(const php_stream_filter_ops *fops, void *abstract, uint8_t persistent STREAMS_DC);
+PHPAPI php_stream_filter *_php_stream_filter_alloc(const php_stream_filter_ops *fops,
+ void *abstract, bool persistent, php_stream_filter_seekable_t seekable STREAMS_DC);
+
END_EXTERN_C()
-#define php_stream_filter_alloc(fops, thisptr, persistent) _php_stream_filter_alloc((fops), (thisptr), (persistent) STREAMS_CC)
-#define php_stream_filter_alloc_rel(fops, thisptr, persistent) _php_stream_filter_alloc((fops), (thisptr), (persistent) STREAMS_REL_CC)
+#define php_stream_filter_alloc(fops, thisptr, persistent, seekable) \
+ _php_stream_filter_alloc((fops), (thisptr), (persistent), (seekable) STREAMS_CC)
+#define php_stream_filter_alloc_rel(fops, thisptr, persistent, seekable) \
+ _php_stream_filter_alloc((fops), (thisptr), (persistent), (seekable) STREAMS_REL_CC)
#define php_stream_filter_prepend(chain, filter) _php_stream_filter_prepend((chain), (filter))
#define php_stream_filter_append(chain, filter) _php_stream_filter_append((chain), (filter))
#define php_stream_filter_flush(filter, finish) _php_stream_filter_flush((filter), (finish))
@@ -138,12 +157,12 @@ END_EXTERN_C()
#define php_stream_is_filtered(stream) ((stream)->readfilters.head || (stream)->writefilters.head)
typedef struct _php_stream_filter_factory {
- php_stream_filter *(*create_filter)(const char *filtername, zval *filterparams, uint8_t persistent);
+ php_stream_filter *(*create_filter)(const char *filtername, zval *filterparams, bool persistent);
} php_stream_filter_factory;
BEGIN_EXTERN_C()
PHPAPI zend_result php_stream_filter_register_factory(const char *filterpattern, const php_stream_filter_factory *factory);
PHPAPI zend_result php_stream_filter_unregister_factory(const char *filterpattern);
PHPAPI zend_result php_stream_filter_register_factory_volatile(zend_string *filterpattern, const php_stream_filter_factory *factory);
-PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, zval *filterparams, uint8_t persistent);
+PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, zval *filterparams, bool persistent);
END_EXTERN_C()
diff --git a/main/streams/streams.c b/main/streams/streams.c
index 32c7ba99f58c2..26f147632cef7 100644
--- a/main/streams/streams.c
+++ b/main/streams/streams.c
@@ -1362,6 +1362,52 @@ PHPAPI zend_off_t _php_stream_tell(const php_stream *stream)
return stream->position;
}
+static bool php_stream_are_filters_seekable(php_stream_filter *filter, bool is_start_seeking)
+{
+ while (filter) {
+ if (filter->seekable == PSFS_SEEKABLE_NEVER) {
+ php_error_docref(NULL, E_WARNING, "Stream filter %s is never seekable", filter->fops->label);
+ return false;
+ }
+ if (!is_start_seeking && filter->seekable == PSFS_SEEKABLE_START) {
+ php_error_docref(NULL, E_WARNING, "Stream filter %s is seekable only to start position", filter->fops->label);
+ return false;
+ }
+ filter = filter->next;
+ }
+ return true;
+}
+
+static zend_result php_stream_filters_seek(php_stream *stream, php_stream_filter *filter,
+ bool is_start_seeking, zend_off_t offset, int whence)
+{
+ while (filter) {
+ if (((filter->seekable == PSFS_SEEKABLE_START && is_start_seeking) ||
+ filter->seekable == PSFS_SEEKABLE_CHECK) &&
+ filter->fops->seek(stream, filter, offset, whence) == FAILURE) {
+ php_error_docref(NULL, E_WARNING, "Stream filter seeking for %s failed", filter->fops->label);
+ return FAILURE;
+ }
+ filter = filter->next;
+ }
+ return SUCCESS;
+}
+
+static zend_result php_stream_filters_seek_all(php_stream *stream, bool is_start_seeking,
+ zend_off_t offset, int whence)
+{
+ if (php_stream_filters_seek(stream, stream->writefilters.head, is_start_seeking, offset, whence) == FAILURE) {
+ return FAILURE;
+ }
+ if (php_stream_filters_seek(stream, stream->readfilters.head, is_start_seeking, offset, whence) == FAILURE) {
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
+
+
+
PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence)
{
if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) {
@@ -1374,6 +1420,18 @@ PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence)
}
}
+ bool is_start_seeking = whence == SEEK_SET && offset == 0;
+
+ if (stream->writefilters.head) {
+ _php_stream_flush(stream, 0);
+ if (!php_stream_are_filters_seekable(stream->writefilters.head, is_start_seeking)) {
+ return -1;
+ }
+ }
+ if (stream->readfilters.head && !php_stream_are_filters_seekable(stream->readfilters.head, is_start_seeking)) {
+ return -1;
+ }
+
/* handle the case where we are in the buffer */
if ((stream->flags & PHP_STREAM_FLAG_NO_BUFFER) == 0) {
switch(whence) {
@@ -1383,7 +1441,7 @@ PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence)
stream->position += offset;
stream->eof = 0;
stream->fatal_error = 0;
- return 0;
+ return php_stream_filters_seek_all(stream, is_start_seeking, offset, whence) == SUCCESS ? 0 : -1;
}
break;
case SEEK_SET:
@@ -1393,7 +1451,7 @@ PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence)
stream->position = offset;
stream->eof = 0;
stream->fatal_error = 0;
- return 0;
+ return php_stream_filters_seek_all(stream, is_start_seeking, offset, whence) == SUCCESS ? 0 : -1;
}
break;
}
@@ -1402,11 +1460,6 @@ PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence)
if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) {
int ret;
-
- if (stream->writefilters.head) {
- _php_stream_flush(stream, 0);
- }
-
switch(whence) {
case SEEK_CUR:
ZEND_ASSERT(stream->position >= 0);
@@ -1433,7 +1486,7 @@ PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence)
/* invalidate the buffer contents */
stream->readpos = stream->writepos = 0;
- return ret;
+ return php_stream_filters_seek_all(stream, is_start_seeking, offset, whence) == SUCCESS ? ret : -1;
}
/* else the stream has decided that it can't support seeking after all;
* fall through to attempt emulation */
diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c
index 9566c1abd4eec..f3b4ff6b9ddbe 100644
--- a/sapi/phpdbg/phpdbg_prompt.c
+++ b/sapi/phpdbg/phpdbg_prompt.c
@@ -1190,19 +1190,20 @@ static void add_zendext_info(zend_extension *ext) /* {{{ */ {
#ifdef HAVE_LIBDL
PHPDBG_API const char *phpdbg_load_module_or_extension(char **path, const char **name) /* {{{ */ {
DL_HANDLE handle;
- char *extension_dir;
-
- extension_dir = INI_STR("extension_dir");
+ zend_string *extension_dir = zend_ini_str_literal("extension_dir");
+ if (UNEXPECTED(zend_str_has_nul_byte(extension_dir))) {
+ phpdbg_error("extension_dir ini setting contains a nul byte");
+ return NULL;
+ }
if (strchr(*path, '/') != NULL || strchr(*path, DEFAULT_SLASH) != NULL) {
/* path is fine */
- } else if (extension_dir && extension_dir[0]) {
+ } else if (extension_dir && ZSTR_LEN(extension_dir) > 0) {
char *libpath;
- int extension_dir_len = strlen(extension_dir);
- if (IS_SLASH(extension_dir[extension_dir_len-1])) {
- spprintf(&libpath, 0, "%s%s", extension_dir, *path); /* SAFE */
+ if (IS_SLASH(ZSTR_VAL(extension_dir)[ZSTR_LEN(extension_dir)-1])) {
+ spprintf(&libpath, 0, "%s%s", ZSTR_VAL(extension_dir), *path); /* SAFE */
} else {
- spprintf(&libpath, 0, "%s%c%s", extension_dir, DEFAULT_SLASH, *path); /* SAFE */
+ spprintf(&libpath, 0, "%s%c%s", ZSTR_VAL(extension_dir), DEFAULT_SLASH, *path); /* SAFE */
}
efree(*path);
*path = libpath;
diff --git a/win32/sendmail.c b/win32/sendmail.c
index f267d1fae85ab..939106f31b00e 100644
--- a/win32/sendmail.c
+++ b/win32/sendmail.c
@@ -216,8 +216,8 @@ PHPAPI int TSendMail(const char *host, int *error, char **error_message,
}
/* Fall back to sendmail_from php.ini setting */
- if (INI_STR("sendmail_from")) {
- RPath = estrdup(INI_STR("sendmail_from"));
+ if (zend_ini_string_literal("sendmail_from")) {
+ RPath = estrdup(zend_ini_string_literal("sendmail_from"));
} else if (headers_lc) {
int found = 0;
const char *lookup = ZSTR_VAL(headers_lc);
@@ -276,7 +276,7 @@ PHPAPI int TSendMail(const char *host, int *error, char **error_message,
snprintf(*error_message, HOST_NAME_LEN + 128,
"Failed to connect to mailserver at \"%s\" port " ZEND_ULONG_FMT ", verify your \"SMTP\" "
"and \"smtp_port\" setting in php.ini or use ini_set()",
- PW32G(mail_host), !INI_INT("smtp_port") ? 25 : INI_INT("smtp_port"));
+ PW32G(mail_host), !zend_ini_long_literal("smtp_port") ? 25 : zend_ini_long_literal("smtp_port"));
return FAILURE;
} else {
ret = SendText(RPath, Subject, mailTo, data, headers_trim, headers_lc, error_message);
@@ -789,7 +789,7 @@ return 0;
}
*/
- portnum = (short) INI_INT("smtp_port");
+ portnum = (short) zend_ini_long_literal("smtp_port");
if (!portnum) {
portnum = 25;
}