diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 7af8a81e8f61..93004531dac2 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -2920,7 +2920,7 @@ PHP_METHOD(ZipArchive, extractTo) } } - uint32_t nelems, i; + uint32_t nelems; ZIP_FROM_OBJECT(intern, self); if (files_str) { @@ -2932,19 +2932,18 @@ PHP_METHOD(ZipArchive, extractTo) if (nelems == 0 ) { RETURN_FALSE; } - for (i = 0; i < nelems; i++) { - zval *zval_file; - if ((zval_file = zend_hash_index_find_deref(files_ht, i)) != NULL) { - if (Z_TYPE_P(zval_file) == IS_STRING) { - if (!php_zip_extract_file(intern, pathto, Z_STRVAL_P(zval_file), Z_STRLEN_P(zval_file), -1)) { - RETURN_FALSE; - } - } else { - zend_argument_type_error(2, "must only have elements of type string, %s given", zend_zval_value_name(zval_file)); - RETURN_THROWS(); + zval *zval_file; + ZEND_HASH_FOREACH_VAL(files_ht, zval_file) { + ZVAL_DEREF(zval_file); + if (Z_TYPE_P(zval_file) == IS_STRING) { + if (!php_zip_extract_file(intern, pathto, Z_STRVAL_P(zval_file), Z_STRLEN_P(zval_file), -1)) { + RETURN_FALSE; } + } else { + zend_argument_type_error(2, "must only have elements of type string, %s given", zend_zval_value_name(zval_file)); + RETURN_THROWS(); } - } + } ZEND_HASH_FOREACH_END(); } else { /* Extract all files */ zip_int64_t i, filecount = zip_get_num_entries(intern, 0); diff --git a/ext/zip/tests/oo_extract_array_keys.phpt b/ext/zip/tests/oo_extract_array_keys.phpt new file mode 100644 index 000000000000..c825bbefbbb0 --- /dev/null +++ b/ext/zip/tests/oo_extract_array_keys.phpt @@ -0,0 +1,38 @@ +--TEST-- +ZipArchive::extractTo() with a non-list files array (non-sequential keys) +--EXTENSIONS-- +zip +--FILE-- +open($archive, ZipArchive::CREATE | ZipArchive::OVERWRITE); +$zip->addFromString("file0.txt", "zero"); +$zip->addFromString("file1.txt", "one"); +$zip->close(); + +$target = __DIR__ . "/oo_extract_array_keys"; +mkdir($target); + +// array_filter() (and array_unique/array_diff) preserve keys, so this is [1 => "file1.txt"], +// a perfectly valid list of entry names that is not a packed 0-based array. +$files = array_filter(["file0.txt", "file1.txt"], fn($f) => $f === "file1.txt"); + +$zip = new ZipArchive(); +$zip->open($archive); +var_dump($zip->extractTo($target, $files)); +$zip->close(); + +var_dump(is_file("$target/file1.txt")); +?> +--EXPECT-- +bool(true) +bool(true) +--CLEAN-- +