From 2c9e8c81646d8b43f04695c3a1f1dde349a595ff Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 14 Aug 2026 02:46:16 +0800 Subject: [PATCH 1/3] ext/phar: Only treat files with a `.phar` suffix as phar files when including --- NEWS | 4 ++ UPGRADING | 5 ++ ext/phar/phar.c | 2 +- ext/phar/tests/include_file_extension.phpt | 55 ++++++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 ext/phar/tests/include_file_extension.phpt diff --git a/NEWS b/NEWS index d3b9b62ce8b2..6c8ee3089848 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,10 @@ PHP NEWS left busy for the next fetch, and rows delivered from a result another statement took over. (KentarouTakeda) +- Phar: + . Fixed files whose paths merely contain ".phar" being automatically + interpreted as Phar archives when included. (Weilin Du) + - Readline: . Fixed class constant completion in the interactive shell. (Weilin Du) diff --git a/UPGRADING b/UPGRADING index 271238bf404d..3b491bd98e32 100644 --- a/UPGRADING +++ b/UPGRADING @@ -52,6 +52,11 @@ PHP 8.6 UPGRADE NOTES . imagesetstyle(), imagefilter() and imagecrop() filter their array arguments types / values and raise a TypeError / ValueError accordingly. +- Phar: + . Files are only automatically interpreted as Phar archives when included + if their paths end in ".phar". Previously, any path containing ".phar" + was sufficient. + - GMP: . GMP power and shift operators now throw ValueError when GMP right operands are outside the unsigned long range, instead of silently truncating them. diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 9f0ccc627279..7b63646a28f3 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -3186,7 +3186,7 @@ static zend_op_array *phar_compile_file(zend_file_handle *file_handle, int type) if (!file_handle || !file_handle->filename) { return phar_orig_compile_file(file_handle, type); } - if (strstr(ZSTR_VAL(file_handle->filename), ".phar") && !strstr(ZSTR_VAL(file_handle->filename), "://")) { + if (zend_string_ends_with_literal(file_handle->filename, ".phar") && !strstr(ZSTR_VAL(file_handle->filename), "://")) { if (SUCCESS == phar_open_from_filename(ZSTR_VAL(file_handle->filename), ZSTR_LEN(file_handle->filename), NULL, 0, &phar, NULL)) { if (phar->is_zip || phar->is_tar) { zend_file_handle f; diff --git a/ext/phar/tests/include_file_extension.phpt b/ext/phar/tests/include_file_extension.phpt new file mode 100644 index 000000000000..ca9ae378784c --- /dev/null +++ b/ext/phar/tests/include_file_extension.phpt @@ -0,0 +1,55 @@ +--TEST-- +Phar: only files ending in .phar are automatically interpreted as Phar archives +--EXTENSIONS-- +phar +zlib +--INI-- +phar.readonly=0 +phar.require_hash=0 +--FILE-- + $target) { + $source = $base . ".source-$i.phar.zip"; + $constant = "PHAR_STUB_EXECUTED_$i"; + + $phar = new Phar($source); + $phar->addFromString('payload', 'payload'); + $phar->setStub("compressFiles(Phar::GZ); + unset($phar); + + rename($source, $target); + + ob_start(); + include $target; + ob_end_clean(); + + var_dump(defined($constant)); +} +?> +--CLEAN-- + +--EXPECT-- +bool(true) +bool(false) +bool(false) From 308ad9a90db0c4fcb86e8b42c128b731f2c7913a Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 14 Aug 2026 03:05:32 +0800 Subject: [PATCH 2/3] ext/phar: .phar in a directory name does not trigger automatic archive detection --- NEWS | 4 ++-- UPGRADING | 4 ++-- ext/phar/phar.c | 15 ++++++++++++++- ext/phar/tests/include_file_extension.phpt | 11 ++++------- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/NEWS b/NEWS index 6c8ee3089848..f059f5d4b5fc 100644 --- a/NEWS +++ b/NEWS @@ -17,8 +17,8 @@ PHP NEWS statement took over. (KentarouTakeda) - Phar: - . Fixed files whose paths merely contain ".phar" being automatically - interpreted as Phar archives when included. (Weilin Du) + . Fixed Phar archives being automatically detected when ".phar" only occurs + in a directory name in an included file's path. (Weilin Du) - Readline: . Fixed class constant completion in the interactive shell. (Weilin Du) diff --git a/UPGRADING b/UPGRADING index 3b491bd98e32..78a047fd8263 100644 --- a/UPGRADING +++ b/UPGRADING @@ -54,8 +54,8 @@ PHP 8.6 UPGRADE NOTES - Phar: . Files are only automatically interpreted as Phar archives when included - if their paths end in ".phar". Previously, any path containing ".phar" - was sufficient. + if ".phar" occurs in the filename component of their paths. Previously, + it could occur in a directory name. - GMP: . GMP power and shift operators now throw ValueError when GMP right operands diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 7b63646a28f3..78195f2560e6 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -3167,6 +3167,19 @@ static size_t phar_zend_stream_fsizer(void *handle) /* {{{ */ zend_op_array *(*phar_orig_compile_file)(zend_file_handle *file_handle, int type); +static bool phar_has_marker_in_filename(const zend_string *filename) +{ + const char *path = ZSTR_VAL(filename); + const char *basename = zend_memrchr(path, '/', ZSTR_LEN(filename)); +#ifdef PHP_WIN32 + const char *backslash = zend_memrchr(path, '\\', ZSTR_LEN(filename)); + if (backslash && (!basename || backslash > basename)) { + basename = backslash; + } +#endif + return strstr(basename ? basename + 1 : path, ".phar") != NULL; +} + static zend_string *phar_resolve_path(zend_string *filename) { zend_string *ret = phar_find_in_include_path(filename); @@ -3186,7 +3199,7 @@ static zend_op_array *phar_compile_file(zend_file_handle *file_handle, int type) if (!file_handle || !file_handle->filename) { return phar_orig_compile_file(file_handle, type); } - if (zend_string_ends_with_literal(file_handle->filename, ".phar") && !strstr(ZSTR_VAL(file_handle->filename), "://")) { + if (phar_has_marker_in_filename(file_handle->filename) && !strstr(ZSTR_VAL(file_handle->filename), "://")) { if (SUCCESS == phar_open_from_filename(ZSTR_VAL(file_handle->filename), ZSTR_LEN(file_handle->filename), NULL, 0, &phar, NULL)) { if (phar->is_zip || phar->is_tar) { zend_file_handle f; diff --git a/ext/phar/tests/include_file_extension.phpt b/ext/phar/tests/include_file_extension.phpt index ca9ae378784c..ffaf368e44f4 100644 --- a/ext/phar/tests/include_file_extension.phpt +++ b/ext/phar/tests/include_file_extension.phpt @@ -1,5 +1,5 @@ --TEST-- -Phar: only files ending in .phar are automatically interpreted as Phar archives +Phar: .phar in a directory name does not trigger automatic archive detection --EXTENSIONS-- phar zlib @@ -10,13 +10,12 @@ phar.require_hash=0 $target) { @@ -41,10 +40,9 @@ foreach ($targets as $i => $target) { --CLEAN-- Date: Fri, 14 Aug 2026 04:18:51 +0800 Subject: [PATCH 3/3] feedback --- NEWS | 3 ++- UPGRADING | 5 +++-- ext/phar/phar.c | 9 ++++++++- ext/phar/tests/include_file_extension.phpt | 10 ++++++++-- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index f059f5d4b5fc..f87c8e58ad00 100644 --- a/NEWS +++ b/NEWS @@ -18,7 +18,8 @@ PHP NEWS - Phar: . Fixed Phar archives being automatically detected when ".phar" only occurs - in a directory name in an included file's path. (Weilin Du) + in a directory name or is not a filename extension in an included file's + path. (Weilin Du) - Readline: . Fixed class constant completion in the interactive shell. (Weilin Du) diff --git a/UPGRADING b/UPGRADING index 78a047fd8263..b98b6039cd60 100644 --- a/UPGRADING +++ b/UPGRADING @@ -54,8 +54,9 @@ PHP 8.6 UPGRADE NOTES - Phar: . Files are only automatically interpreted as Phar archives when included - if ".phar" occurs in the filename component of their paths. Previously, - it could occur in a directory name. + if ".phar" occurs as an extension in the filename component of their + paths. Previously, it could occur in a directory name or as part of an + extension such as ".pharma". - GMP: . GMP power and shift operators now throw ValueError when GMP right operands diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 78195f2560e6..af3a4992d6d5 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -3177,7 +3177,14 @@ static bool phar_has_marker_in_filename(const zend_string *filename) basename = backslash; } #endif - return strstr(basename ? basename + 1 : path, ".phar") != NULL; + const char *marker = basename ? basename + 1 : path; + while ((marker = strstr(marker, ".phar"))) { + marker += sizeof(".phar") - 1; + if (*marker == '\0' || *marker == '.') { + return true; + } + } + return false; } static zend_string *phar_resolve_path(zend_string *filename) diff --git a/ext/phar/tests/include_file_extension.phpt b/ext/phar/tests/include_file_extension.phpt index ffaf368e44f4..2e95c0dc324b 100644 --- a/ext/phar/tests/include_file_extension.phpt +++ b/ext/phar/tests/include_file_extension.phpt @@ -1,5 +1,5 @@ --TEST-- -Phar: .phar in a directory name does not trigger automatic archive detection +Phar: only .phar extensions in file names trigger automatic archive detection --EXTENSIONS-- phar zlib @@ -12,6 +12,8 @@ $base = __DIR__ . '/' . basename(__FILE__, '.php'); $targets = [ $base . '.phar.png', $base . '.pharabcd/archive.html', + $base . '.pharma.report.txt', + $base . '.pharma.phar.html', ]; if (!is_dir(dirname($targets[1]))) { @@ -42,7 +44,9 @@ foreach ($targets as $i => $target) { $base = __DIR__ . '/' . basename(__FILE__, '.clean.php'); @unlink($base . '.phar.png'); @unlink($base . '.pharabcd/archive.html'); -for ($i = 0; $i < 2; $i++) { +@unlink($base . '.pharma.report.txt'); +@unlink($base . '.pharma.phar.html'); +for ($i = 0; $i < 4; $i++) { @unlink($base . ".source-$i.phar.zip"); } @rmdir($base . '.pharabcd'); @@ -50,3 +54,5 @@ for ($i = 0; $i < 2; $i++) { --EXPECT-- bool(true) bool(false) +bool(false) +bool(true)