WordPress Version: 6.4
/**
* Attempts to unzip an archive using the PclZip library.
*
* This function should not be called directly, use `unzip_file()` instead.
*
* Assumes that WP_Filesystem() has already been called and set up.
*
* @since 3.0.0
* @access private
*
* @see unzip_file()
*
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
*
* @param string $file Full path and filename of ZIP archive.
* @param string $to Full path on the filesystem to extract archive to.
* @param string[] $needed_dirs A partial list of required folders needed to be created.
* @return true|WP_Error True on success, WP_Error on failure.
*/
function _unzip_file_pclzip($file, $to, $needed_dirs = array())
{
global $wp_filesystem;
mbstring_binary_safe_encoding();
require_once ABSPATH . 'wp-admin/includes/class-pclzip.php';
$archive = new PclZip($file);
$archive_files = $archive->extract(PCLZIP_OPT_EXTRACT_AS_STRING);
reset_mbstring_encoding();
// Is the archive valid?
if (!is_array($archive_files)) {
return new WP_Error('incompatible_archive', __('Incompatible Archive.'), $archive->errorInfo(true));
}
if (0 === count($archive_files)) {
return new WP_Error('empty_archive_pclzip', __('Empty archive.'));
}
$uncompressed_size = 0;
// Determine any children directories needed (From within the archive).
foreach ($archive_files as $file) {
if (str_starts_with($file['filename'], '__MACOSX/')) {
// Skip the OS X-created __MACOSX directory.
continue;
}
$uncompressed_size += $file['size'];
$needed_dirs[] = $to . untrailingslashit($file['folder'] ? $file['filename'] : dirname($file['filename']));
}
// Enough space to unzip the file and copy its contents, with a 10% buffer.
$required_space = $uncompressed_size * 2.1;
/*
* disk_free_space() could return false. Assume that any falsey value is an error.
* A disk that has zero free bytes has bigger problems.
* Require we have enough space to unzip the file and copy its contents, with a 10% buffer.
*/
if (wp_doing_cron()) {
$available_space = function_exists('disk_free_space') ? @disk_free_space(WP_CONTENT_DIR) : false;
if ($available_space && $required_space > $available_space) {
return new WP_Error('disk_full_unzip_file', __('Could not copy files. You may have run out of disk space.'), compact('uncompressed_size', 'available_space'));
}
}
$needed_dirs = array_unique($needed_dirs);
foreach ($needed_dirs as $dir) {
// Check the parent folders of the folders all exist within the creation array.
if (untrailingslashit($to) === $dir) {
// Skip over the working directory, we know this exists (or will exist).
continue;
}
if (!str_contains($dir, $to)) {
// If the directory is not within the working directory, skip it.
continue;
}
$parent_folder = dirname($dir);
while (!empty($parent_folder) && untrailingslashit($to) !== $parent_folder && !in_array($parent_folder, $needed_dirs, true)) {
$needed_dirs[] = $parent_folder;
$parent_folder = dirname($parent_folder);
}
}
asort($needed_dirs);
// Create those directories if need be:
foreach ($needed_dirs as $_dir) {
// Only check to see if the dir exists upon creation failure. Less I/O this way.
if (!$wp_filesystem->mkdir($_dir, FS_CHMOD_DIR) && !$wp_filesystem->is_dir($_dir)) {
return new WP_Error('mkdir_failed_pclzip', __('Could not create directory.'), $_dir);
}
}
/** This filter is documented in src/wp-admin/includes/file.php */
$pre = apply_filters('pre_unzip_file', null, $file, $to, $needed_dirs, $required_space);
if (null !== $pre) {
return $pre;
}
// Extract the files from the zip.
foreach ($archive_files as $file) {
if ($file['folder']) {
continue;
}
if (str_starts_with($file['filename'], '__MACOSX/')) {
// Don't extract the OS X-created __MACOSX directory files.
continue;
}
// Don't extract invalid files:
if (0 !== validate_file($file['filename'])) {
continue;
}
if (!$wp_filesystem->put_contents($to . $file['filename'], $file['content'], FS_CHMOD_FILE)) {
return new WP_Error('copy_failed_pclzip', __('Could not copy file.'), $file['filename']);
}
}
/** This action is documented in src/wp-admin/includes/file.php */
$result = apply_filters('unzip_file', true, $file, $to, $needed_dirs, $required_space);
unset($needed_dirs);
return $result;
}