diff --git a/NEWS b/NEWS index d3b9b62ce8b2..f87c8e58ad00 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,11 @@ PHP NEWS left busy for the next fetch, and rows delivered from a result another statement took over. (KentarouTakeda) +- Phar: + . Fixed Phar archives being automatically detected when ".phar" only occurs + 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 271238bf404d..b98b6039cd60 100644 --- a/UPGRADING +++ b/UPGRADING @@ -52,6 +52,12 @@ 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 ".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 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..af3a4992d6d5 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -3167,6 +3167,26 @@ 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 + 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) { zend_string *ret = phar_find_in_include_path(filename); @@ -3186,7 +3206,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 (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 new file mode 100644 index 000000000000..2e95c0dc324b --- /dev/null +++ b/ext/phar/tests/include_file_extension.phpt @@ -0,0 +1,58 @@ +--TEST-- +Phar: only .phar extensions in file names trigger automatic archive detection +--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) +bool(true)