diff --git a/src/wp-admin/includes/file.php b/src/wp-admin/includes/file.php index ef5a5025bef22..163ccea057fd8 100644 --- a/src/wp-admin/includes/file.php +++ b/src/wp-admin/includes/file.php @@ -1880,6 +1880,20 @@ function _unzip_file_ziparchive( $file, $to, $needed_dirs = array() ) { function _unzip_file_pclzip( $file, $to, $needed_dirs = array() ) { global $wp_filesystem; + /** + * Filters whether the zlib PHP extension is available for PclZip to extract archives. + * + * PclZip requires `gzopen()`, provided by the zlib extension. Without it, extraction + * cannot proceed. + * + * @since 7.2.0 + * + * @param bool $zlib_available Whether the zlib extension is available. Default is the result of `function_exists( 'gzopen' )`. + */ + if ( ! apply_filters( 'unzip_file_pclzip_zlib_available', function_exists( 'gzopen' ) ) ) { + return new WP_Error( 'unzip_file_missing_zlib', __( 'PHP does not have the zlib extension enabled, which is required to extract this archive.' ) ); + } + mbstring_binary_safe_encoding(); require_once ABSPATH . 'wp-admin/includes/class-pclzip.php'; diff --git a/tests/phpunit/tests/filesystem/unzipFilePclzip.php b/tests/phpunit/tests/filesystem/unzipFilePclzip.php index 10e4b1082a794..384bff901d261 100644 --- a/tests/phpunit/tests/filesystem/unzipFilePclzip.php +++ b/tests/phpunit/tests/filesystem/unzipFilePclzip.php @@ -83,4 +83,19 @@ public function test_should_apply_unzip_file_filters() { $this->assertSame( 1, $filter->get_call_count(), 'The filter should be called once.' ); $this->assertSame( self::$test_data_dir . 'archive.zip', $filter->get_args()[0][1], 'The $file parameter should be correct.' ); } + + /** + * Tests that _unzip_file_pclzip() returns a translatable WP_Error instead of + * fatal-erroring when the zlib extension is unavailable. + * + * @ticket 30963 + */ + public function test_should_return_wp_error_when_zlib_extension_is_unavailable() { + add_filter( 'unzip_file_pclzip_zlib_available', '__return_false' ); + + $result = _unzip_file_pclzip( self::$test_data_dir . 'archive.zip', self::$test_data_dir . 'archive/' ); + + $this->assertWPError( $result, 'A WP_Error should be returned instead of a fatal error.' ); + $this->assertSame( 'unzip_file_missing_zlib', $result->get_error_code(), 'The error code should identify the missing zlib extension.' ); + } }