[php-src] master: ext/zip: addGlob() and addPattern() ignore their default options.

David Carlier <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: David Carlier (devnexen)
Date: 2026-08-08T21:27:14+01:00

Commit: https://github.com/php/php-src/commit/8d5b0d6fb4a9e6175f6a7e636d3e9f5c0ace7862
Raw diff: https://github.com/php/php-src/commit/8d5b0d6fb4a9e6175f6a7e636d3e9f5c0ace7862.diff

ext/zip: addGlob() and addPattern() ignore their default options.

php_zip_parse_options() held the defaults but only ran for a non-empty
options array, so opts stayed zeroed otherwise: comp_method 0 is CM_STORE
and flags 0 drops FL_OVERWRITE. Entries were therefore stored uncompressed,
and an already present entry name failed the call instead of being replaced.
The defaults now live in PHP_ZIP_DEFAULT_OPTIONS, applied at declaration.

Close GH-23108

Changed paths:
  A  ext/zip/tests/addGlob_default_options.phpt
  A  ext/zip/tests/addPattern_default_options.phpt
  M  NEWS
  M  ext/zip/php_zip.c


Diff:

diff --git a/NEWS b/NEWS
index 9cc94c63eaf2..99de3bd4b2a7 100644
--- a/NEWS
+++ b/NEWS
@@ -69,6 +69,10 @@ PHP                                                                        NEWS
   . Fixed out-of-bounds write when shm_attach() opens an existing segment with
     a size larger than the segment actually is. (David Carlier)
 
+- Zip:
+  . Fixed ZipArchive::addGlob() and ZipArchive::addPattern() ignoring their
+    default options when no options array is given. (David Carlier)
+
 30 Jul 2026, PHP 8.4.24
 
 - BCMath:
diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c
index ccc474bc715c..fecb9396ace9 100644
--- a/ext/zip/php_zip.c
+++ b/ext/zip/php_zip.c
@@ -64,6 +64,12 @@ static int le_zip_entry;
 	}
 /* }}} */
 
+#ifdef HAVE_ENCRYPTION
+#define PHP_ZIP_DEFAULT_OPTIONS { .enc_method = -1, .comp_method = -1, .flags = ZIP_FL_OVERWRITE }
+#else
+#define PHP_ZIP_DEFAULT_OPTIONS { .comp_method = -1, .flags = ZIP_FL_OVERWRITE }
+#endif
+
 /* {{{ php_zip_set_file_comment */
 static bool php_zip_set_file_comment(struct zip *za, zip_uint64_t index, const char *comment, size_t comment_len)
 {
@@ -373,13 +379,6 @@ static int php_zip_parse_options(HashTable *options, zip_options *opts)
 {
 	zval *option;
 
-	/* default values */
-	opts->flags = ZIP_FL_OVERWRITE;
-	opts->comp_method = -1; /* -1 to not change default */
-#ifdef HAVE_ENCRYPTION
-	opts->enc_method = -1;  /* -1 to not change default */
-#endif
-
 	if ((option = zend_hash_str_find(options, "remove_all_path", sizeof("remove_all_path") - 1)) != NULL) {
 		if (Z_TYPE_P(option) != IS_FALSE && Z_TYPE_P(option) != IS_TRUE) {
 			php_error_docref(NULL, E_WARNING, "Option \"remove_all_path\" must be of type bool, %s given",
@@ -1757,7 +1756,7 @@ static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /*
 	size_t  path_len = 1;
 	zend_long glob_flags = 0;
 	HashTable *options = NULL;
-	zip_options opts = {0};
+	zip_options opts = PHP_ZIP_DEFAULT_OPTIONS;
 	int found;
 	zend_string *pattern;
 
diff --git a/ext/zip/tests/addGlob_default_options.phpt b/ext/zip/tests/addGlob_default_options.phpt
new file mode 100644
index 000000000000..f1951e849e07
--- /dev/null
+++ b/ext/zip/tests/addGlob_default_options.phpt
@@ -0,0 +1,79 @@
+--TEST--
+ZipArchive::addGlob() uses the default options when none are supplied
+--EXTENSIONS--
+zip
+--FILE--
+<?php
+$dir = __DIR__ . '/addglob_default_options_dir';
+@mkdir($dir);
+$src = $dir . '/data.txt';
+file_put_contents($src, str_repeat('The quick brown fox. ', 3000));
+$archive = $dir . '/test.zip';
+
+function stat_first(string $archive): array
+{
+    $zip = new ZipArchive();
+    $zip->open($archive);
+    $sb = $zip->statIndex(0);
+    $zip->close();
+
+    return $sb;
+}
+
+function add_glob(string $archive, string $pattern, ?array $options): bool
+{
+    $zip = new ZipArchive();
+    $zip->open($archive, ZipArchive::CREATE | ZipArchive::OVERWRITE);
+    $added = $options === null
+        ? $zip->addGlob($pattern)
+        : $zip->addGlob($pattern, 0, $options);
+    $zip->close();
+
+    return is_array($added);
+}
+
+/* comp_method defaults to "leave it to libzip", i.e. deflate, not CM_STORE. */
+foreach (['no options' => null, 'empty options' => []] as $label => $options) {
+    echo "-- $label --", PHP_EOL;
+    @unlink($archive);
+    var_dump(add_glob($archive, $dir . '/*.txt', $options));
+    $sb = stat_first($archive);
+    var_dump($sb['comp_method'] === ZipArchive::CM_DEFLATE);
+    var_dump($sb['comp_size'] < $sb['size']);
+}
+
+/* flags defaults to FL_OVERWRITE, so an existing entry is replaced. */
+echo '-- overwrites an existing entry --', PHP_EOL;
+@unlink($archive);
+$zip = new ZipArchive();
+$zip->open($archive, ZipArchive::CREATE | ZipArchive::OVERWRITE);
+$zip->addFromString($src, 'placeholder');
+var_dump(is_array($zip->addGlob($dir . '/*.txt')));
+$zip->close();
+
+$zip = new ZipArchive();
+$zip->open($archive);
+var_dump($zip->numFiles);
+var_dump($zip->getFromName($src) === file_get_contents($src));
+$zip->close();
+?>
+--CLEAN--
+<?php
+$dir = __DIR__ . '/addglob_default_options_dir';
+@unlink($dir . '/test.zip');
+@unlink($dir . '/data.txt');
+@rmdir($dir);
+?>
+--EXPECT--
+-- no options --
+bool(true)
+bool(true)
+bool(true)
+-- empty options --
+bool(true)
+bool(true)
+bool(true)
+-- overwrites an existing entry --
+bool(true)
+int(1)
+bool(true)
diff --git a/ext/zip/tests/addPattern_default_options.phpt b/ext/zip/tests/addPattern_default_options.phpt
new file mode 100644
index 000000000000..404e47e5291c
--- /dev/null
+++ b/ext/zip/tests/addPattern_default_options.phpt
@@ -0,0 +1,79 @@
+--TEST--
+ZipArchive::addPattern() uses the default options when none are supplied
+--EXTENSIONS--
+zip
+--FILE--
+<?php
+$dir = __DIR__ . DIRECTORY_SEPARATOR . 'addpattern_default_options_dir';
+@mkdir($dir);
+$src = $dir . DIRECTORY_SEPARATOR . 'data.txt';
+file_put_contents($src, str_repeat('The quick brown fox. ', 3000));
+$archive = $dir . DIRECTORY_SEPARATOR . 'test.zip';
+
+function stat_first(string $archive): array
+{
+    $zip = new ZipArchive();
+    $zip->open($archive);
+    $sb = $zip->statIndex(0);
+    $zip->close();
+
+    return $sb;
+}
+
+function add_pattern(string $archive, string $pattern, string $path, ?array $options): bool
+{
+    $zip = new ZipArchive();
+    $zip->open($archive, ZipArchive::CREATE | ZipArchive::OVERWRITE);
+    $added = $options === null
+        ? $zip->addPattern($pattern, $path)
+        : $zip->addPattern($pattern, $path, $options);
+    $zip->close();
+
+    return is_array($added);
+}
+
+/* comp_method defaults to "leave it to libzip", i.e. deflate, not CM_STORE. */
+foreach (['no options' => null, 'empty options' => []] as $label => $options) {
+    echo "-- $label --", PHP_EOL;
+    @unlink($archive);
+    var_dump(add_pattern($archive, '/^data\.txt$/', $dir, $options));
+    $sb = stat_first($archive);
+    var_dump($sb['comp_method'] === ZipArchive::CM_DEFLATE);
+    var_dump($sb['comp_size'] < $sb['size']);
+}
+
+/* flags defaults to FL_OVERWRITE, so an existing entry is replaced. */
+echo '-- overwrites an existing entry --', PHP_EOL;
+@unlink($archive);
+$zip = new ZipArchive();
+$zip->open($archive, ZipArchive::CREATE | ZipArchive::OVERWRITE);
+$zip->addFromString($src, 'placeholder');
+var_dump(is_array($zip->addPattern('/^data\.txt$/', $dir)));
+$zip->close();
+
+$zip = new ZipArchive();
+$zip->open($archive);
+var_dump($zip->numFiles);
+var_dump($zip->getFromIndex(0) === file_get_contents($src));
+$zip->close();
+?>
+--CLEAN--
+<?php
+$dir = __DIR__ . DIRECTORY_SEPARATOR . 'addpattern_default_options_dir';
+@unlink($dir . DIRECTORY_SEPARATOR . 'test.zip');
+@unlink($dir . DIRECTORY_SEPARATOR . 'data.txt');
+@rmdir($dir);
+?>
+--EXPECT--
+-- no options --
+bool(true)
+bool(true)
+bool(true)
+-- empty options --
+bool(true)
+bool(true)
+bool(true)
+-- overwrites an existing entry --
+bool(true)
+int(1)
+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.