first commit

This commit is contained in:
Roman Pyrih
2026-04-21 15:48:41 +02:00
commit 7483681901
10216 changed files with 3236626 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
<?php
/**
* Add wp cli commands
*
* Name: WP-CLI Addon
* Version: 1
* Author: Duplicator
* Author URI: http://snapcreek.com
*
* PHP version 7.4
*
* @category Duplicator
* @package Plugin
* @author Duplicator
* @copyright 2011-2024 Snapcreek LLC
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
* @version GIT: $Id$
* @link https://duplicator.com/
*/
namespace Duplicator\Addons\WpCliAddon;
/**
* Version Pro Base addon class
*
* @category Duplicator
* @package Plugin
* @author Snapcreek <admin@snapcreek.com>
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
* @link http://snapcreek.com
*/
class WpCLiAddon extends \Duplicator\Core\Addons\AbstractAddonCore
{
/**
* @return void
*/
public function init(): void
{
$cli = new DuplicatorCli();
}
/**
*
* @return string
*/
public static function getAddonPath(): string
{
return __DIR__;
}
/**
*
* @return string
*/
public static function getAddonFile(): string
{
return __FILE__;
}
}

View File

@@ -0,0 +1,107 @@
<?php
/**
* @package Duplicator
* @copyright (c) 2022, Snap Creek LLC
*/
namespace Duplicator\Addons\WpCliAddon;
use Duplicator\Utils\Logging\DupLog;
use Duplicator\Package\DupPackage;
use Duplicator\Package\AbstractPackage;
use Duplicator\Ajax\ServicesPackage;
use Duplicator\Libs\Snap\SnapUtil;
use Duplicator\Models\Storages\StoragesUtil;
use Exception;
use WP_CLI;
class BackupBuild
{
/**
* Process schedules by cron
*
* @param DupPackage $package Package to process
*
* @return void
*/
public static function process(DupPackage $package): void
{
StoragesUtil::getDefaultStorage()->initStorageDirectory(true);
if (SnapUtil::isIniValChangeable('memory_limit')) {
@ini_set('memory_limit', -1);
}
$start_time = time();
DupLog::trace("PACKAGE {$package->getId()}:PROCESSING");
if ($package->getStatus() < AbstractPackage::STATUS_AFTER_SCAN) {
// Scan step built into package build - used by schedules - NOT manual build where scan is done in web service.
DupLog::trace("PACKAGE {$package->getId()}:SCANNING");
//After scanner runs. Save FilterInfo (unreadable, warnings, globals etc)
if (!$package->Archive->scanFiles(true)) {
while ($package->Archive->scanFiles() !== true) {
DupLog::trace("CONTINUE SCANNING");
}
}
$scan_report = $package->createScanReport();
$package->setStatus(AbstractPackage::STATUS_AFTER_SCAN);
$end_time = time();
$scan_time = $end_time - $start_time;
DupLog::trace("SCAN TIME=$scan_time seconds");
WP_CLI::debug("Scan result\n" . json_encode($scan_report, JSON_PRETTY_PRINT));
if ($scan_report['Status'] > ServicesPackage::EXEC_STATUS_PASS) {
if (empty($scan_report['Message'])) {
$scan_report['Message'] = 'Scan failed';
}
throw new Exception("Scan failed, Status: {$scan_report['Status']}, Message: {$scan_report['Message']}");
} else {
WP_CLI::success("Scan success");
}
} elseif ($package->getStatus() < AbstractPackage::STATUS_COPIEDPACKAGE) {
DupLog::trace("PACKAGE {$package->getId()}:BUILDING");
$package->runBuild(false);
$end_time = time();
$build_time = $end_time - $start_time;
DupLog::trace("BUILD TIME=$build_time seconds");
if ($package->build_progress->hasCompleted()) {
if ($package->build_progress->failed) {
throw new Exception("Build failed");
}
}
} elseif ($package->getStatus() < AbstractPackage::STATUS_COMPLETE) {
DupLog::trace("PACKAGE {$package->getId()}:STORAGE PROCESSING");
$package->setStatus(AbstractPackage::STATUS_STORAGE_PROCESSING);
$package->processStorages();
$end_time = time();
$build_time = $end_time - $start_time;
DupLog::trace("STORAGE CHUNK PROCESSING TIME=$build_time seconds");
if ($package->getStatus() == AbstractPackage::STATUS_COMPLETE) {
DupLog::trace("PACKAGE {$package->getId()} COMPLETE");
} elseif ($package->getStatus() == AbstractPackage::STATUS_ERROR) {
DupLog::trace("PACKAGE {$package->getId()} IN ERROR STATE");
}
$packageCompleteStatuses = [
AbstractPackage::STATUS_COMPLETE,
AbstractPackage::STATUS_ERROR,
];
if (in_array($package->getStatus(), $packageCompleteStatuses)) {
$info = "\n";
$info .= "********************************************************************************\n";
$info .= "********************************************************************************\n";
$info .= "DUPLICATOR PRO PACKAGE CREATION OR MANUAL STORAGE TRANSFER END: " . @date("Y-m-d H:i:s") . "\n";
$info .= "NOTICE: Do NOT post to public sites or forums \n";
$info .= "********************************************************************************\n";
$info .= "********************************************************************************\n";
DupLog::infoTrace($info);
}
if ($package->getStatus() == AbstractPackage::STATUS_ERROR) {
throw new Exception("Storage failed");
}
}
}
}

View File

@@ -0,0 +1,318 @@
<?php
/**
* @package Duplicator
* @copyright (c) 2022, Snap Creek LLC
*/
namespace Duplicator\Addons\WpCliAddon;
use Duplicator\Models\GlobalEntity;
use Duplicator\Utils\Logging\DupLog;
use Duplicator\Package\DupPackage;
use Duplicator\Models\TemplateEntity;
use Duplicator\Package\AbstractPackage;
use Duplicator\Libs\Snap\SnapWP;
use Duplicator\Models\Storages\StoragesUtil;
use Duplicator\Package\Archive\PackageArchive;
use Duplicator\Package\PackageUtils;
use Duplicator\Utils\LockUtil;
use Duplicator\Package\Storage\Status\StatusChecker;
use Error;
use Exception;
use WP_CLI;
class DuplicatorCli
{
const STORAGES_UPDATE_MIN_INTERVAL = 10;
/**
* DuplicatorCli constructor.
*/
public function __construct()
{
if (defined('WP_CLI') && WP_CLI) {
WP_CLI::add_command('duplicator', self::class);
}
}
/**
* Duplicator Info
*
* @return void
*/
public function info(): void
{
WP_CLI::line('Duplicator WP-CLI Addon');
WP_CLI::line('Duplicator Version: ' . DUPLICATOR_VERSION);
WP_CLI::line('PHP Version: ' . PHP_VERSION);
global $wp_version;
WP_CLI::line('WordPress Version: ' . $wp_version);
}
/**
* Check and update backup storage status, check if the archive file is available in the storage.
*
* ## OPTIONS
*
* --minInterval=<seconds>
* Minimum time interval in seconds between status checks for each backup. Default is 10 minutes (600 seconds).
* Only backups that haven't been checked within this interval will be processed. Minimum allowed value is 10 seconds.
*
* --delete
* Delete all backups that don't have at least one storage or error status.
*
* ## EXAMPLES
*
* wp duplicator storagesUpdate
* wp duplicator storagesUpdate --minInterval=30
* wp duplicator storagesUpdate --delete
*
* @param scalar[] $args Command arguments
* @param array<string,scalar> $assocArgs Command options
*
* @return void
*/
public function storagesUpdate($args, $assocArgs = []): void
{
try {
WP_CLI::log('Starting remote backup status check...');
$minInterval = max(self::STORAGES_UPDATE_MIN_INTERVAL, $assocArgs['minInterval'] ?? StatusChecker::MIN_INTERVAL_WP_CLI);
$delete = isset($assocArgs['delete']) && (bool)$assocArgs['delete'];
WP_CLI::log('Skipping backups checked in the last ' . $minInterval . ' seconds');
if ($delete) {
WP_CLI::log('Deleting backups that don\'t have at least one storage or error status...');
}
$totalProcessed = 0;
do {
if (($processed = StatusChecker::processNextChunk($minInterval)) < 0) {
throw new Exception('Failed to process next chunk');
}
WP_CLI::log('Processing ... total processed ' . $totalProcessed);
$totalProcessed += $processed;
} while ($processed > 0);
WP_CLI::success(sprintf('Successfully checked %d backups.', $totalProcessed));
if ($delete) {
WP_CLI::log('Deleting backups that don\'t have at least one storage or error status...');
$totalDeleted = 0;
do {
$deleted = PackageUtils::bulkDeletePackageWithoutStoragesChunk();
$totalDeleted += $deleted;
WP_CLI::log('Deleting ... total deleted ' . $totalDeleted);
} while ($deleted > 0);
WP_CLI::success(sprintf('Successfully deleted %d backups that don\'t have at least one storage or error status.', $totalDeleted));
}
} catch (Exception | Error $e) {
WP_CLI::error($e->getMessage());
}
}
/**
* Build Duplicator Backup
*
* Options
*
* --template=<ID>
* The template id to use, if not specified the default template will be used
*
* --dir=<path>
* The directory to copy the backup to, if not specified the backup will not be copied
*
* --delete
* Delete the package after copying
*
* --phpsqldump
* If true use phpdump instead of mysqldump, default false and use mysqldump (Not implemented yet)
*
* --phpzip
* If true use php zip instead shell zip, default false and use shell zip
*
* --duparchive
* If true use dup archive engine, default false and use shell zip
*
* @param scalar[] $args Command arguments
* @param array<string,scalar> $assocArgs Command options
*
* @return void
*/
public function build($args, $assocArgs = []): void
{
try {
$failed = false;
WP_CLI::debug("Excecute Backup Build");
DupLog::trace('WP-CLI: Backup Build Command');
$assocArgs = wp_parse_args(
$assocArgs,
[
'phpsqldump' => false, // Not implemented yet
'phpzip' => false, // If true use php zip instead shell zip, default false and use shell zip
'duparchive' => false, // If true use dup archive engine, default false and use shell zip
'template' => 0, // The template id to use, if not specified the default template will be used
'dir' => '', // The directory to copy the backup to, if not specified the backup will not be copied
'delete' => false, // Delete the package after copying
]
);
$tempalteId = $assocArgs['template'];
if (!is_writable(DUPLICATOR_SSDIR_PATH_TMP)) {
throw new Exception('Current user does not have permission to write to the Duplicator temporary directory');
}
if (strlen($assocArgs['dir']) > 0 && !is_dir($assocArgs['dir'])) {
throw new Exception('The directory specified does not exist');
}
if (strlen($assocArgs['dir']) > 0 && !is_writable($assocArgs['dir'])) {
throw new Exception('The directory specified is not writable');
}
if ($tempalteId == 0) {
if (($template = TemplateEntity::getDefaultTemplate()) == null) {
throw new Exception('No default template found');
}
} else {
if (($template = TemplateEntity::getById($tempalteId)) == null) {
throw new Exception("Template {$tempalteId} not found");
}
}
$homePath = SnapWP::getHomePath();
if (!is_dir($homePath) || chdir($homePath) == false) {
throw new Exception("Failed to change directory to {$homePath}");
}
if (!LockUtil::lockProcess()) {
DupLog::trace("File locked so skipping");
throw new Exception("Another cron already running so skipping");
}
$global = GlobalEntity::getInstance();
if ($assocArgs['phpzip']) {
$archiveMode = PackageArchive::BUILD_MODE_ZIP_ARCHIVE;
} elseif ($assocArgs['duparchive']) {
$archiveMode = PackageArchive::BUILD_MODE_DUP_ARCHIVE;
} else {
$archiveMode = PackageArchive::BUILD_MODE_SHELL_EXEC;
}
$global->setArchiveMode($archiveMode, PackageArchive::ZIP_MODE_SINGLE_THREAD, true, true);
$global->setDbMode('mysql');
$package = new DupPackage(
DupPackage::EXEC_TYPE_MANUAL,
[StoragesUtil::getDefaultStorageId()],
$template,
null
);
$package->save();
WP_CLI::success("Building Backup[{$package->getId()}] {$package->getName()} ...");
DupLog::trace('WP-CLI: Backup Build Command: Start build');
WP_CLI::debug("Run Process");
do {
BackupBuild::process($package);
WP_CLI::debug("Run build end Package status " . $package->getStatus());
if ($package->getStatus() < AbstractPackage::STATUS_PRE_PROCESS) {
throw new Exception('Package status error: ' . $package->getStatus());
}
} while ($package->getStatus() < AbstractPackage::STATUS_COMPLETE);
$package->save();
if (strlen($assocArgs['dir']) > 0) {
$backupFile = $package->getLocalPackageFilePath(AbstractPackage::FILE_TYPE_ARCHIVE);
$installerFile = $package->getLocalPackageFilePath(AbstractPackage::FILE_TYPE_INSTALLER);
$targetDir = $assocArgs['dir'];
if (!file_exists($backupFile)) {
WP_CLI::warning("Backup file not found: {$backupFile}");
$failed = true;
} else {
$backupFileTarget = $targetDir . '/' . basename($backupFile);
if (!copy($backupFile, $backupFileTarget)) {
WP_CLI::warning("Failed to copy backup file to {$backupFileTarget}");
$failed = true;
}
}
if (!file_exists($installerFile)) {
WP_CLI::warning("Installer file not found: {$installerFile}");
$failed = true;
} else {
$installerFileTarget = $targetDir . '/' . basename($installerFile);
if (!copy($installerFile, $installerFileTarget)) {
WP_CLI::warning("Failed to copy installer file to {$installerFileTarget}");
$failed = true;
}
}
if (!$failed) {
WP_CLI::success("Backup files copied to {$targetDir}");
}
}
if ($assocArgs['delete']) {
$package->delete();
WP_CLI::success("Backup {$package->getName()} Deleted");
}
WP_CLI::success("Backup {$package->getName()} Build Completed");
} catch (Exception | Error $e) {
WP_CLI::warning($e->getMessage());
$failed = true;
} finally {
LockUtil::unlockProcess();
DupLog::trace('WP-CLI: Backup Build Command end');
DupLog::close();
}
if ($failed) {
WP_CLI::error("Command Backup Build Failed");
} else {
WP_CLI::success("Command Backup Build Completed");
}
}
/**
* Duplicator Full Cleanup
* Remove all Duplicator backup files and temporary files
*
* @return void
*/
public function cleanup(): void
{
try {
// first last package id
$ids = DupPackage::getIdsByStatus();
foreach ($ids as $id) {
$package = DupPackage::getById($id);
WP_CLI::line("Delete Backup[{$package->getId()}] {$package->getName()}");
// A smooth deletion is not performed because it is a forced reset.
DupPackage::forceDelete($id);
}
foreach (PackageUtils::getOrphanedPackageFiles() as $filepath) {
if (is_writable($filepath)) {
WP_CLI::line("Delete Orphaned Backup File: {$filepath}");
unlink($filepath);
} else {
WP_CLI::warning("Failed to delete Orphaned Backup File: {$filepath}");
}
}
PackageUtils::tmpCleanup(true);
} catch (Exception | Error $e) {
WP_CLI::warning($e->getMessage());
} finally {
DupLog::trace('WP-CLI: Backup Build Command end');
DupLog::close();
}
WP_CLI::success("Build Cleanup Completed");
}
}