forked from LiveCarta/LiveCartaWP
Changed source root directory
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* archive path file list object
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*
|
||||
* @package name
|
||||
* @copyright (c) 2019, Snapcreek LLC
|
||||
* @license https://opensource.org/licenses/GPL-3.0 GNU Public License
|
||||
*/
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapJson;
|
||||
|
||||
class DUP_Archive_File_List
|
||||
{
|
||||
protected $path = null;
|
||||
protected $handle = null;
|
||||
protected $cache = null;
|
||||
|
||||
public function __construct($path)
|
||||
{
|
||||
if (empty($path)) {
|
||||
throw new Exception('path can\'t be empty');
|
||||
}
|
||||
|
||||
$this->path = SnapIO::safePath($path);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->close();
|
||||
}
|
||||
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function open($truncate = false)
|
||||
{
|
||||
if (is_null($this->handle)) {
|
||||
if (($this->handle = fopen($this->path, 'a+')) === false) {
|
||||
DUP_Log::trace('Can\'t open ' . $this->path);
|
||||
$this->handle = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ($truncate) {
|
||||
$this->emptyFile();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function emptyFile()
|
||||
{
|
||||
if (!$this->open(false)) {
|
||||
return false;
|
||||
}
|
||||
if (($res = ftruncate($this->handle, 0)) === false) {
|
||||
DUP_Log::trace('Can\'t truncate file ' . $this->path);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
if (!is_null($this->handle)) {
|
||||
if (($res = @fclose($this->handle)) === false) {
|
||||
DUP_Log::trace('Can\'t close ' . $this->path);
|
||||
return false;
|
||||
}
|
||||
$this->handle = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function addEntry($path, $size, $nodes)
|
||||
{
|
||||
|
||||
if (is_null($this->handle)) { // check to generate less overhead
|
||||
if (!$this->open()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$entry = array('p' => $path, 's' => $size, 'n' => $nodes);
|
||||
fwrite($this->handle, SnapJson::jsonEncode($entry) . "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param bool $pathOnly if true return only payth
|
||||
*
|
||||
* @return boolean|array|string return false if is end of filer.
|
||||
*/
|
||||
public function getEntry($pathOnly = false)
|
||||
{
|
||||
if (is_null($this->handle)) { // check to generate less overhead
|
||||
if (!$this->open()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (($json = fgets($this->handle, 4196)) === false) {
|
||||
// end of file return false
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = json_decode($json, true);
|
||||
if ($pathOnly) {
|
||||
return $result['p'];
|
||||
} else {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
protected function cleanCache()
|
||||
{
|
||||
$this->cache = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function loadCache($refreshCache = false)
|
||||
{
|
||||
if ($refreshCache || is_null($this->cache)) {
|
||||
if (!$this->open()) {
|
||||
return false;
|
||||
}
|
||||
$this->cache = array();
|
||||
if (@fseek($this->handle, 0) === -1) {
|
||||
DUP_Log::trace('Can\'t seek at 0 pos for file ' . $this->path);
|
||||
$this->cleanCache();
|
||||
return false;
|
||||
}
|
||||
while (($entry = $this->getEntry()) !== false) {
|
||||
$this->cache[$entry['p']] = $entry;
|
||||
}
|
||||
if (!feof($this->handle)) {
|
||||
DUP_Log::trace('Error: unexpected fgets() fail', '', false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getEntryFromPath($path, $refreshCache = false)
|
||||
{
|
||||
if (!$this->loadCache($refreshCache)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (array_key_exists($path, $this->cache)) {
|
||||
return $this->cache[$path];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getEntriesFormPath($path, $refreshCache = false)
|
||||
{
|
||||
if (!$this->loadCache($refreshCache)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (array_key_exists($path, $this->cache)) {
|
||||
$result = array();
|
||||
foreach ($this->cache as $current => $entry) {
|
||||
if (preg_match('/^' . preg_quote($path, '/') . '\/[^\/]+$/', $current)) {
|
||||
$result[] = $entry;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getArrayPaths($pathPrefix = '')
|
||||
{
|
||||
if (!$this->open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = array();
|
||||
if (@fseek($this->handle, 0) === -1) {
|
||||
DUP_Log::trace('Can\'t seek at 0 pos for file ' . $this->path);
|
||||
return false;
|
||||
}
|
||||
$safePrefix = SnapIO::safePathUntrailingslashit($pathPrefix);
|
||||
while (($path = $this->getEntry(true)) !== false) {
|
||||
$result[] = $safePrefix . '/' . $path;
|
||||
}
|
||||
if (!feof($this->handle)) {
|
||||
DUP_Log::trace('Error: unexpected fgets() fail', '', false);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
/**
|
||||
* The base class for all filter types Directories/Files/Extentions
|
||||
*
|
||||
* @package Duplicator
|
||||
* @subpackage classes/package
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (! defined('DUPLICATOR_VERSION')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class DUP_Archive_Filter_Scope_Base
|
||||
{
|
||||
//All internal storage items that duplicator decides to filter
|
||||
public $Core = array();
|
||||
//Global filter items added from settings
|
||||
public $Global = array();
|
||||
//Items when creating a package or template that a user decides to filter
|
||||
public $Instance = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* The filter types that belong to directories
|
||||
*
|
||||
* @package Duplicator
|
||||
* @subpackage classes/package
|
||||
*/
|
||||
class DUP_Archive_Filter_Scope_Directory extends DUP_Archive_Filter_Scope_Base
|
||||
{
|
||||
//Items that are not readable
|
||||
public $Warning = array();
|
||||
|
||||
// Items that are not readable
|
||||
public $Unreadable = array();
|
||||
public $AddonSites = array();
|
||||
|
||||
// Items that exceed size threshold
|
||||
public $Size = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* The filter types that belong to files
|
||||
*
|
||||
* @package Duplicator
|
||||
* @subpackage classes/package
|
||||
*/
|
||||
class DUP_Archive_Filter_Scope_File extends DUP_Archive_Filter_Scope_Directory
|
||||
{
|
||||
//Items that are too large
|
||||
public $Size = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* The filter information object which store all information about the filtered
|
||||
* data that is gathered to the execution of a scan process
|
||||
*
|
||||
* @package Duplicator
|
||||
* @subpackage classes/package
|
||||
*/
|
||||
class DUP_Archive_Filter_Info
|
||||
{
|
||||
/** @var DUP_Archive_Filter_Scope_Directory Contains all folder filter info */
|
||||
public $Dirs = null;
|
||||
/** @var DUP_Archive_Filter_Scope_File Contains all file filter info */
|
||||
public $Files = null;
|
||||
/** @var DUP_Archive_Filter_Scope_Base Contains all extensions filter info */
|
||||
public $Exts = null;
|
||||
public $UDirCount = 0;
|
||||
public $UFileCount = 0;
|
||||
public $UExtCount = 0;
|
||||
public $TreeSize;
|
||||
public $TreeWarning;
|
||||
/**
|
||||
* Init this object
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* reset and clean all object
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->Dirs = new DUP_Archive_Filter_Scope_Directory();
|
||||
$this->Files = new DUP_Archive_Filter_Scope_File();
|
||||
$this->Exts = new DUP_Archive_Filter_Scope_Base();
|
||||
$this->TreeSize = array();
|
||||
$this->TreeWarning = array();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,251 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
// Exit if accessed directly
|
||||
if (! defined('DUPLICATOR_VERSION')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once DUPLICATOR_PLUGIN_PATH . 'classes/package/class.pack.archive.php';
|
||||
|
||||
/**
|
||||
* Creates a zip file using the built in PHP ZipArchive class
|
||||
*/
|
||||
class DUP_Zip extends DUP_Archive
|
||||
{
|
||||
//PRIVATE
|
||||
private static $compressDir;
|
||||
private static $countDirs = 0;
|
||||
private static $countFiles = 0;
|
||||
private static $sqlPath;
|
||||
private static $zipPath;
|
||||
private static $zipFileSize;
|
||||
private static $zipArchive;
|
||||
private static $limitItems = 0;
|
||||
private static $networkFlush = false;
|
||||
private static $scanReport;
|
||||
|
||||
/**
|
||||
* Creates the zip file and adds the SQL file to the archive
|
||||
*/
|
||||
public static function create(DUP_Archive $archive, $buildProgress)
|
||||
{
|
||||
try {
|
||||
$timerAllStart = DUP_Util::getMicrotime();
|
||||
$package_zip_flush = DUP_Settings::Get('package_zip_flush');
|
||||
|
||||
self::$compressDir = rtrim(wp_normalize_path(DUP_Util::safePath($archive->PackDir)), '/');
|
||||
self::$sqlPath = DUP_Settings::getSsdirTmpPath() . "/{$archive->Package->Database->File}";
|
||||
self::$zipPath = DUP_Settings::getSsdirTmpPath() . "/{$archive->File}";
|
||||
self::$zipArchive = new ZipArchive();
|
||||
self::$networkFlush = empty($package_zip_flush) ? false : $package_zip_flush;
|
||||
|
||||
$filterDirs = empty($archive->FilterDirs) ? 'not set' : $archive->FilterDirs;
|
||||
$filterExts = empty($archive->FilterExts) ? 'not set' : $archive->FilterExts;
|
||||
$filterFiles = empty($archive->FilterFiles) ? 'not set' : $archive->FilterFiles;
|
||||
$filterOn = ($archive->FilterOn) ? 'ON' : 'OFF';
|
||||
$filterDirsFormat = rtrim(str_replace(';', "\n\t", $filterDirs));
|
||||
$filterFilesFormat = rtrim(str_replace(';', "\n\t", $filterFiles));
|
||||
$lastDirSuccess = self::$compressDir;
|
||||
|
||||
//LOAD SCAN REPORT
|
||||
$json = file_get_contents(DUP_Settings::getSsdirTmpPath() . "/{$archive->Package->NameHash}_scan.json");
|
||||
self::$scanReport = json_decode($json);
|
||||
|
||||
DUP_Log::Info("\n********************************************************************************");
|
||||
DUP_Log::Info("ARCHIVE (ZIP):");
|
||||
DUP_Log::Info("********************************************************************************");
|
||||
$isZipOpen = (self::$zipArchive->open(self::$zipPath, ZIPARCHIVE::CREATE) === true);
|
||||
if (!$isZipOpen) {
|
||||
$error_message = "Cannot open zip file with PHP ZipArchive.";
|
||||
$buildProgress->set_failed($error_message);
|
||||
DUP_Log::error($error_message, "Path location [" . self::$zipPath . "]", Dup_ErrorBehavior::LogOnly);
|
||||
$archive->Package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return;
|
||||
}
|
||||
DUP_Log::Info("ARCHIVE DIR: " . self::$compressDir);
|
||||
DUP_Log::Info("ARCHIVE FILE: " . basename(self::$zipPath));
|
||||
DUP_Log::Info("FILTERS: *{$filterOn}*");
|
||||
DUP_Log::Info("DIRS:\n\t{$filterDirsFormat}");
|
||||
DUP_Log::Info("FILES:\n\t{$filterFilesFormat}");
|
||||
DUP_Log::Info("EXTS: {$filterExts}");
|
||||
|
||||
DUP_Log::Info("----------------------------------------");
|
||||
DUP_Log::Info("COMPRESSING");
|
||||
DUP_Log::Info("SIZE:\t" . self::$scanReport->ARC->Size);
|
||||
DUP_Log::Info("STATS:\tDirs " . self::$scanReport->ARC->DirCount . " | Files " . self::$scanReport->ARC->FileCount);
|
||||
|
||||
//ADD SQL
|
||||
$sql_ark_file_path = $archive->Package->getSqlArkFilePath();
|
||||
$isSQLInZip = self::$zipArchive->addFile(self::$sqlPath, $sql_ark_file_path);
|
||||
|
||||
if ($isSQLInZip) {
|
||||
DUP_Log::Info("SQL ADDED: " . basename(self::$sqlPath));
|
||||
} else {
|
||||
$error_message = "Unable to add database.sql to archive.";
|
||||
DUP_Log::error($error_message, "SQL File Path [" . self::$sqlPath . "]", Dup_ErrorBehavior::LogOnly);
|
||||
$buildProgress->set_failed($error_message);
|
||||
$archive->Package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return;
|
||||
}
|
||||
self::$zipArchive->close();
|
||||
self::$zipArchive->open(self::$zipPath, ZipArchive::CREATE);
|
||||
|
||||
//ZIP DIRECTORIES
|
||||
$info = '';
|
||||
foreach (self::$scanReport->ARC->Dirs as $dir) {
|
||||
$emptyDir = $archive->getLocalDirPath($dir);
|
||||
|
||||
if (is_readable($dir) && self::$zipArchive->addEmptyDir($emptyDir)) {
|
||||
self::$countDirs++;
|
||||
$lastDirSuccess = $dir;
|
||||
} else {
|
||||
//Don't warn when dirtory is the root path
|
||||
if (strcmp($dir, rtrim(self::$compressDir, '/')) != 0) {
|
||||
$dir_path = strlen($dir) ? "[{$dir}]" : "[Read Error] - last successful read was: [{$lastDirSuccess}]";
|
||||
$info .= "DIR: {$dir_path}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//LOG Unreadable DIR info
|
||||
if (strlen($info)) {
|
||||
DUP_Log::Info("\nWARNING: Unable to zip directories:");
|
||||
DUP_Log::Info($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* count update for integrity check
|
||||
*/
|
||||
$sumItems = (self::$countDirs + self::$countFiles);
|
||||
|
||||
/* ZIP FILES: Network Flush
|
||||
* This allows the process to not timeout on fcgi
|
||||
* setups that need a response every X seconds */
|
||||
$totalFileCount = count(self::$scanReport->ARC->Files);
|
||||
$info = '';
|
||||
if (self::$networkFlush) {
|
||||
foreach (self::$scanReport->ARC->Files as $file) {
|
||||
$file_size = @filesize($file);
|
||||
$localFileName = $archive->getLocalFilePath($file);
|
||||
|
||||
if (is_readable($file)) {
|
||||
if (
|
||||
defined('DUPLICATOR_ZIP_ARCHIVE_ADD_FROM_STR') &&
|
||||
DUPLICATOR_ZIP_ARCHIVE_ADD_FROM_STR &&
|
||||
$file_size < DUP_Constants::ZIP_STRING_LIMIT &&
|
||||
self::$zipArchive->addFromString($localFileName, file_get_contents($file))
|
||||
) {
|
||||
Dup_Log::Info("Adding {$file} to zip");
|
||||
self::$limitItems++;
|
||||
self::$countFiles++;
|
||||
} elseif (self::$zipArchive->addFile($file, $localFileName)) {
|
||||
Dup_Log::Info("Adding {$file} to zip");
|
||||
self::$limitItems++;
|
||||
self::$countFiles++;
|
||||
} else {
|
||||
$info .= "FILE: [{$file}]\n";
|
||||
}
|
||||
} else {
|
||||
$info .= "FILE: [{$file}]\n";
|
||||
}
|
||||
//Trigger a flush to the web server after so many files have been loaded.
|
||||
if (self::$limitItems > DUPLICATOR_ZIP_FLUSH_TRIGGER) {
|
||||
self::$zipArchive->close();
|
||||
self::$zipArchive->open(self::$zipPath);
|
||||
self::$limitItems = 0;
|
||||
DUP_Util::fcgiFlush();
|
||||
DUP_Log::Info("Items archived [{$sumItems}] flushing response.");
|
||||
}
|
||||
|
||||
if (self::$countFiles % 500 == 0) {
|
||||
// Every so many files update the status so the UI can display
|
||||
$archive->Package->Status = SnapUtil::getWorkPercent(DUP_PackageStatus::ARCSTART, DUP_PackageStatus::ARCVALIDATION, $totalFileCount, self::$countFiles);
|
||||
$archive->Package->update();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//Normal
|
||||
foreach (self::$scanReport->ARC->Files as $file) {
|
||||
$file_size = @filesize($file);
|
||||
$localFileName = $archive->getLocalFilePath($file);
|
||||
|
||||
if (is_readable($file)) {
|
||||
if (
|
||||
defined('DUPLICATOR_ZIP_ARCHIVE_ADD_FROM_STR') &&
|
||||
DUPLICATOR_ZIP_ARCHIVE_ADD_FROM_STR &&
|
||||
$file_size < DUP_Constants::ZIP_STRING_LIMIT &&
|
||||
self::$zipArchive->addFromString($localFileName, file_get_contents($file))
|
||||
) {
|
||||
self::$countFiles++;
|
||||
} elseif (self::$zipArchive->addFile($file, $localFileName)) {
|
||||
self::$countFiles++;
|
||||
} else {
|
||||
$info .= "FILE: [{$file}]\n";
|
||||
}
|
||||
} else {
|
||||
$info .= "FILE: [{$file}]\n";
|
||||
}
|
||||
|
||||
if (self::$countFiles % 500 == 0) {
|
||||
// Every so many files update the status so the UI can display
|
||||
$archive->Package->Status = SnapUtil::getWorkPercent(DUP_PackageStatus::ARCSTART, DUP_PackageStatus::ARCVALIDATION, $totalFileCount, self::$countFiles);
|
||||
$archive->Package->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//LOG Unreadable FILE info
|
||||
if (strlen($info)) {
|
||||
DUP_Log::Info("\nWARNING: Unable to zip files:");
|
||||
DUP_Log::Info($info);
|
||||
unset($info);
|
||||
}
|
||||
|
||||
DUP_Log::Info(print_r(self::$zipArchive, true));
|
||||
|
||||
/**
|
||||
* count update for integrity check
|
||||
*/
|
||||
$archive->file_count = self::$countDirs + self::$countFiles;
|
||||
DUP_Log::Info("FILE ADDED TO ZIP: " . $archive->file_count);
|
||||
|
||||
|
||||
//--------------------------------
|
||||
//LOG FINAL RESULTS
|
||||
DUP_Util::fcgiFlush();
|
||||
$zipCloseResult = self::$zipArchive->close();
|
||||
if ($zipCloseResult) {
|
||||
DUP_Log::Info("COMPRESSION RESULT: '{$zipCloseResult}'");
|
||||
} else {
|
||||
$error_message = "ZipArchive close failure.";
|
||||
DUP_Log::error(
|
||||
$error_message,
|
||||
"The ZipArchive engine is having issues zipping up the files on this server. For more details visit the FAQ\n"
|
||||
. "I'm getting a ZipArchive close failure when building. How can I resolve this?\n"
|
||||
. "[" . DUPLICATOR_DOCS_URL . "how-to-resolve-zip-format-related-build-issues/]",
|
||||
Dup_ErrorBehavior::LogOnly
|
||||
);
|
||||
$buildProgress->set_failed($error_message);
|
||||
$archive->Package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
$timerAllEnd = DUP_Util::getMicrotime();
|
||||
$timerAllSum = DUP_Util::elapsedTime($timerAllEnd, $timerAllStart);
|
||||
|
||||
self::$zipFileSize = @filesize(self::$zipPath);
|
||||
DUP_Log::Info("COMPRESSED SIZE: " . DUP_Util::byteSize(self::$zipFileSize));
|
||||
DUP_Log::Info("ARCHIVE RUNTIME: {$timerAllSum}");
|
||||
DUP_Log::Info("MEMORY STACK: " . DUP_Server::getPHPMemory());
|
||||
} catch (Exception $e) {
|
||||
$error_message = "Runtime error in class.pack.archive.zip.php constructor.";
|
||||
DUP_Log::error($error_message, "Exception: {$e}", Dup_ErrorBehavior::LogOnly);
|
||||
$buildProgress->set_failed($error_message);
|
||||
$archive->Package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,879 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\Snap\SnapDB;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapURL;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use Duplicator\Libs\Snap\SnapWP;
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
// Exit if accessed directly
|
||||
if (!defined('DUPLICATOR_VERSION')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for gathering system information about a database
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*/
|
||||
class DUP_DatabaseInfo
|
||||
{
|
||||
/**
|
||||
* The SQL file was built with mysqldump or PHP
|
||||
*/
|
||||
public $buildMode = 'PHP';
|
||||
/** @var string[] A unique list of all charsets table types used in the database */
|
||||
public $charSetList = array();
|
||||
/** @var string[] A unique list of all the collation table types used in the database */
|
||||
public $collationList = array();
|
||||
/** @var string[] engile list used in database tables */
|
||||
public $engineList = array();
|
||||
/**
|
||||
* Does any filtered table have an upper case character in it
|
||||
*/
|
||||
public $isTablesUpperCase = false;
|
||||
/**
|
||||
* Does the database name have any filtered characters in it
|
||||
*/
|
||||
public $isNameUpperCase = false;
|
||||
/**
|
||||
* The real name of the database
|
||||
*/
|
||||
public $name = '';
|
||||
/** @var int The full count of all tables in the database */
|
||||
public $tablesBaseCount = 0;
|
||||
/** @var int The count of tables after the tables filter has been applied */
|
||||
public $tablesFinalCount = 0;
|
||||
/** @var int multisite tables filtered count */
|
||||
public $muFilteredTableCount = 0;
|
||||
/** @var int The number of rows from all filtered tables in the database */
|
||||
public $tablesRowCount = 0;
|
||||
/** @var int The estimated data size on disk from all filtered tables in the database */
|
||||
public $tablesSizeOnDisk = 0;
|
||||
/** @var array */
|
||||
public $tablesList = array();
|
||||
|
||||
/**
|
||||
* Gets the server variable lower_case_table_names
|
||||
*
|
||||
* 0 store=lowercase; compare=sensitive (works only on case sensitive file systems )
|
||||
* 1 store=lowercase; compare=insensitive
|
||||
* 2 store=exact; compare=insensitive (works only on case INsensitive file systems )
|
||||
* default is 0/Linux ; 1/Windows
|
||||
*/
|
||||
public $lowerCaseTableNames = 0;
|
||||
/**
|
||||
* The database engine (MySQL/MariaDB/Percona)
|
||||
*
|
||||
* @var string
|
||||
* @example MariaDB
|
||||
*/
|
||||
public $dbEngine = '';
|
||||
/**
|
||||
* The simple numeric version number of the database server
|
||||
*
|
||||
* @exmaple: 5.5
|
||||
*/
|
||||
public $version = 0;
|
||||
/**
|
||||
* The full text version number of the database server
|
||||
*
|
||||
* @exmaple: 10.2 mariadb.org binary distribution
|
||||
*/
|
||||
public $versionComment = 0;
|
||||
|
||||
/**
|
||||
* @var int Number of VIEWs in the database
|
||||
*/
|
||||
public $viewCount = 0;
|
||||
|
||||
/**
|
||||
* @var int Number of PROCEDUREs in the database
|
||||
*/
|
||||
public $procCount = 0;
|
||||
|
||||
/**
|
||||
* @var int Number of PROCEDUREs in the database
|
||||
*/
|
||||
public $funcCount = 0;
|
||||
|
||||
/**
|
||||
* @var array List of triggers included in the database
|
||||
*/
|
||||
public $triggerList = array();
|
||||
/**
|
||||
* Integer field file structure of table, table name as key
|
||||
*/
|
||||
private $intFieldsStruct = array();
|
||||
/**
|
||||
* $currentIndex => processedSchemaSize
|
||||
*/
|
||||
private $indexProcessedSchemaSize = array();
|
||||
//CONSTRUCTOR
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function addTriggers()
|
||||
{
|
||||
global $wpdb;
|
||||
if (!is_array($triggers = $wpdb->get_results("SHOW TRIGGERS", ARRAY_A))) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($triggers as $trigger) {
|
||||
$name = $trigger["Trigger"];
|
||||
$create = $wpdb->get_row("SHOW CREATE TRIGGER `{$name}`", ARRAY_N);
|
||||
$this->triggerList[$name] = array(
|
||||
"create" => "DELIMITER ;;\n" . $create[2] . ";;\nDELIMITER ;"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param stirng $name // table name
|
||||
* @param int $inaccurateRows // This data is intended as a preliminary count and therefore not necessarily accurate
|
||||
* @param int $size // This data is intended as a preliminary count and therefore not necessarily accurate
|
||||
* @param int|bool $insertedRows // This value, if other than false, is the exact line value inserted into the dump file
|
||||
*/
|
||||
public function addTableInList($name, $inaccurateRows, $size, $insertedRows = false)
|
||||
{
|
||||
$this->tablesList[$name] = array(
|
||||
'inaccurateRows' => (int) $inaccurateRows,
|
||||
'insertedRows' => (int) $insertedRows,
|
||||
'size' => (int) $size
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DUP_Database
|
||||
{
|
||||
const TABLE_CREATION_END_MARKER = "/***** TABLE CREATION END *****/\n";
|
||||
/**
|
||||
* The mysqldump allowed size difference (50MB) to memory limit in bytes. Run musqldump only on DBs smaller than memory_limit minus this value.
|
||||
*/
|
||||
const MYSQLDUMP_ALLOWED_SIZE_DIFFERENCE = 52428800;
|
||||
|
||||
/**
|
||||
* Whitelist of valid mysqldump --compatible mode values.
|
||||
*/
|
||||
const ALLOWED_COMPAT_MODES = array(
|
||||
'mysql323',
|
||||
'mysql40',
|
||||
'postgresql',
|
||||
'oracle',
|
||||
'mssql',
|
||||
'db2',
|
||||
'maxdb',
|
||||
'no_key_options',
|
||||
'no_table_options',
|
||||
'no_field_options',
|
||||
'ansi'
|
||||
);
|
||||
|
||||
/**
|
||||
* Validates and sanitizes mysqldump compatibility mode values.
|
||||
*
|
||||
* @param string|array $compatValue The compatibility value(s) to validate
|
||||
*
|
||||
* @return string Comma-separated string of valid compatibility modes, or empty string if none valid
|
||||
*/
|
||||
public static function sanitizeCompatibilityMode($compatValue)
|
||||
{
|
||||
if (is_array($compatValue)) {
|
||||
$values = array_map('sanitize_text_field', $compatValue);
|
||||
} else {
|
||||
$values = array_map('trim', explode(',', sanitize_text_field($compatValue)));
|
||||
}
|
||||
|
||||
$validated = array_intersect($values, self::ALLOWED_COMPAT_MODES);
|
||||
return implode(',', $validated);
|
||||
}
|
||||
|
||||
//PUBLIC
|
||||
public $Type = 'MySQL';
|
||||
public $Size;
|
||||
public $File;
|
||||
public $Path;
|
||||
public $FilterTables;
|
||||
public $FilterOn;
|
||||
public $Name;
|
||||
public $Compatible;
|
||||
public $Comments;
|
||||
/** @var null|bool */
|
||||
public $sameNameTableExists = null;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var DUP_DatabaseInfo
|
||||
*/
|
||||
public $info = null;
|
||||
//PROTECTED
|
||||
protected $Package;
|
||||
//PRIVATE
|
||||
private $tempDbPath;
|
||||
private $EOFMarker;
|
||||
private $networkFlush;
|
||||
/**
|
||||
* Init this object
|
||||
*/
|
||||
public function __construct($package)
|
||||
{
|
||||
$this->Package = $package;
|
||||
$this->EOFMarker = "";
|
||||
$package_zip_flush = DUP_Settings::Get('package_zip_flush');
|
||||
$this->networkFlush = empty($package_zip_flush) ? false : $package_zip_flush;
|
||||
$this->info = new DUP_DatabaseInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the database script
|
||||
*
|
||||
* @param DUP_Package $package A reference to the package that this database object belongs in
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function build($package, $errorBehavior = Dup_ErrorBehavior::ThrowException)
|
||||
{
|
||||
try {
|
||||
$this->Package = $package;
|
||||
do_action('duplicator_lite_build_database_before_start', $package);
|
||||
$time_start = DUP_Util::getMicrotime();
|
||||
$this->Package->setStatus(DUP_PackageStatus::DBSTART);
|
||||
$this->tempDbPath = DUP_Settings::getSsdirTmpPath() . "/{$this->File}";
|
||||
$package_mysqldump = DUP_Settings::Get('package_mysqldump');
|
||||
$package_phpdump_qrylimit = DUP_Settings::Get('package_phpdump_qrylimit');
|
||||
$mysqlDumpPath = DUP_DB::getMySqlDumpPath();
|
||||
$mode = DUP_DB::getBuildMode();
|
||||
$reserved_db_filepath = duplicator_get_abs_path() . '/database.sql';
|
||||
$log = "\n********************************************************************************\n";
|
||||
$log .= "DATABASE:\n";
|
||||
$log .= "********************************************************************************\n";
|
||||
$log .= "BUILD MODE: {$mode}";
|
||||
$log .= ($mode == 'PHP') ? "(query limit - {$package_phpdump_qrylimit})\n" : "\n";
|
||||
$log .= "MYSQLTIMEOUT: " . DUPLICATOR_DB_MAX_TIME . "\n";
|
||||
$log .= "MYSQLDUMP: ";
|
||||
$log .= ($mysqlDumpPath) ? "Is Supported" : "Not Supported";
|
||||
DUP_Log::Info($log);
|
||||
$log = null;
|
||||
do_action('duplicator_lite_build_database_start', $package);
|
||||
switch ($mode) {
|
||||
case 'MYSQLDUMP':
|
||||
$this->mysqlDump($mysqlDumpPath);
|
||||
break;
|
||||
case 'PHP':
|
||||
$this->phpDump($package);
|
||||
break;
|
||||
}
|
||||
|
||||
DUP_Log::Info("SQL CREATED: {$this->File}");
|
||||
$time_end = DUP_Util::getMicrotime();
|
||||
$time_sum = DUP_Util::elapsedTime($time_end, $time_start);
|
||||
//File below 10k considered incomplete
|
||||
$sql_file_size = is_file($this->tempDbPath) ? @filesize($this->tempDbPath) : 0;
|
||||
DUP_Log::Info("SQL FILE SIZE: " . DUP_Util::byteSize($sql_file_size) . " ({$sql_file_size})");
|
||||
if ($sql_file_size < 1350) {
|
||||
$error_message = "SQL file size too low.";
|
||||
$package->BuildProgress->set_failed($error_message);
|
||||
$package->setStatus(DUP_PackageStatus::ERROR);
|
||||
DUP_Log::error($error_message, "File does not look complete. Check permission on file and parent directory at [{$this->tempDbPath}]", $errorBehavior);
|
||||
do_action('duplicator_lite_build_database_fail', $package);
|
||||
} else {
|
||||
do_action('duplicator_lite_build_database_completed', $package);
|
||||
}
|
||||
|
||||
DUP_Log::Info("SQL FILE TIME: " . date("Y-m-d H:i:s"));
|
||||
DUP_Log::Info("SQL RUNTIME: {$time_sum}");
|
||||
$this->Size = is_file($this->tempDbPath) ? @filesize($this->tempDbPath) : 0;
|
||||
$this->Package->setStatus(DUP_PackageStatus::DBDONE);
|
||||
} catch (Exception $e) {
|
||||
do_action('duplicator_lite_build_database_fail', $package);
|
||||
DUP_Log::error("Runtime error in DUP_Database::Build. " . $e->getMessage(), "Exception: {$e}", $errorBehavior);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database meta-data such as tables as all there details
|
||||
*
|
||||
* @return array Returns an array full of meta-data about the database
|
||||
*/
|
||||
public function getScannerData()
|
||||
{
|
||||
global $wpdb;
|
||||
$filterTables = isset($this->FilterTables) ? explode(',', $this->FilterTables) : array();
|
||||
$tblBaseCount = 0;
|
||||
$tblCount = 0;
|
||||
$tables = $this->getBaseTables();
|
||||
$info = array();
|
||||
$info['Status']['Success'] = is_null($tables) ? false : true;
|
||||
//DB_Case for the database name is never checked on
|
||||
$info['Status']['DB_Case'] = 'Good';
|
||||
$info['Status']['DB_Rows'] = 'Good';
|
||||
$info['Status']['DB_Size'] = 'Good';
|
||||
$info['Status']['TBL_Case'] = 'Good';
|
||||
$info['Status']['TBL_Rows'] = 'Good';
|
||||
$info['Status']['TBL_Size'] = 'Good';
|
||||
$info['Size'] = 0;
|
||||
$info['Rows'] = 0;
|
||||
$info['TableCount'] = 0;
|
||||
$info['TableList'] = array();
|
||||
$tblCaseFound = 0;
|
||||
$tblRowsFound = 0;
|
||||
$tblSizeFound = 0;
|
||||
//Grab Table Stats
|
||||
$filteredTables = array();
|
||||
$this->info->tablesList = array();
|
||||
|
||||
foreach ($tables as $table) {
|
||||
$tblBaseCount++;
|
||||
$name = $table["name"];
|
||||
if ($this->FilterOn && is_array($filterTables)) {
|
||||
if (in_array($name, $filterTables)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$size = $table['size'];
|
||||
$rows = empty($table["rows"]) ? '0' : $table["rows"];
|
||||
$info['Size'] += $size;
|
||||
$info['Rows'] += $rows;
|
||||
$info['TableList'][$name]['Case'] = preg_match('/[A-Z]/', $name) ? 1 : 0;
|
||||
$info['TableList'][$name]['Rows'] = number_format($rows);
|
||||
$info['TableList'][$name]['Size'] = DUP_Util::byteSize($size);
|
||||
$info['TableList'][$name]['USize'] = $size;
|
||||
$filteredTables[] = $name;
|
||||
|
||||
if (($qRes = $GLOBALS['wpdb']->get_var("SELECT Count(*) FROM `{$name}`")) === null) {
|
||||
$qRes = $rows;
|
||||
}
|
||||
|
||||
$row_count = (int) $qRes;
|
||||
$this->info->addTableInList($name, $rows, $size, $row_count);
|
||||
$tblCount++;
|
||||
|
||||
// Table Uppercase
|
||||
if ($info['TableList'][$name]['Case']) {
|
||||
if (!$tblCaseFound) {
|
||||
$tblCaseFound = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//Table Row Count
|
||||
if ($rows > DUPLICATOR_SCAN_DB_TBL_ROWS) {
|
||||
if (!$tblRowsFound) {
|
||||
$tblRowsFound = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//Table Size
|
||||
if ($size > DUPLICATOR_SCAN_DB_TBL_SIZE) {
|
||||
if (!$tblSizeFound) {
|
||||
$tblSizeFound = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->setInfoObj($filteredTables);
|
||||
$this->info->addTriggers();
|
||||
$info['Status']['DB_Case'] = preg_match('/[A-Z]/', $wpdb->dbname) ? 'Warn' : 'Good';
|
||||
$info['Status']['DB_Rows'] = ($info['Rows'] > DUPLICATOR_SCAN_DB_ALL_ROWS) ? 'Warn' : 'Good';
|
||||
$info['Status']['DB_Size'] = ($info['Size'] > DUPLICATOR_SCAN_DB_ALL_SIZE) ? 'Warn' : 'Good';
|
||||
$info['Status']['TBL_Case'] = ($tblCaseFound) ? 'Warn' : 'Good';
|
||||
$info['Status']['TBL_Rows'] = ($tblRowsFound) ? 'Warn' : 'Good';
|
||||
$info['Status']['TBL_Size'] = ($tblSizeFound) ? 'Warn' : 'Good';
|
||||
$info['Status']['Triggers'] = count($this->info->triggerList) > 0 ? 'Warn' : 'Good';
|
||||
$info['Status']['mysqlDumpMemoryCheck'] = self::mysqldumpMemoryCheck($info['Size']);
|
||||
$info['Status']['requiredMysqlDumpLimit'] = DUP_Util::byteSize(self::requiredMysqlDumpLimit($info['Size']));
|
||||
|
||||
$info['RawSize'] = $info['Size'];
|
||||
$info['TableList'] = $info['TableList'] or "unknown";
|
||||
$info['TableCount'] = $tblCount;
|
||||
$this->info->isTablesUpperCase = $tblCaseFound;
|
||||
$this->info->tablesBaseCount = $tblBaseCount;
|
||||
$this->info->tablesFinalCount = $tblCount;
|
||||
$this->info->tablesRowCount = (int) $info['Rows'];
|
||||
$this->info->tablesSizeOnDisk = (int) $info['Size'];
|
||||
$this->info->dbEngine = SnapDB::getDBEngine($wpdb->dbh);
|
||||
$info['EasySize'] = DUP_Util::byteSize($info['Size']) or "unknown";
|
||||
|
||||
$this->info->viewCount = count($wpdb->get_results("SHOW FULL TABLES WHERE Table_Type = 'VIEW'", ARRAY_A));
|
||||
$query = $wpdb->prepare("SHOW PROCEDURE STATUS WHERE `Db` = %s", DB_NAME);
|
||||
$this->info->procCount = count($wpdb->get_results($query, ARRAY_A));
|
||||
$query = $wpdb->prepare("SHOW FUNCTION STATUS WHERE `Db` = %s", DB_NAME);
|
||||
$this->info->funcCount = count($wpdb->get_results($query, ARRAY_A));
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array &$filteredTables Filtered names of tables to include in collation search.
|
||||
* Parameter does not change in the function, is passed by reference only to avoid copying.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setInfoObj($filteredTables)
|
||||
{
|
||||
global $wpdb;
|
||||
$this->info->buildMode = DUP_DB::getBuildMode();
|
||||
$this->info->version = DUP_DB::getVersion();
|
||||
$this->info->versionComment = DUP_DB::getVariable('version_comment');
|
||||
$this->info->lowerCaseTableNames = DUP_DB::getLowerCaseTableNames();
|
||||
$this->info->name = $wpdb->dbname;
|
||||
$this->info->isNameUpperCase = preg_match('/[A-Z]/', $wpdb->dbname) ? 1 : 0;
|
||||
$this->info->charSetList = DUP_DB::getTableCharSetList($filteredTables);
|
||||
$this->info->collationList = DUP_DB::getTableCollationList($filteredTables);
|
||||
$this->info->engineList = DUP_DB::getTableEngineList($filteredTables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of base tables to dump
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getBaseTables($nameOnly = false)
|
||||
{
|
||||
/** @var \wpdb $wpdb */
|
||||
global $wpdb;
|
||||
|
||||
// (TABLE_NAME REGEXP '^rte4ed_(2|6)_' OR TABLE_NAME NOT REGEXP '^rte4ed_[0-9]+_')
|
||||
$query = 'SELECT `TABLE_NAME` as `name`, `TABLE_ROWS` as `rows`, DATA_LENGTH + INDEX_LENGTH as `size` FROM `information_schema`.`tables`';
|
||||
|
||||
$where = array(
|
||||
'TABLE_SCHEMA = "' . esc_sql($wpdb->dbname) . '"',
|
||||
'TABLE_TYPE != "VIEW"'
|
||||
);
|
||||
|
||||
$query .= ' WHERE ' . implode(' AND ', $where);
|
||||
$query .= ' ORDER BY TABLE_NAME';
|
||||
|
||||
if ($nameOnly) {
|
||||
return $wpdb->get_col($query, 0);
|
||||
} else {
|
||||
return $wpdb->get_results($query, ARRAY_A);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the database script using mysqldump
|
||||
*
|
||||
* @return bool Returns true if the sql script was successfully created
|
||||
*/
|
||||
private function mysqlDump($exePath)
|
||||
{
|
||||
global $wpdb;
|
||||
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/utilities/class.u.shell.php');
|
||||
$host = SnapURL::parseUrl(DB_HOST, PHP_URL_HOST);
|
||||
if (($port = SnapURL::parseUrl(DB_HOST, PHP_URL_PORT)) == false) {
|
||||
$port = '';
|
||||
}
|
||||
$name = DB_NAME;
|
||||
$mysqlcompat_on = isset($this->Compatible) && strlen($this->Compatible);
|
||||
//Build command
|
||||
$cmd = escapeshellarg($exePath);
|
||||
$cmd .= ' --no-create-db';
|
||||
$cmd .= ' --single-transaction';
|
||||
$cmd .= ' --hex-blob';
|
||||
$cmd .= ' --skip-add-drop-table';
|
||||
$cmd .= ' --routines';
|
||||
$cmd .= ' --quote-names';
|
||||
$cmd .= ' --skip-comments';
|
||||
$cmd .= ' --skip-set-charset';
|
||||
$cmd .= ' --skip-triggers';
|
||||
$cmd .= ' --allow-keywords';
|
||||
$cmd .= ' --no-tablespaces';
|
||||
//Compatibility mode
|
||||
if ($mysqlcompat_on) {
|
||||
$safeCompatible = self::sanitizeCompatibilityMode($this->Compatible);
|
||||
|
||||
if (!empty($safeCompatible)) {
|
||||
DUP_Log::Info("COMPATIBLE: [{$safeCompatible}]");
|
||||
$cmd .= " --compatible=" . escapeshellarg($safeCompatible);
|
||||
} elseif (!empty($this->Compatible)) {
|
||||
DUP_Log::Info("COMPATIBLE: Invalid value detected and skipped: [{$this->Compatible}]");
|
||||
}
|
||||
}
|
||||
|
||||
//Filter tables
|
||||
$res = $wpdb->get_results('SHOW FULL TABLES', ARRAY_N);
|
||||
$tables = array();
|
||||
$baseTables = array();
|
||||
foreach ($res as $row) {
|
||||
if (DUP_Util::isTableExists($row[0])) {
|
||||
$tables[] = $row[0];
|
||||
if ('BASE TABLE' == $row[1]) {
|
||||
$baseTables[] = $row[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
$filterTables = isset($this->FilterTables) ? explode(',', $this->FilterTables) : null;
|
||||
$tblAllCount = count($tables);
|
||||
|
||||
//$tblFilterOn = ($this->FilterOn) ? 'ON' : 'OFF';
|
||||
if (is_array($filterTables) && $this->FilterOn) {
|
||||
foreach ($tables as $key => $val) {
|
||||
if (in_array($tables[$key], $filterTables)) {
|
||||
$cmd .= " --ignore-table={$name}.{$tables[$key]} ";
|
||||
unset($tables[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$cmd .= ' -u ' . escapeshellarg(DB_USER);
|
||||
$cmd .= (DB_PASSWORD) ?
|
||||
' -p' . DUP_Shell_U::escapeshellargWindowsSupport(DB_PASSWORD) : '';
|
||||
$cmd .= ' -h ' . escapeshellarg($host);
|
||||
$cmd .= (!empty($port) && is_numeric($port) ) ?
|
||||
' -P ' . $port : '';
|
||||
$isPopenEnabled = DUP_Shell_U::isPopenEnabled();
|
||||
if (!$isPopenEnabled) {
|
||||
$cmd .= ' -r ' . escapeshellarg($this->tempDbPath);
|
||||
}
|
||||
|
||||
$cmd .= ' ' . escapeshellarg(DB_NAME);
|
||||
$cmd .= ' 2>&1';
|
||||
if ($isPopenEnabled) {
|
||||
$needToRewrite = false;
|
||||
foreach ($tables as $tableName) {
|
||||
$rewriteTableAs = $this->rewriteTableNameAs($tableName);
|
||||
if ($tableName != $rewriteTableAs) {
|
||||
$needToRewrite = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($needToRewrite) {
|
||||
$findReplaceTableNames = array();
|
||||
// orignal table name => rewrite table name
|
||||
|
||||
foreach ($tables as $tableName) {
|
||||
$rewriteTableAs = $this->rewriteTableNameAs($tableName);
|
||||
if ($tableName != $rewriteTableAs) {
|
||||
$findReplaceTableNames[$tableName] = $rewriteTableAs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$firstLine = '';
|
||||
DUP_LOG::trace("Executing mysql dump command by popen: $cmd");
|
||||
$handle = popen($cmd, "r");
|
||||
if ($handle) {
|
||||
$sql_header = "/* DUPLICATOR-LITE (MYSQL-DUMP BUILD MODE) MYSQL SCRIPT CREATED ON : " . @date("Y-m-d H:i:s") . " */\n\n";
|
||||
file_put_contents($this->tempDbPath, $sql_header, FILE_APPEND);
|
||||
while (!feof($handle)) {
|
||||
$line = fgets($handle);
|
||||
//get ony one line
|
||||
if ($line) {
|
||||
if (empty($firstLine)) {
|
||||
$firstLine = $line;
|
||||
if (false !== stripos($line, 'Using a password on the command line interface can be insecure')) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($needToRewrite) {
|
||||
$replaceCount = 1;
|
||||
if (preg_match('/CREATE TABLE `(.*?)`/', $line, $matches)) {
|
||||
$tableName = $matches[1];
|
||||
if (isset($findReplaceTableNames[$tableName])) {
|
||||
$rewriteTableAs = $findReplaceTableNames[$tableName];
|
||||
$line = str_replace('CREATE TABLE `' . $tableName . '`', 'CREATE TABLE `' . $rewriteTableAs . '`', $line, $replaceCount);
|
||||
}
|
||||
} elseif (preg_match('/INSERT INTO `(.*?)`/', $line, $matches)) {
|
||||
$tableName = $matches[1];
|
||||
if (isset($findReplaceTableNames[$tableName])) {
|
||||
$rewriteTableAs = $findReplaceTableNames[$tableName];
|
||||
$line = str_replace('INSERT INTO `' . $tableName . '`', 'INSERT INTO `' . $rewriteTableAs . '`', $line, $replaceCount);
|
||||
}
|
||||
} elseif (preg_match('/LOCK TABLES `(.*?)`/', $line, $matches)) {
|
||||
$tableName = $matches[1];
|
||||
if (isset($findReplaceTableNames[$tableName])) {
|
||||
$rewriteTableAs = $findReplaceTableNames[$tableName];
|
||||
$line = str_replace('LOCK TABLES `' . $tableName . '`', 'LOCK TABLES `' . $rewriteTableAs . '`', $line, $replaceCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents($this->tempDbPath, $line, FILE_APPEND);
|
||||
$output = "Ran from {$exePath}";
|
||||
}
|
||||
}
|
||||
$mysqlResult = pclose($handle);
|
||||
} else {
|
||||
$output = '';
|
||||
}
|
||||
|
||||
// Password bug > 5.6 (@see http://bugs.mysql.com/bug.php?id=66546)
|
||||
if (empty($output) && trim($firstLine) === 'Warning: Using a password on the command line interface can be insecure.') {
|
||||
$output = '';
|
||||
}
|
||||
} else {
|
||||
DUP_LOG::trace("Executing mysql dump command $cmd");
|
||||
exec($cmd, $output, $mysqlResult);
|
||||
$output = implode("\n", $output);
|
||||
// Password bug > 5.6 (@see http://bugs.mysql.com/bug.php?id=66546)
|
||||
if (trim($output) === 'Warning: Using a password on the command line interface can be insecure.') {
|
||||
$output = '';
|
||||
}
|
||||
$output = (strlen($output)) ? $output : "Ran from {$exePath}";
|
||||
$tblCreateCount = count($tables);
|
||||
$tblFilterCount = $tblAllCount - $tblCreateCount;
|
||||
//DEBUG
|
||||
//DUP_Log::Info("COMMAND: {$cmd}");
|
||||
DUP_Log::Info("FILTERED: [{$this->FilterTables}]");
|
||||
DUP_Log::Info("RESPONSE: {$output}");
|
||||
DUP_Log::Info("TABLES: total:{$tblAllCount} | filtered:{$tblFilterCount} | create:{$tblCreateCount}");
|
||||
}
|
||||
|
||||
$sql_footer = "\n\n/* Duplicator WordPress Timestamp: " . date("Y-m-d H:i:s") . "*/\n";
|
||||
$sql_footer .= "/* " . DUPLICATOR_DB_EOF_MARKER . " */\n";
|
||||
file_put_contents($this->tempDbPath, $sql_footer, FILE_APPEND);
|
||||
if ($mysqlResult !== 0) {
|
||||
/**
|
||||
* -1 error command shell
|
||||
* mysqldump return
|
||||
* 0 - Success
|
||||
* 1 - Warning
|
||||
* 2 - Exception
|
||||
*/
|
||||
DUP_Log::Info('MYSQL DUMP ERROR ' . print_r($mysqlResult, true));
|
||||
DUP_Log::error(
|
||||
__('Shell mysql dump error. Change SQL Mode to the "PHP Code" in the Duplicator > Settings > Backups.', 'duplicator'),
|
||||
implode("\n", SnapIO::getLastLinesOfFile(
|
||||
$this->tempDbPath,
|
||||
DUPLICATOR_DB_MYSQLDUMP_ERROR_CONTAINING_LINE_COUNT,
|
||||
DUPLICATOR_DB_MYSQLDUMP_ERROR_CHARS_IN_LINE_COUNT
|
||||
)),
|
||||
Dup_ErrorBehavior::ThrowException
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if database size is within the mysqldump size limit
|
||||
*
|
||||
* @param int $dbSize Size of the database to check
|
||||
*
|
||||
* @return bool Returns true if DB size is within the mysqldump size limit, otherwise false
|
||||
*/
|
||||
protected static function mysqldumpMemoryCheck($dbSize)
|
||||
{
|
||||
$mem = SnapUtil::phpIniGet('memory_limit', false);
|
||||
$memInBytes = SnapUtil::convertToBytes($mem);
|
||||
|
||||
// If the memory limit is unknown or unlimited (-1), return true
|
||||
if ($mem === false || $memInBytes <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (self::requiredMysqlDumpLimit($dbSize) <= $memInBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return mysql required limit
|
||||
*
|
||||
* @param int $dbSize Size of the database to check
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected static function requiredMysqlDumpLimit($dbSize)
|
||||
{
|
||||
return $dbSize + self::MYSQLDUMP_ALLOWED_SIZE_DIFFERENCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the database script using php
|
||||
*
|
||||
* @return bool Returns true if the sql script was successfully created
|
||||
*/
|
||||
private function phpDump($package)
|
||||
{
|
||||
global $wpdb;
|
||||
$query = $wpdb->prepare("SET session wait_timeout = %d", DUPLICATOR_DB_MAX_TIME);
|
||||
$wpdb->query($query);
|
||||
if (($handle = fopen($this->tempDbPath, 'w+')) == false) {
|
||||
DUP_Log::error('[PHP DUMP] ERROR Can\'t open sbStorePath "' . $this->tempDbPath . '"', Dup_ErrorBehavior::ThrowException);
|
||||
}
|
||||
$tables = $wpdb->get_col("SHOW FULL TABLES WHERE Table_Type != 'VIEW'");
|
||||
$filterTables = isset($this->FilterTables) ? explode(',', $this->FilterTables) : null;
|
||||
$tblAllCount = count($tables);
|
||||
//$tblFilterOn = ($this->FilterOn) ? 'ON' : 'OFF';
|
||||
$qryLimit = DUP_Settings::Get('package_phpdump_qrylimit');
|
||||
if (is_array($filterTables) && $this->FilterOn) {
|
||||
foreach ($tables as $key => $val) {
|
||||
if (in_array($tables[$key], $filterTables)) {
|
||||
unset($tables[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$tblCreateCount = count($tables);
|
||||
$tblFilterCount = $tblAllCount - $tblCreateCount;
|
||||
DUP_Log::Info("TABLES: total:{$tblAllCount} | filtered:{$tblFilterCount} | create:{$tblCreateCount}");
|
||||
DUP_Log::Info("FILTERED: [{$this->FilterTables}]");
|
||||
//Added 'NO_AUTO_VALUE_ON_ZERO' at plugin version 1.2.12 to fix :
|
||||
//**ERROR** database error write 'Invalid default value for for older mysql versions
|
||||
$sql_header = "/* DUPLICATOR-LITE (PHP BUILD MODE) MYSQL SCRIPT CREATED ON : " . @date("Y-m-d H:i:s") . " */\n\n";
|
||||
$sql_header .= "/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;\n\n";
|
||||
$sql_header .= "SET FOREIGN_KEY_CHECKS = 0;\n\n";
|
||||
fwrite($handle, $sql_header);
|
||||
//BUILD CREATES:
|
||||
//All creates must be created before inserts do to foreign key constraints
|
||||
foreach ($tables as $table) {
|
||||
$rewrite_table_as = $this->rewriteTableNameAs($table);
|
||||
$create = $wpdb->get_row("SHOW CREATE TABLE `{$table}`", ARRAY_N);
|
||||
$count = 1;
|
||||
$create_table_query = str_replace($table, $rewrite_table_as, $create[1], $count);
|
||||
@fwrite($handle, "{$create_table_query};\n\n");
|
||||
}
|
||||
|
||||
|
||||
$query = $wpdb->prepare("SHOW PROCEDURE STATUS WHERE `Db` = %s", $wpdb->dbname);
|
||||
$procedures = $wpdb->get_col($query, 1);
|
||||
if (count($procedures)) {
|
||||
foreach ($procedures as $procedure) {
|
||||
@fwrite($handle, "DELIMITER ;;\n");
|
||||
$create = $wpdb->get_row("SHOW CREATE PROCEDURE `{$procedure}`", ARRAY_N);
|
||||
@fwrite($handle, "{$create[2]} ;;\n");
|
||||
@fwrite($handle, "DELIMITER ;\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
$query = $wpdb->prepare("SHOW FUNCTION STATUS WHERE `Db` = %s", $wpdb->dbname);
|
||||
$functions = $wpdb->get_col($query, 1);
|
||||
if (count($functions)) {
|
||||
foreach ($functions as $function) {
|
||||
@fwrite($handle, "DELIMITER ;;\n");
|
||||
$create = $wpdb->get_row("SHOW CREATE FUNCTION `{$function}`", ARRAY_N);
|
||||
@fwrite($handle, "{$create[2]} ;;\n");
|
||||
@fwrite($handle, "DELIMITER ;\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
$views = $wpdb->get_col("SHOW FULL TABLES WHERE Table_Type = 'VIEW'");
|
||||
if (count($views)) {
|
||||
foreach ($views as $view) {
|
||||
$create = $wpdb->get_row("SHOW CREATE VIEW `{$view}`", ARRAY_N);
|
||||
@fwrite($handle, "{$create[1]};\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
@fwrite($handle, self::TABLE_CREATION_END_MARKER . "\n");
|
||||
|
||||
$table_count = count($tables);
|
||||
$table_number = 0;
|
||||
//BUILD INSERTS:
|
||||
//Create Insert in 100 row increments to better handle memory
|
||||
foreach ($tables as $table) {
|
||||
$table_number++;
|
||||
if ($table_number % 2 == 0) {
|
||||
$this->Package->Status = SnapUtil::getWorkPercent(DUP_PackageStatus::DBSTART, DUP_PackageStatus::DBDONE, $table_count, $table_number);
|
||||
$this->Package->update();
|
||||
}
|
||||
|
||||
$row_count = $wpdb->get_var("SELECT Count(*) FROM `{$table}`");
|
||||
$rewrite_table_as = $this->rewriteTableNameAs($table);
|
||||
|
||||
if ($row_count > $qryLimit) {
|
||||
$row_count = ceil($row_count / $qryLimit);
|
||||
} elseif ($row_count > 0) {
|
||||
$row_count = 1;
|
||||
}
|
||||
|
||||
if ($row_count >= 1) {
|
||||
fwrite($handle, "\n/* INSERT TABLE DATA: {$table} */\n");
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $row_count; $i++) {
|
||||
$sql = "";
|
||||
$limit = $i * $qryLimit;
|
||||
$query = "SELECT * FROM `{$table}` LIMIT {$limit}, {$qryLimit}";
|
||||
$rows = $wpdb->get_results($query, ARRAY_A);
|
||||
$select_last_error = $wpdb->last_error;
|
||||
if ('' !== $select_last_error) {
|
||||
$fix = esc_html__('Please contact your DataBase administrator to fix the error.', 'duplicator');
|
||||
$errorMessage = $select_last_error . ' ' . $fix . '.';
|
||||
$package->BuildProgress->set_failed($errorMessage);
|
||||
$package->BuildProgress->failed = true;
|
||||
$package->failed = true;
|
||||
$package->Status = DUP_PackageStatus::ERROR;
|
||||
$package->Update();
|
||||
DUP_Log::error($select_last_error, $fix, Dup_ErrorBehavior::ThrowException);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_array($rows)) {
|
||||
foreach ($rows as $row) {
|
||||
$sql .= "INSERT INTO `{$rewrite_table_as}` VALUES(";
|
||||
$num_values = count($row);
|
||||
$num_counter = 1;
|
||||
foreach ($row as $value) {
|
||||
if (is_null($value) || !isset($value)) {
|
||||
($num_values == $num_counter) ? $sql .= 'NULL' : $sql .= 'NULL, ';
|
||||
} else {
|
||||
($num_values == $num_counter) ? $sql .= '"' . DUP_DB::escSQL($value, true) . '"' : $sql .= '"' . DUP_DB::escSQL($value, true) . '", ';
|
||||
}
|
||||
$num_counter++;
|
||||
}
|
||||
$sql .= ");\n";
|
||||
}
|
||||
fwrite($handle, $sql);
|
||||
}
|
||||
}
|
||||
|
||||
//Flush buffer if enabled
|
||||
if ($this->networkFlush) {
|
||||
DUP_Util::fcgiFlush();
|
||||
}
|
||||
$sql = null;
|
||||
$rows = null;
|
||||
}
|
||||
|
||||
$sql_footer = "\nSET FOREIGN_KEY_CHECKS = 1; \n\n";
|
||||
$sql_footer .= "/* Duplicator WordPress Timestamp: " . date("Y-m-d H:i:s") . "*/\n";
|
||||
$sql_footer .= "/* " . DUPLICATOR_DB_EOF_MARKER . " */\n";
|
||||
fwrite($handle, $sql_footer);
|
||||
$wpdb->flush();
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
private function rewriteTableNameAs($table)
|
||||
{
|
||||
$table_prefix = $this->getTablePrefix();
|
||||
if (!isset($this->sameNameTableExists)) {
|
||||
global $wpdb;
|
||||
$this->sameNameTableExists = false;
|
||||
$all_tables = $wpdb->get_col("SHOW FULL TABLES WHERE Table_Type != 'VIEW'");
|
||||
foreach ($all_tables as $table_name) {
|
||||
if (strtolower($table_name) != $table_name && in_array(strtolower($table_name), $all_tables)) {
|
||||
$this->sameNameTableExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (false === $this->sameNameTableExists && 0 === stripos($table, $table_prefix) && 0 !== strpos($table, $table_prefix)) {
|
||||
$post_fix = substr($table, strlen($table_prefix));
|
||||
$rewrite_table_name = $table_prefix . $post_fix;
|
||||
} else {
|
||||
$rewrite_table_name = $table;
|
||||
}
|
||||
return $rewrite_table_name;
|
||||
}
|
||||
|
||||
private function getTablePrefix()
|
||||
{
|
||||
global $wpdb;
|
||||
$table_prefix = (is_multisite() && !defined('MULTISITE')) ? $wpdb->base_prefix : $wpdb->get_blog_prefix(0);
|
||||
return $table_prefix;
|
||||
}
|
||||
|
||||
public function getUrl()
|
||||
{
|
||||
return DUP_Settings::getSsdirUrl() . "/" . $this->File;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
1926
html/wp-content/plugins/duplicator/classes/package/class.pack.php
Normal file
1926
html/wp-content/plugins/duplicator/classes/package/class.pack.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\DupArchive\DupArchiveEngine;
|
||||
use Duplicator\Libs\DupArchive\DupArchiveLoggerBase;
|
||||
use Duplicator\Libs\DupArchive\States\DupArchiveExpandState;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
|
||||
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/package/duparchive/class.pack.archive.duparchive.state.expand.php');
|
||||
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/package/duparchive/class.pack.archive.duparchive.state.create.php');
|
||||
|
||||
class DUP_DupArchive_Logger extends DupArchiveLoggerBase
|
||||
{
|
||||
public function log($s, $flush = false, $callingFunctionOverride = null)
|
||||
{
|
||||
DUP_Log::Trace($s, true, $callingFunctionOverride);
|
||||
}
|
||||
}
|
||||
|
||||
class DUP_DupArchive
|
||||
{
|
||||
// Using a worker time override since evidence shorter time works much
|
||||
const WORKER_TIME_IN_SEC = 10;
|
||||
/**
|
||||
* CREATE
|
||||
* Creates the zip file and adds the SQL file to the archive
|
||||
*/
|
||||
public static function create(DUP_Archive $archive, $buildProgress, $package)
|
||||
{
|
||||
/* @var $buildProgress DUP_Build_Progress */
|
||||
|
||||
DUP_LOG::trace("start");
|
||||
try {
|
||||
DUP_Log::Open($package->NameHash);
|
||||
if ($buildProgress->retries > DUPLICATOR_MAX_BUILD_RETRIES) {
|
||||
$error_msg = __('Backup build appears stuck so marking Backup as failed. Is the Max Worker Time set too high?.', 'duplicator');
|
||||
DUP_Log::error(esc_html__('Build Failure', 'duplicator'), esc_html($error_msg), Dup_ErrorBehavior::LogOnly);
|
||||
//$buildProgress->failed = true;
|
||||
$buildProgress->set_failed($error_msg);
|
||||
$package->setStatus(DUP_PackageStatus::ERROR);
|
||||
;
|
||||
return true;
|
||||
} else {
|
||||
// If all goes well retries will be reset to 0 at the end of this function.
|
||||
$buildProgress->retries++;
|
||||
$package->update();
|
||||
}
|
||||
|
||||
$done = false;
|
||||
DupArchiveEngine::init(new DUP_DupArchive_Logger(), $archive->getTargetRootPath());
|
||||
DUP_Package::safeTmpCleanup(true);
|
||||
$compressDir = rtrim(DUP_Util::safePath($archive->PackDir), '/');
|
||||
$sqlPath = DUP_Settings::getSsdirTmpPath() . "/{$package->Database->File}";
|
||||
$archivePath = DUP_Settings::getSsdirTmpPath() . "/{$archive->File}";
|
||||
$scanFilepath = DUP_Settings::getSsdirTmpPath() . "/{$package->NameHash}_scan.json";
|
||||
$skipArchiveFinalization = false;
|
||||
$json = '';
|
||||
if (file_exists($scanFilepath)) {
|
||||
$json = file_get_contents($scanFilepath);
|
||||
if (empty($json)) {
|
||||
$errorText = __("Scan file $scanFilepath is empty!", 'duplicator');
|
||||
$fixText = __("Click on \"Resolve This\" button to fix the JSON settings.", 'duplicator');
|
||||
DUP_Log::Trace($errorText);
|
||||
DUP_Log::error(esc_html($errorText) . " **RECOMMENDATION: " . esc_html($fixText) . ".", '', Dup_ErrorBehavior::LogOnly);
|
||||
//$buildProgress->failed = true;
|
||||
$buildProgress->set_failed($errorText);
|
||||
$package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
DUP_Log::trace("**** scan file $scanFilepath doesn't exist!!");
|
||||
$errorMessage = sprintf(__("ERROR: Can't find Scanfile %s. Please ensure there no non-English characters in the Backup or schedule name.", 'duplicator'), $scanFilepath);
|
||||
DUP_Log::error($errorMessage, '', Dup_ErrorBehavior::LogOnly);
|
||||
//$buildProgress->failed = true;
|
||||
$buildProgress->set_failed($errorMessage);
|
||||
$package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return true;
|
||||
}
|
||||
|
||||
Dup_Log::TraceObject("buildprogress object", $buildProgress, false);
|
||||
$scanReport = json_decode($json);
|
||||
if ($buildProgress->archive_started == false) {
|
||||
$filterDirs = empty($archive->FilterDirs) ? 'not set' : $archive->FilterDirs;
|
||||
$filterExts = empty($archive->FilterExts) ? 'not set' : $archive->FilterExts;
|
||||
$filterFiles = empty($archive->FilterFiles) ? 'not set' : $archive->FilterFiles;
|
||||
$filterOn = ($archive->FilterOn) ? 'ON' : 'OFF';
|
||||
$filterDirsFormat = rtrim(str_replace(';', "\n\t", $filterDirs));
|
||||
$filterFilesFormat = rtrim(str_replace(';', "\n\t", $filterFiles));
|
||||
DUP_Log::info("\n********************************************************************************");
|
||||
DUP_Log::info("ARCHIVE Type=DUP Mode=DupArchive");
|
||||
DUP_Log::info("********************************************************************************");
|
||||
DUP_Log::info("ARCHIVE DIR: " . $compressDir);
|
||||
DUP_Log::info("ARCHIVE FILE: " . basename($archivePath));
|
||||
DUP_Log::info("FILTERS: *{$filterOn}*");
|
||||
DUP_Log::Info("DIRS:\n\t{$filterDirsFormat}");
|
||||
DUP_Log::Info("FILES:\n\t{$filterFilesFormat}");
|
||||
DUP_Log::info("EXTS: {$filterExts}");
|
||||
DUP_Log::info("----------------------------------------");
|
||||
DUP_Log::info("COMPRESSING");
|
||||
DUP_Log::info("SIZE:\t" . $scanReport->ARC->Size);
|
||||
DUP_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 == '')) {
|
||||
$error_message = 'Invalid Scan Report Detected';
|
||||
DUP_Log::error($error_message, 'Invalid Scan Report Detected', Dup_ErrorBehavior::LogOnly);
|
||||
$buildProgress->set_failed($error_message);
|
||||
$package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
DupArchiveEngine::createArchive($archivePath, true);
|
||||
$sql_ark_file_path = $package->getSqlArkFilePath();
|
||||
DupArchiveEngine::addRelativeFileToArchiveST($archivePath, $sqlPath, $sql_ark_file_path);
|
||||
} catch (Exception $ex) {
|
||||
$error_message = 'Error adding database.sql to archive';
|
||||
DUP_Log::error($error_message, $ex->getMessage(), Dup_ErrorBehavior::LogOnly);
|
||||
$buildProgress->set_failed($error_message);
|
||||
$package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return true;
|
||||
}
|
||||
|
||||
$buildProgress->archive_started = true;
|
||||
$buildProgress->retries = 0;
|
||||
$createState = DUP_DupArchive_Create_State::createNew($archivePath, $compressDir, self::WORKER_TIME_IN_SEC, true, true);
|
||||
$createState->throttleDelayInUs = 0;
|
||||
$createState->save();
|
||||
$package->Update();
|
||||
}
|
||||
|
||||
try {
|
||||
$createState = DUP_DupArchive_Create_State::get_instance();
|
||||
if ($buildProgress->retries > 1) {
|
||||
$createState->isRobust = true;
|
||||
$createState->save();
|
||||
}
|
||||
|
||||
if ($createState->working) {
|
||||
DUP_LOG::Trace("Create state is working");
|
||||
DupArchiveEngine::addItemsToArchive($createState, $scanReport->ARC);
|
||||
$buildProgress->set_build_failures($createState->failures);
|
||||
if ($createState->isCriticalFailurePresent()) {
|
||||
throw new Exception($createState->getFailureSummary());
|
||||
}
|
||||
|
||||
$totalFileCount = count($scanReport->ARC->Files);
|
||||
$package->Status = SnapUtil::getWorkPercent(DUP_PackageStatus::ARCSTART, DUP_PackageStatus::ARCVALIDATION, $totalFileCount, $createState->currentFileIndex);
|
||||
$buildProgress->retries = 0;
|
||||
$createState->save();
|
||||
DUP_LOG::TraceObject("Stored Create State", $createState);
|
||||
DUP_LOG::TraceObject('Stored build_progress', $package->BuildProgress);
|
||||
if ($createState->working == false) {
|
||||
// Want it to do the final cleanup work in an entirely new thread so return immediately
|
||||
$skipArchiveFinalization = true;
|
||||
DUP_LOG::TraceObject("Done build phase. Create State=", $createState);
|
||||
}
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
$message = __('Problem adding items to archive.', 'duplicator') . ' ' . $ex->getMessage();
|
||||
DUP_Log::error(__('Problems adding items to archive.', 'duplicator'), $message, Dup_ErrorBehavior::LogOnly);
|
||||
DUP_Log::TraceObject($message . " EXCEPTION:", $ex);
|
||||
//$buildProgress->failed = true;
|
||||
$buildProgress->set_failed($message);
|
||||
$package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-- Final Wrapup of the Archive
|
||||
if ((!$skipArchiveFinalization) && ($createState->working == false)) {
|
||||
DUP_LOG::Trace("Create state is not working and not skip archive finalization");
|
||||
if (!$buildProgress->installer_built) {
|
||||
if ($package->Installer->build($package, false)) {
|
||||
$package->Runtime = -1;
|
||||
$package->ExeSize = DUP_Util::byteSize($package->Installer->Size);
|
||||
$package->ZipSize = DUP_Util::byteSize($package->Archive->Size);
|
||||
$package->update();
|
||||
} else {
|
||||
$package->update();
|
||||
return;
|
||||
}
|
||||
|
||||
DUP_Log::Trace("Installer has been built so running expand now");
|
||||
$expandState = DUP_DupArchive_Expand_State::getInstance(true);
|
||||
$expandState->archivePath = $archivePath;
|
||||
$expandState->working = true;
|
||||
$expandState->timeSliceInSecs = self::WORKER_TIME_IN_SEC;
|
||||
$expandState->basePath = DUP_Settings::getSsdirTmpPath() . '/validate';
|
||||
$expandState->throttleDelayInUs = 0;
|
||||
// RSR TODO
|
||||
$expandState->validateOnly = true;
|
||||
$expandState->validationType = DupArchiveExpandState::VALIDATION_NONE;
|
||||
$expandState->working = true;
|
||||
$expandState->expectedDirectoryCount = count($scanReport->ARC->Dirs) - $createState->skippedDirectoryCount + $package->Installer->numDirsAdded;
|
||||
$expandState->expectedFileCount = 1 + count($scanReport->ARC->Files) + 1 - $createState->skippedFileCount + $package->Installer->numFilesAdded;
|
||||
// database.sql will be in there
|
||||
|
||||
$expandState->save();
|
||||
$sfc = count($scanReport->ARC->Files);
|
||||
$nfa = $package->Installer->numFilesAdded;
|
||||
Dup_Log::trace("####scan files {$sfc} skipped files {$createState->skippedFileCount} num files added {$nfa}");
|
||||
DUP_LOG::traceObject("EXPAND STATE AFTER SAVE", $expandState);
|
||||
} else {
|
||||
try {
|
||||
$expandState = DUP_DupArchive_Expand_State::getInstance();
|
||||
if ($buildProgress->retries > 1) {
|
||||
// Indicates it had problems before so move into robustness mode
|
||||
$expandState->isRobust = true;
|
||||
$expandState->save();
|
||||
}
|
||||
|
||||
DUP_Log::traceObject('Resumed validation expand state', $expandState);
|
||||
DupArchiveEngine::expandArchive($expandState);
|
||||
$buildProgress->set_validation_failures($expandState->failures);
|
||||
$totalFileCount = count($scanReport->ARC->Files);
|
||||
$archiveSize = @filesize($expandState->archivePath);
|
||||
|
||||
$package->Status = SnapUtil::getWorkPercent(
|
||||
DUP_PackageStatus::ARCVALIDATION,
|
||||
DUP_PackageStatus::COMPLETE,
|
||||
$archiveSize,
|
||||
$expandState->archiveOffset
|
||||
);
|
||||
DUP_LOG::TraceObject("package status after expand=", $package->Status);
|
||||
DUP_LOG::Trace("archive size:{$archiveSize} expand offset:{$expandState->archiveOffset}");
|
||||
} catch (Exception $ex) {
|
||||
DUP_Log::Trace('Exception:' . $ex->getMessage() . ':' . $ex->getTraceAsString());
|
||||
$buildProgress->set_failed('Error validating archive');
|
||||
$package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($expandState->isCriticalFailurePresent()) {
|
||||
// Fail immediately if critical failure present - even if havent completed processing the entire archive.
|
||||
$error_message = __('Critical failure present in validation', 'duplicator');
|
||||
DUP_Log::error($error_message, $expandState->getFailureSummary(), Dup_ErrorBehavior::LogOnly);
|
||||
$buildProgress->set_failed($error_message);
|
||||
return true;
|
||||
} elseif (!$expandState->working) {
|
||||
$buildProgress->archive_built = true;
|
||||
$buildProgress->retries = 0;
|
||||
$package->update();
|
||||
$timerAllEnd = DUP_Util::getMicrotime();
|
||||
$timerAllSum = DUP_Util::elapsedTime($timerAllEnd, $package->TimerStart);
|
||||
DUP_LOG::traceObject("create state", $createState);
|
||||
$archiveFileSize = @filesize($archivePath);
|
||||
DUP_Log::info("COMPRESSED SIZE: " . DUP_Util::byteSize($archiveFileSize));
|
||||
DUP_Log::info("ARCHIVE RUNTIME: {$timerAllSum}");
|
||||
DUP_Log::info("MEMORY STACK: " . DUP_Server::getPHPMemory());
|
||||
DUP_Log::info("CREATE WARNINGS: " . $createState->getFailureSummary(false, true));
|
||||
DUP_Log::info("VALIDATION WARNINGS: " . $expandState->getFailureSummary(false, true));
|
||||
$archive->file_count = $expandState->fileWriteCount + $expandState->directoryWriteCount;
|
||||
$package->update();
|
||||
$done = true;
|
||||
} else {
|
||||
$expandState->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
// Have to have a catchall since the main system that calls this function is not prepared to handle exceptions
|
||||
DUP_Log::trace('Top level create Exception:' . $ex->getMessage() . ':' . $ex->getTraceAsString());
|
||||
//$buildProgress->failed = true;
|
||||
$buildProgress->set_failed('Error encoundtered creating archive. See Backup log');
|
||||
return true;
|
||||
}
|
||||
|
||||
$buildProgress->retries = 0;
|
||||
return $done;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\DupArchive\States\DupArchiveCreateState;
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
|
||||
class DUP_DupArchive_Create_State extends DupArchiveCreateState
|
||||
{
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->throttleDelayInUs = 10;
|
||||
}
|
||||
|
||||
// Only one active package so straightforward
|
||||
// public static function createFromPackage(&$package)
|
||||
public static function get_instance()
|
||||
{
|
||||
$instance = new DUP_DupArchive_Create_State();
|
||||
$data = DUP_Settings::Get('duparchive_create_state');
|
||||
DUP_Util::objectCopy($data, $instance);
|
||||
$instance->startTimestamp = time();
|
||||
DUP_Log::TraceObject("retrieving create state", $instance);
|
||||
return $instance;
|
||||
}
|
||||
|
||||
public static function createNew($archivePath, $basePath, $timeSliceInSecs, $isCompressed, $setArchiveOffsetToEndOfArchive)
|
||||
{
|
||||
$instance = new DUP_DupArchive_Create_State();
|
||||
if ($setArchiveOffsetToEndOfArchive) {
|
||||
$instance->archiveOffset = filesize($archivePath);
|
||||
} else {
|
||||
$instance->archiveOffset = 0;
|
||||
}
|
||||
|
||||
$instance->archivePath = $archivePath;
|
||||
$instance->basePath = $basePath;
|
||||
$instance->currentDirectoryIndex = 0;
|
||||
$instance->currentFileOffset = 0;
|
||||
$instance->currentFileIndex = 0;
|
||||
$instance->failures = array();
|
||||
$instance->globSize = DupArchiveCreateState::DEFAULT_GLOB_SIZE;
|
||||
$instance->isCompressed = $isCompressed;
|
||||
$instance->timeSliceInSecs = $timeSliceInSecs;
|
||||
$instance->working = true;
|
||||
$instance->skippedDirectoryCount = 0;
|
||||
$instance->skippedFileCount = 0;
|
||||
$instance->startTimestamp = time();
|
||||
return $instance;
|
||||
}
|
||||
|
||||
public function addFailure($type, $subject, $description, $isCritical = false)
|
||||
{
|
||||
parent::addFailure($type, $subject, $description, $isCritical);
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
DUP_Log::TraceObject("Saving create state", $this);
|
||||
DUP_Settings::Set('duparchive_create_state', $this);
|
||||
DUP_Settings::Save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\DupArchive\States\DupArchiveExpandState;
|
||||
use Duplicator\Libs\Snap\SnapJson;
|
||||
|
||||
class DUP_DupArchive_Expand_State extends DupArchiveExpandState
|
||||
{
|
||||
public static function getInstance($reset = false)
|
||||
{
|
||||
$instance = new DUP_DupArchive_Expand_State();
|
||||
if ($reset) {
|
||||
$instance->initMembers();
|
||||
} else {
|
||||
$instance->loadMembers();
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
private function loadMembers()
|
||||
{
|
||||
/** @var object $data */
|
||||
$data = DUP_Settings::Get('duparchive_expand_state');
|
||||
DUP_LOG::traceObject("****RAW EXPAND STATE LOADED****", $data);
|
||||
if ($data->currentFileHeaderString != null) {
|
||||
$this->currentFileHeader = DUP_JSON::decode($data->currentFileHeaderString);
|
||||
} else {
|
||||
$this->currentFileHeader = null;
|
||||
}
|
||||
|
||||
if ($data->archiveHeaderString != null) {
|
||||
$this->archiveHeader = DUP_JSON::decode($data->archiveHeaderString);
|
||||
} else {
|
||||
$this->archiveHeader = null;
|
||||
}
|
||||
|
||||
if ($data->failuresString) {
|
||||
$this->failures = DUP_JSON::decode($data->failuresString);
|
||||
} else {
|
||||
$this->failures = array();
|
||||
}
|
||||
|
||||
DUP_Util::objectCopy($data, $this, array('archiveHeaderString', 'currentFileHeaderString', 'failuresString'));
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$data = new stdClass();
|
||||
if ($this->currentFileHeader != null) {
|
||||
$data->currentFileHeaderString = SnapJson::jsonEncode($this->currentFileHeader);
|
||||
} else {
|
||||
$data->currentFileHeaderString = null;
|
||||
}
|
||||
|
||||
if ($this->archiveHeader != null) {
|
||||
$data->archiveHeaderString = SnapJson::jsonEncode($this->archiveHeader);
|
||||
} else {
|
||||
$data->archiveHeaderString = null;
|
||||
}
|
||||
|
||||
$data->failuresString = SnapJson::jsonEncode($this->failures);
|
||||
// Object members auto skipped
|
||||
DUP_Util::objectCopy($this, $data);
|
||||
DUP_LOG::traceObject("****SAVING EXPAND STATE****", $this);
|
||||
DUP_LOG::traceObject("****SERIALIZED STATE****", $data);
|
||||
DUP_Settings::Set('duparchive_expand_state', $data);
|
||||
DUP_Settings::Save();
|
||||
}
|
||||
|
||||
private function initMembers()
|
||||
{
|
||||
$this->currentFileHeader = null;
|
||||
$this->archiveOffset = 0;
|
||||
$this->archiveHeader = null;
|
||||
$this->archivePath = null;
|
||||
$this->basePath = null;
|
||||
$this->currentFileOffset = 0;
|
||||
$this->failures = array();
|
||||
$this->isCompressed = false;
|
||||
$this->startTimestamp = time();
|
||||
$this->timeSliceInSecs = -1;
|
||||
$this->working = false;
|
||||
$this->validateOnly = false;
|
||||
$this->directoryModeOverride = -1;
|
||||
$this->fileModeOverride = -1;
|
||||
$this->throttleDelayInUs = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//silent
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//silent
|
||||
Reference in New Issue
Block a user