[PHP-CVS] [php-src] master: ext/phar: only `.phar` extensions in file names trigger automatic archive detection (#23260)

[email protected] (Weilin Du via GitHub)
Newsgroups php.cvs
Message-ID <[email protected]>
Author: Weilin Du (LamentXU123)
Committer: GitHub (web-flow)
Pusher: LamentXU123
Date: 2026-08-15T03:00:26+08:00

Commit: https://github.com/php/php-src/commit/5332ab0c291361abe3d12db21dd55e5dc7baa829
Raw diff: https://github.com/php/php-src/commit/5332ab0c291361abe3d12db21dd55e5dc7baa829.diff

ext/phar: only `.phar` extensions in file names trigger automatic archive detection (#23260)

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.

Changed paths:
  A  ext/phar/tests/include_file_extension.phpt
  M  NEWS
  M  UPGRADING
  M  ext/phar/phar.c


Diff:

diff --git a/NEWS b/NEWS
index b7259e81f681..1bba6a7b6c22 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 390012a80702..1cc6d66f3757 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--
+<?php
+$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]))) {
+    mkdir(dirname($targets[1]));
+}
+
+foreach ($targets as $i => $target) {
+    $source = $base . ".source-$i.phar.zip";
+    $constant = "PHAR_STUB_EXECUTED_$i";
+
+    $phar = new Phar($source);
+    $phar->addFromString('payload', 'payload');
+    $phar->setStub("<?php define('$constant', true); __HALT_COMPILER();");
+    $phar->compressFiles(Phar::GZ);
+    unset($phar);
+
+    rename($source, $target);
+
+    ob_start();
+    include $target;
+    ob_end_clean();
+
+    var_dump(defined($constant));
+}
+?>
+--CLEAN--
+<?php
+$base = __DIR__ . '/' . basename(__FILE__, '.clean.php');
+@unlink($base . '.phar.png');
+@unlink($base . '.pharabcd/archive.html');
+@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');
+?>
+--EXPECT--
+bool(true)
+bool(false)
+bool(false)
+bool(true)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.