Commit 201e3cc3d20 for php.net

commit 201e3cc3d20d947ad38ebf6c3fddfee9c957a720
Author: David Carlier <devnexen@gmail.com>
Date:   Sun Nov 16 15:47:32 2025 +0000

    ext/zip: stricter numeric handling for archive array options.

    Use zval_try_get_long() for the numeric entries of the options array
    (comp_method, comp_flags, enc_method, flags) so numeric strings are
    coerced and non-numeric values raise a TypeError instead of a warning.
    remove_all_path is now strictly bool. php_zip_status()/_sys() return
    values are widened to zend_long.

    close GH-20507

diff --git a/UPGRADING b/UPGRADING
index a6cd3bdef1c..031b6751d85 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -209,6 +209,10 @@ PHP 8.6 UPGRADE NOTES
 - Zip:
   . ZipArchive::extractTo now raises a TypeError for the files argument if one
     or more of the entries is not a string.
+  . ZipArchive::addGlob() and ZipArchive::addPattern() now raise a TypeError
+    when the "remove_all_path" option is not of type bool, or when the
+    "comp_method", "comp_flags", or "enc_method" options are not of type int
+    (numeric strings are coerced), instead of emitting a warning.

 - Zlib:
   . deflate_init() now raises a TypeError when the value for option "level",
diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c
index 4156a8deb86..1301c6fc6b1 100644
--- a/ext/zip/php_zip.c
+++ b/ext/zip/php_zip.c
@@ -360,6 +360,8 @@ static zend_result php_zip_parse_options(HashTable *options, zip_options *opts)
 /* {{{ */
 {
 	zval *option;
+	zend_long tmp;
+	bool failed = false;

 	/* default values */
 	opts->flags = ZIP_FL_OVERWRITE;
@@ -370,43 +372,45 @@ static zend_result php_zip_parse_options(HashTable *options, zip_options *opts)

 	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",
-				zend_zval_value_name(option));
+			zend_type_error("Option \"remove_all_path\" must be of type bool, %s given", zend_zval_value_name(option));
+			return FAILURE;
 		}
-		opts->remove_all_path = zval_get_long(option);
+		opts->remove_all_path = Z_TYPE_P(option) == IS_TRUE;
 	}

 	if ((option = zend_hash_str_find(options, "comp_method", sizeof("comp_method") - 1)) != NULL) {
-		if (Z_TYPE_P(option) != IS_LONG) {
-			php_error_docref(NULL, E_WARNING, "Option \"comp_method\" must be of type int, %s given",
-				zend_zval_value_name(option));
+		tmp = zval_try_get_long(option, &failed);
+		if (failed) {
+			zend_type_error("Option \"comp_method\" must be of type int, %s given", zend_zval_value_name(option));
+			return FAILURE;
 		}
-		zend_long comp_method = zval_get_long(option);
+		zend_long comp_method = tmp;
 		if (comp_method < 0 || comp_method > INT_MAX) {
 			php_error_docref(NULL, E_WARNING, "Option \"comp_method\" must be between 0 and %d", INT_MAX);
 		}
 		opts->comp_method = (zip_int32_t)comp_method;

 		if ((option = zend_hash_str_find(options, "comp_flags", sizeof("comp_flags") - 1)) != NULL) {
-			if (Z_TYPE_P(option) != IS_LONG) {
-				php_error_docref(NULL, E_WARNING, "Option \"comp_flags\" must be of type int, %s given",
-					zend_zval_value_name(option));
+			tmp = zval_try_get_long(option, &failed);
+			if (failed) {
+				zend_type_error("Option \"comp_flags\" must be of type int, %s given", zend_zval_value_name(option));
+				return FAILURE;
 			}
-			zend_long comp_flags = zval_get_long(option);
-			if (comp_flags < 0 || comp_flags > USHRT_MAX) {
+			if (tmp < 0 || tmp > USHRT_MAX) {
 				php_error_docref(NULL, E_WARNING, "Option \"comp_flags\" must be between 0 and %u", USHRT_MAX);
 			}
-			opts->comp_flags = (zip_uint32_t)comp_flags;
+			opts->comp_flags = (zip_uint32_t)tmp;
 		}
 	}

 #ifdef HAVE_ENCRYPTION
 	if ((option = zend_hash_str_find(options, "enc_method", sizeof("enc_method") - 1)) != NULL) {
-		if (Z_TYPE_P(option) != IS_LONG) {
-			php_error_docref(NULL, E_WARNING, "Option \"enc_method\" must be of type int, %s given",
-				zend_zval_value_name(option));
+		tmp = zval_try_get_long(option, &failed);
+		if (failed) {
+			zend_type_error("Option \"enc_method\" must be of type int, %s given", zend_zval_value_name(option));
+			return FAILURE;
 		}
-		opts->enc_method = zval_get_long(option);
+		opts->enc_method = tmp;

 		if ((option = zend_hash_str_find(options, "enc_password", sizeof("enc_password") - 1)) != NULL) {
 			if (Z_TYPE_P(option) != IS_STRING) {
@@ -460,12 +464,13 @@ static zend_result php_zip_parse_options(HashTable *options, zip_options *opts)
 	}

 	if ((option = zend_hash_str_find(options, "flags", sizeof("flags") - 1)) != NULL) {
-		if (Z_TYPE_P(option) != IS_LONG) {
+		tmp = zval_try_get_long(option, &failed);
+		if (failed) {
 			zend_type_error("Option \"flags\" must be of type int, %s given",
 				zend_zval_value_name(option));
 			return FAILURE;
 		}
-		opts->flags = Z_LVAL_P(option);
+		opts->flags = tmp;
 	}

 	return SUCCESS;
@@ -515,13 +520,13 @@ static zend_result php_zip_parse_options(HashTable *options, zip_options *opts)

 static zend_long php_zip_status(ze_zip_object *obj) /* {{{ */
 {
-	int zep = obj->err_zip; /* saved err if closed */
+	zend_long zep = (zend_long)obj->err_zip; /* saved err if closed */

 	if (obj->za) {
 		zip_error_t *err;

 		err = zip_get_error(obj->za);
-		zep = zip_error_code_zip(err);
+		zep = (zend_long)zip_error_code_zip(err);
 		zip_error_fini(err);
 	}
 	return zep;
@@ -536,13 +541,13 @@ static zend_long php_zip_last_id(ze_zip_object *obj) /* {{{ */

 static zend_long php_zip_status_sys(ze_zip_object *obj) /* {{{ */
 {
-	int syp = obj->err_sys;  /* saved err if closed */
+	zend_long syp = (zend_long)obj->err_sys;  /* saved err if closed */

 	if (obj->za) {
 		zip_error_t *err;

 		err = zip_get_error(obj->za);
-		syp = zip_error_code_system(err);
+		syp = (zend_long)zip_error_code_system(err);
 		zip_error_fini(err);
 	}
 	return syp;