Files
2025-02-24 22:33:42 +01:00

666 lines
31 KiB
PHP

<?php
/**
* Class to create a zip file using PHP ZipArchive
*
* Standard: PSR-2 (almost)
*
* @link http://www.php-fig.org/psr/psr-2
*
* @package DUP_PRO
* @subpackage classes/package
* @copyright (c) 2017, Snapcreek LLC
* @license https://opensource.org/licenses/GPL-3.0 GNU Public License
* @since 1.0.0
*
* @notes: Trace process time
* $timer01 = DUP_PRO_U::getMicrotime();
* DUP_PRO_Log::trace("SCAN TIME-B = " . DUP_PRO_U::elapsedTime(DUP_PRO_U::getMicrotime(), $timer01));
*/
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
use Duplicator\Libs\Snap\SnapIO;
use Duplicator\Models\SystemGlobalEntity;
use Duplicator\Package\Create\BuildProgress;
use Duplicator\Utils\ZipArchiveExtended;
class DUP_PRO_ZipArchive
{
/** @var DUP_PRO_Global_Entity */
private $global = null;
/** @var bool */
private $optMaxBuildTimeOn = true;
/** @var int */
private $maxBuildTimeFileSize = 100000;
/** @var int */
private $throttleDelayInUs = 0;
/** @var DUP_PRO_Package */
private $package = null;
/** @var ZipArchiveExtended */
private $zipArchive = null;
/**
* Class constructor
*
* @param DUP_PRO_Package $package The package to create the zip file for
*/
public function __construct(DUP_PRO_Package $package)
{
$this->global = DUP_PRO_Global_Entity::getInstance();
$this->optMaxBuildTimeOn = ($this->global->max_package_runtime_in_min > 0);
$this->throttleDelayInUs = $this->global->getMicrosecLoadReduction();
$this->package = $package;
$this->zipArchive = new ZipArchiveExtended($this->package->StorePath . '/' . $this->package->Archive->File);
$password = $this->package->Archive->getArchivePassword();
if (strlen($password) > 0) {
$this->zipArchive->setEncrypt(true, $password);
}
}
/**
* Creates the zip file and adds the SQL file to the archive
*
* @param BuildProgress $build_progress A copy of the current build progress
*
* @return bool Returns true if the process was successful
*/
public function create(BuildProgress $build_progress)
{
try {
if (!ZipArchiveExtended::isPhpZipAvailable()) {
DUP_PRO_Log::trace("Zip archive doesn't exist?");
return false;
}
$this->package->safe_tmp_cleanup(true);
if ($this->package->ziparchive_mode == DUP_PRO_ZipArchive_Mode::SingleThread) {
return $this->createSingleThreaded($build_progress);
} else {
return $this->createMultiThreaded($build_progress);
}
} catch (Exception $ex) {
DUP_PRO_Log::error("Runtime error in class-package-archive-zip.php.", "Exception: {$ex}");
return false;
}
}
/**
* Creates the zip file using a single thread approach
*
* @param BuildProgress $build_progress A copy of the current build progress
*
* @return bool Returns true if the process was successful
*/
private function createSingleThreaded(BuildProgress $build_progress)
{
$countFiles = 0;
$compressDir = rtrim(SnapIO::safePath($this->package->Archive->PackDir), '/');
$sqlPath = $this->package->StorePath . '/' . $this->package->Database->File;
$zipPath = $this->package->StorePath . '/' . $this->package->Archive->File;
$filterDirs = empty($this->package->Archive->FilterDirs) ? 'not set' : rtrim(str_replace(';', "\n\t", $this->package->Archive->FilterDirs));
$filterFiles = empty($this->package->Archive->FilterFiles) ? 'not set' : rtrim(str_replace(';', "\n\t", $this->package->Archive->FilterFiles));
$filterExts = empty($this->package->Archive->FilterExts) ? 'not set' : $this->package->Archive->FilterExts;
$filterOn = ($this->package->Archive->FilterOn) ? 'ON' : 'OFF';
$validation = ($this->global->ziparchive_validation) ? 'ON' : 'OFF';
$compression = $build_progress->current_build_compression ? 'ON' : 'OFF';
$this->zipArchive->setCompressed($build_progress->current_build_compression);
//PREVENT RETRIES PAST 3: Default is 10 (DUP_PRO_Constants::MAX_BUILD_RETRIES)
//since this is ST Mode no reason to keep trying like MT
if ($build_progress->retries >= 3) {
$err = __('Package build appears stuck so marking package as failed. Is the PHP or Web Server timeouts too low?', 'duplicator-pro');
DUP_PRO_Log::error(__('Build Failure', 'duplicator-pro'), $err, false);
DUP_PRO_Log::trace($err);
return $build_progress->failed = true;
} else {
if ($build_progress->retries > 0) {
DUP_PRO_Log::infoTrace("**NOTICE: Retry count at: {$build_progress->retries}");
}
$build_progress->retries++;
$this->package->update();
}
//LOAD SCAN REPORT
try {
$scanReport = $this->package->getScanReportFromJson(DUPLICATOR_PRO_SSDIR_PATH_TMP . "/{$this->package->NameHash}_scan.json");
} catch (DUP_PRO_NoScanFileException $ex) {
DUP_PRO_Log::trace("**** scan file doesn't exist!!");
DUP_PRO_Log::error($ex->getMessage(), '', false);
$build_progress->failed = true;
return true;
} catch (DUP_PRO_NoFileListException $ex) {
DUP_PRO_Log::trace("**** list of files doesn't exist!!");
DUP_PRO_Log::error($ex->getMessage(), '', false);
$build_progress->failed = true;
return true;
} catch (DUP_PRO_NoDirListException $ex) {
DUP_PRO_Log::trace("**** list of directories doesn't exist!!");
DUP_PRO_Log::error($ex->getMessage(), '', false);
$build_progress->failed = true;
return true;
}
//============================================
//ST: START ZIP
//============================================
if ($build_progress->archive_started === false) {
DUP_PRO_Log::info("\n********************************************************************************");
DUP_PRO_Log::info("ARCHIVE ZipArchive Single-Threaded");
DUP_PRO_Log::info("********************************************************************************");
DUP_PRO_Log::info("ARCHIVE DIR: " . $compressDir);
DUP_PRO_Log::info("ARCHIVE FILE: " . basename($zipPath));
DUP_PRO_Log::info("COMPRESSION: *{$compression}*");
DUP_PRO_Log::info("VALIDATION: *{$validation}*");
DUP_PRO_Log::info("FILTERS: *{$filterOn}*");
DUP_PRO_Log::info("DIRS:\t{$filterDirs}");
DUP_PRO_Log::info("EXTS: {$filterExts}");
DUP_PRO_Log::info("FILES: {$filterFiles}");
DUP_PRO_Log::info("----------------------------------------");
DUP_PRO_Log::info("COMPRESSING");
DUP_PRO_Log::info("SIZE:\t" . $scanReport->ARC->Size);
DUP_PRO_Log::info("STATS:\tDirs " . $scanReport->ARC->DirCount . " | Files " . $scanReport->ARC->FileCount . " | Total " . $scanReport->ARC->FullCount);
if (($scanReport->ARC->DirCount == '') || ($scanReport->ARC->FileCount == '') || ($scanReport->ARC->FullCount == '')) {
DUP_PRO_Log::error('Invalid Scan Report Detected', 'Invalid Scan Report Detected', false);
return $build_progress->failed = true;
}
$build_progress->archive_started = true;
$build_progress->archive_start_time = DUP_PRO_U::getMicrotime();
}
//============================================
//ST: ADD DATABASE FILE
//============================================
if ($build_progress->archive_has_database === false) {
if (!$this->zipArchive->open()) {
DUP_PRO_Log::error("Couldn't open $zipPath", '', false);
return $build_progress->failed = true;
}
if ($this->zipArchive->addFile($sqlPath, $this->package->get_sql_ark_file_path())) {
DUP_PRO_Log::info("SQL ADDED: " . basename($sqlPath));
} else {
DUP_PRO_Log::error("Unable to add database.sql to archive.", "SQL File Path [" . $sqlPath . "]", false);
return $build_progress->failed = true;
}
if ($this->zipArchive->close()) {
$build_progress->archive_has_database = true;
$this->package->update();
} else {
$err = 'ZipArchive close failure during database.sql phase.';
$this->setDupArchiveSwitchFix($err);
return $build_progress->failed = true;
}
}
//============================================
//ST: ZIP DIRECTORIES
//Keep this loop tight: ZipArchive can handle over 10k+ dir entries in under 0.01 seconds.
//Its really fast without files so no need to do status pushes or other checks in loop
//============================================
if ($build_progress->next_archive_dir_index < count($scanReport->ARC->Dirs)) {
if (!$this->zipArchive->open()) {
DUP_PRO_Log::error("Couldn't open $zipPath", '', false);
return $build_progress->failed = true;
}
foreach ($scanReport->ARC->Dirs as $dir) {
$emptyDir = $this->package->Archive->getLocalDirPath($dir);
DUP_PRO_Log::trace("ADD DIR TO ZIP: '{$emptyDir}'");
if (!$this->zipArchive->addEmptyDir($emptyDir)) {
if (empty($compressDir) || strpos($dir, rtrim($compressDir, '/')) != 0) {
DUP_PRO_Log::infoTrace("WARNING: Unable to zip directory: '{$dir}'");
}
}
$build_progress->next_archive_dir_index++;
}
if ($this->zipArchive->close()) {
$this->package->update();
} else {
$err = 'ZipArchive close failure during directory add phase.';
$this->setDupArchiveSwitchFix($err);
return $build_progress->failed = true;
}
}
//============================================
//ST: ZIP FILES
//============================================
if ($build_progress->archive_built === false) {
if ($this->zipArchive->open() === false) {
DUP_PRO_Log::error("Can not open zip file at: [{$zipPath}]", '', false);
return $build_progress->failed = true;
}
// Since we have to estimate progress in Single Thread mode
// set the status when we start archiving just like Shell Exec
$this->package->set_status(DUP_PRO_PackageStatus::ARCSTART);
$total_file_size = 0;
$total_file_count_trip = ($scanReport->ARC->UFileCount + 1000);
foreach ($scanReport->ARC->Files as $file) {
//NON-ASCII check
if (preg_match('/[^\x20-\x7f]/', $file)) {
if (!$this->isUTF8FileSafe($file)) {
continue;
}
}
if ($this->global->ziparchive_validation) {
if (!is_readable($file)) {
DUP_PRO_Log::infoTrace("NOTICE: File [{$file}] is unreadable!");
continue;
}
}
$local_name = $this->package->Archive->getLocalFilePath($file);
if (!$this->zipArchive->addFile($file, $local_name)) {
// Assumption is that we continue?? for some things this would be fatal others it would be ok - leave up to user
DUP_PRO_Log::info("WARNING: Unable to zip file: {$file}");
continue;
}
$total_file_size += filesize($file);
//ST: SERVER THROTTLE
if ($this->throttleDelayInUs !== 0) {
usleep($this->throttleDelayInUs);
}
//Prevent Overflow
if ($countFiles++ > $total_file_count_trip) {
DUP_PRO_Log::error("ZipArchive-ST: file loop overflow detected at {$countFiles}", '', false);
return $build_progress->failed = true;
}
}
//START ARCHIVE CLOSE
$total_file_size_easy = DUP_PRO_U::byteSize($total_file_size);
DUP_PRO_Log::trace("Doing final zip close after adding $total_file_size_easy ({$total_file_size})");
if ($this->zipArchive->close()) {
DUP_PRO_Log::trace("Final zip closed.");
$build_progress->next_archive_file_index = $countFiles;
$build_progress->archive_built = true;
$this->package->update();
} else {
if ($this->global->ziparchive_validation === false) {
$this->global->ziparchive_validation = true;
$this->global->save();
DUP_PRO_Log::infoTrace("**NOTICE: ZipArchive: validation mode enabled");
} else {
$err = 'ZipArchive close failure during file phase with file validation enabled';
$this->setDupArchiveSwitchFix($err);
return $build_progress->failed = true;
}
}
}
//============================================
//ST: LOG FINAL RESULTS
//============================================
if ($build_progress->archive_built) {
$timerAllEnd = DUP_PRO_U::getMicrotime();
$timerAllSum = DUP_PRO_U::elapsedTime($timerAllEnd, $build_progress->archive_start_time);
$zipFileSize = @filesize($zipPath);
DUP_PRO_Log::info("MEMORY STACK: " . DUP_PRO_Server::getPHPMemory());
DUP_PRO_Log::info("FINAL SIZE: " . DUP_PRO_U::byteSize($zipFileSize));
DUP_PRO_Log::info("ARCHIVE RUNTIME: {$timerAllSum}");
if ($this->zipArchive->open()) {
$this->package->Archive->file_count = $this->zipArchive->getNumFiles();
$this->package->update();
$this->zipArchive->close();
} else {
DUP_PRO_Log::error("ZipArchive open failure.", "Encountered when retrieving final archive file count.", false);
return $build_progress->failed = true;
}
}
return true;
}
/**
* Creates the zip file using a multi-thread approach
*
* @param BuildProgress $build_progress A copy of the current build progress
*
* @return bool Returns true if the process was successful
*/
private function createMultiThreaded(BuildProgress $build_progress)
{
$timed_out = false;
$countFiles = 0;
$compressDir = rtrim(SnapIO::safePath($this->package->Archive->PackDir), '/');
$sqlPath = $this->package->StorePath . '/' . $this->package->Database->File;
$zipPath = $this->package->StorePath . '/' . $this->package->Archive->File;
$filterDirs = empty($this->package->Archive->FilterDirs) ? 'not set' : rtrim(str_replace(';', "\n\t", $this->package->Archive->FilterDirs));
$filterFiles = empty($this->package->Archive->FilterFiles) ? 'not set' : rtrim(str_replace(';', "\n\t", $this->package->Archive->FilterFiles));
$filterExts = empty($this->package->Archive->FilterExts) ? 'not set' : $this->package->Archive->FilterExts;
$filterOn = ($this->package->Archive->FilterOn) ? 'ON' : 'OFF';
$compression = $build_progress->current_build_compression ? 'ON' : 'OFF';
$this->zipArchive->setCompressed($build_progress->current_build_compression);
$scanFilepath = DUPLICATOR_PRO_SSDIR_PATH_TMP . "/{$this->package->NameHash}_scan.json";
//LOAD SCAN REPORT
try {
$scanReport = $this->package->getScanReportFromJson($scanFilepath);
} catch (DUP_PRO_NoScanFileException $ex) {
DUP_PRO_Log::trace("**** scan file $scanFilepath doesn't exist!!");
DUP_PRO_Log::error($ex->getMessage(), '', false);
$build_progress->failed = true;
return true;
} catch (DUP_PRO_NoFileListException $ex) {
DUP_PRO_Log::trace("**** list of files doesn't exist!!");
DUP_PRO_Log::error($ex->getMessage(), '', false);
$build_progress->failed = true;
return true;
} catch (DUP_PRO_NoDirListException $ex) {
DUP_PRO_Log::trace("**** list of directories doesn't exist!!");
DUP_PRO_Log::error($ex->getMessage(), '', false);
$build_progress->failed = true;
return true;
}
//============================================
//MT: START ZIP & ADD SQL FILE
//============================================
if ($build_progress->archive_started === false) {
DUP_PRO_Log::info("\n********************************************************************************");
DUP_PRO_Log::info("ARCHIVE Mode:ZipArchive Multi-Threaded");
DUP_PRO_Log::info("********************************************************************************");
DUP_PRO_Log::info("ARCHIVE DIR: " . $compressDir);
DUP_PRO_Log::info("ARCHIVE FILE: " . basename($zipPath));
DUP_PRO_Log::info("COMPRESSION: *{$compression}*");
DUP_PRO_Log::info("FILTERS: *{$filterOn}*");
DUP_PRO_Log::info("DIRS: {$filterDirs}");
DUP_PRO_Log::info("EXTS: {$filterExts}");
DUP_PRO_Log::info("FILES: {$filterFiles}");
DUP_PRO_Log::info("----------------------------------------");
DUP_PRO_Log::info("COMPRESSING");
DUP_PRO_Log::info("SIZE:\t" . $scanReport->ARC->Size);
DUP_PRO_Log::info("STATS:\tDirs " . $scanReport->ARC->DirCount . " | Files " . $scanReport->ARC->FileCount . " | Total " . $scanReport->ARC->FullCount);
if (($scanReport->ARC->DirCount == '') || ($scanReport->ARC->FileCount == '') || ($scanReport->ARC->FullCount == '')) {
DUP_PRO_Log::error('Invalid Scan Report Detected', 'Invalid Scan Report Detected', false);
return $build_progress->failed = true;
}
if (!$this->zipArchive->open()) {
DUP_PRO_Log::error("Couldn't open $zipPath", '', false);
return $build_progress->failed = true;
}
if ($this->zipArchive->addFile($sqlPath, $this->package->get_sql_ark_file_path())) {
DUP_PRO_Log::info("SQL ADDED: " . basename($sqlPath));
} else {
DUP_PRO_Log::error("Unable to add database.sql to archive.", "SQL File Path [" . $sqlPath . "]", false);
return $build_progress->failed = true;
}
if ($this->zipArchive->close()) {
$build_progress->archive_has_database = true;
$this->package->update();
} else {
$err = 'ZipArchive close failure during database.sql phase.';
$this->setDupArchiveSwitchFix($err);
return $build_progress->failed = true;
}
}
//============================================
//MT: ZIP DIRECTORIES
//Keep this loop tight: ZipArchive can handle over 10k dir entries in under 0.01 seconds.
//Its really fast without files no need to do status pushes or other checks in loop
//============================================
if ($this->zipArchive->open()) {
foreach ($scanReport->ARC->Dirs as $dir) {
$emptyDir = $this->package->Archive->getLocalDirPath($dir);
DUP_PRO_Log::trace("ADD DIR TO ZIP: '{$emptyDir}'");
if (!$this->zipArchive->addEmptyDir($emptyDir)) {
if (empty($compressDir) || strpos($dir, rtrim($compressDir, '/')) != 0) {
DUP_PRO_Log::infoTrace("WARNING: Unable to zip directory: '{$dir}'");
}
}
$build_progress->next_archive_dir_index++;
}
$this->package->update();
if ($build_progress->timedOut($this->global->php_max_worker_time_in_sec)) {
$timed_out = true;
$diff = time() - $build_progress->thread_start_time;
DUP_PRO_Log::trace("Timed out after hitting thread time of $diff {$this->global->php_max_worker_time_in_sec} so quitting zipping early in the directory phase");
}
} else {
DUP_PRO_Log::error("Couldn't open $zipPath", '', false);
return $build_progress->failed = true;
}
if ($this->zipArchive->close() === false) {
$err = __('ZipArchive close failure during directory add phase.', 'duplicator-pro');
$this->setDupArchiveSwitchFix($err);
return $build_progress->failed = true;
}
//============================================
//MT: ZIP FILES
//============================================
if ($timed_out === false) {
// PREVENT RETRIES (10x)
if ($build_progress->retries > DUP_PRO_Constants::MAX_BUILD_RETRIES) {
$err = __('Zip build appears stuck.', 'duplicator-pro');
$this->setDupArchiveSwitchFix($err);
$error_msg = __('Package build appears stuck so marking package failed. Recommend setting Settings > Packages > Archive Engine to DupArchive', 'duplicator-pro');
DUP_PRO_Log::error(__('Build Failure', 'duplicator-pro'), $error_msg, false);
DUP_PRO_Log::trace($error_msg);
return $build_progress->failed = true;
} else {
$build_progress->retries++;
$this->package->update();
}
$zip_is_open = false;
$total_file_size = 0;
$incremental_file_size = 0;
$used_zip_file_descriptor_count = 0;
$total_file_count = empty($scanReport->ARC->UFileCount) ? 0 : $scanReport->ARC->UFileCount;
foreach ($scanReport->ARC->Files as $file) {
if ($zip_is_open || ($countFiles == $build_progress->next_archive_file_index)) {
if ($zip_is_open === false) {
DUP_PRO_Log::trace("resuming archive building at file # $countFiles");
if ($this->zipArchive->open() !== true) {
DUP_PRO_Log::error("Couldn't open $zipPath", '', false);
$build_progress->failed = true;
return true;
}
$zip_is_open = true;
}
//NON-ASCII check
if (preg_match('/[^\x20-\x7f]/', $file)) {
if (!$this->isUTF8FileSafe($file)) {
continue;
}
} elseif (!file_exists($file)) {
DUP_PRO_Log::trace("NOTICE: ASCII file [{$file}] does not exist!");
continue;
}
$local_name = $this->package->Archive->getLocalFilePath($file);
$file_size = filesize($file);
$zip_status = $this->zipArchive->addFile($file, $local_name);
if ($zip_status) {
$total_file_size += $file_size;
$incremental_file_size += $file_size;
} else {
// Assumption is that we continue?? for some things this would be fatal others it would be ok - leave up to user
DUP_PRO_Log::info("WARNING: Unable to zip file: {$file}");
}
$countFiles++;
$chunk_size_in_bytes = $this->global->ziparchive_chunk_size_in_mb * 1000000;
if ($incremental_file_size > $chunk_size_in_bytes) {
// Only close because of chunk size and file descriptors when in legacy mode
DUP_PRO_Log::trace("closing zip because ziparchive mode = {$this->global->ziparchive_mode} fd count = $used_zip_file_descriptor_count or incremental file size=$incremental_file_size and chunk size = $chunk_size_in_bytes");
$incremental_file_size = 0;
$used_zip_file_descriptor_count = 0;
if ($this->zipArchive->close() == true) {
$adjusted_percent = floor(DUP_PRO_PackageStatus::ARCSTART + ((DUP_PRO_PackageStatus::ARCDONE - DUP_PRO_PackageStatus::ARCSTART) * ($countFiles / (float) $total_file_count)));
$build_progress->next_archive_file_index = $countFiles;
$build_progress->retries = 0;
$this->package->Status = $adjusted_percent;
$this->package->update();
$zip_is_open = false;
DUP_PRO_Log::trace("closed zip");
} else {
$err = 'ZipArchive close failure during file phase using multi-threaded setting.';
$this->setDupArchiveSwitchFix($err);
return $build_progress->failed = true;
}
}
//MT: SERVER THROTTLE
if ($this->throttleDelayInUs !== 0) {
usleep($this->throttleDelayInUs);
}
//MT: MAX WORKER TIME (SECS)
if ($build_progress->timedOut($this->global->php_max_worker_time_in_sec)) {
// Only close because of timeout
$timed_out = true;
$diff = time() - $build_progress->thread_start_time;
DUP_PRO_Log::trace("Timed out after hitting thread time of $diff so quitting zipping early in the file phase");
break;
}
//MT: MAX BUILD TIME (MINUTES)
//Only stop to check on larger files above 100K to avoid checking every single file
if ($file_size > $this->maxBuildTimeFileSize && $this->optMaxBuildTimeOn) {
$elapsed_sec = time() - $this->package->timer_start;
$elapsed_minutes = $elapsed_sec / 60;
if ($elapsed_minutes > $this->global->max_package_runtime_in_min) {
DUP_PRO_Log::trace("ZipArchive: Multi-thread max build time {$this->global->max_package_runtime_in_min} minutes reached killing process.");
return false;
}
}
} else {
$countFiles++;
}
}
DUP_PRO_Log::trace("total file size added to zip = $total_file_size");
if ($zip_is_open) {
DUP_PRO_Log::trace("Doing final zip close after adding $incremental_file_size");
if ($this->zipArchive->close()) {
DUP_PRO_Log::trace("Final zip closed.");
$build_progress->next_archive_file_index = $countFiles;
$build_progress->retries = 0;
$this->package->update();
} else {
$err = __('ZipArchive close failure.', 'duplicator-pro');
$this->setDupArchiveSwitchFix($err);
DUP_PRO_Log::error($err);
return $build_progress->failed = true;
}
}
}
//============================================
//MT: LOG FINAL RESULTS
//============================================
if ($timed_out === false) {
$build_progress->archive_built = true;
$build_progress->retries = 0;
$this->package->update();
$timerAllEnd = DUP_PRO_U::getMicrotime();
$timerAllSum = DUP_PRO_U::elapsedTime($timerAllEnd, $build_progress->archive_start_time);
$zipFileSize = @filesize($zipPath);
DUP_PRO_Log::info("COMPRESSED SIZE: " . DUP_PRO_U::byteSize($zipFileSize));
DUP_PRO_Log::info("ARCHIVE RUNTIME: {$timerAllSum}");
DUP_PRO_Log::info("MEMORY STACK: " . DUP_PRO_Server::getPHPMemory());
if ($this->zipArchive->open() === true) {
$this->package->Archive->file_count = $this->zipArchive->getNumFiles();
$this->package->update();
$this->zipArchive->close();
} else {
DUP_PRO_Log::error("ZipArchive open failure.", "Encountered when retrieving final archive file count.", false);
return $build_progress->failed = true;
}
}
return !$timed_out;
}
/**
* Encodes a UTF8 file and then determines if it is safe to add to an archive
*
* @param string $file The file to test
*
* @return bool Returns true if the file is readable and safe to add to archive
*/
private function isUTF8FileSafe($file)
{
$is_safe = true;
$original_file = $file;
DUP_PRO_Log::trace("[{$file}] is non ASCII");
// Necessary for adfron type files
if (DUP_PRO_STR::hasUTF8($file)) {
$file = utf8_decode($file);
}
if (file_exists($file) === false) {
if (file_exists($original_file) === false) {
DUP_PRO_Log::trace("$file CAN'T BE READ!");
DUP_PRO_Log::info("WARNING: Unable to zip file: {$file}. Cannot be read");
$is_safe = false;
}
}
return $is_safe;
}
/**
* Wrapper for switching to DupArchive quick fix
*
* @param string $message The error message
*
* @return void
*/
private function setDupArchiveSwitchFix($message)
{
$fix_text = __('Click to switch archive engine to DupArchive.', 'duplicator-pro');
$this->setFix(
$message,
$fix_text,
array(
'global' => array(
'archive_build_mode' => DUP_PRO_Archive_Build_Mode::DupArchive,
),
)
);
}
/**
* Sends an error to the trace and build logs and sets the UI message
*
* @param string $message The error message
* @param string $fix The details for how to fix the issue
* @param mixed[] $option The options to set
*
* @return void
*/
private function setFix($message, $fix, $option)
{
DUP_PRO_Log::trace($message);
DUP_PRO_Log::error("$message **FIX: $fix.", '', false);
$system_global = SystemGlobalEntity::getInstance();
$system_global->addQuickFix($message, $fix, $option);
}
}