getTask()->func([$this, '_prepareWorkingSpace']); $this->getTask()->func([$this, '_dumpDB']); $this->getTask()->func([$this, '_archiveFiles'], [rtrim(Factory::getWPHelper()->getWordPressHomedir(), JetBackup::SEP)]); $this->getTask()->func([$this, '_compressFiles']); $this->getTask()->func([$this, '_transferToDestination']); if($this->getQueueItem()->getStatus() < Queue::STATUS_DONE) { $backup_destinations = sizeof($this->getBackupJob()->getDestinations()); $queue_destinations = sizeof($this->getQueueItemBackup()->getDestinations()); if($backup_destinations == $queue_destinations && !$this->getQueueItem()->getErrors()) $this->getQueueItem()->updateStatus(Queue::STATUS_DONE); else $this->getQueueItem()->updateStatus(Queue::STATUS_PARTIALLY); } $this->getLogController()->logMessage('Completed!'); $this->getQueueItem()->updateProgress( 'Backup Completed! '. '['.$this->getBackupJob()->getName().']', QueueItem::PROGRESS_LAST_STEP); $this->getBackupJob()->calculateNextRun(); $this->getBackupJob()->setLastRun(time()); $this->getBackupJob()->save(); //Send notification Notification::message() ->addParam('backup_domain', Wordpress::getSiteDomain()) ->addParam('job_name', $this->getBackupJob()->getName()) ->addParam('backup_date', Util::date('Y-m-d H:i:s', $this->getBackupJob()->getLastRun())) ->addParam('backup_status', Queue::STATUS_NAMES[$this->getQueueItem()->getStatus()]) ->send('JetBackup Completed', 'job_completed'); //Add retention cleanup to the queue after job is done; //This has to run AFTER reindex because the data is based on reindex table $this->_retentionCleanup(); //Add to queue backups that have 'After job is done' $this->_calculateAfterJobDone(); } catch( Exception $e) { $this->getQueueItem()->updateStatus(Queue::STATUS_FAILED); $this->getLogController()->logError($e->getMessage()); $this->getLogController()->logMessage('Failed!'); $progress = $this->getQueueItem()->getProgress(); $progress->setMessage('Backup Failed!'); $progress->setCurrentItem(7); $this->getQueueItem()->save(); $this->getBackupJob()->calculateNextRun(); $this->getBackupJob()->setLastRun(time()); $this->getBackupJob()->save(); } $this->getLogController()->logMessage('Total time: ' . $this->getTask()->getExecutionTimeElapsed()); } public function _transferToDestination() { $this->getLogController()->logMessage('Execution time: ' . $this->getTask()->getExecutionTimeElapsed()); $this->getLogController()->logMessage('TTL time: ' . $this->getTask()->getExecutionTimeLimit()); $this->getQueueItem()->updateStatus(Queue::STATUS_BACKUP_ACCOUNT_SEND_TO_DESTINATION); $this->getQueueItem()->updateProgress("Transferring backup to destinations"); parent::_transferToDestination(); } public function _compressFiles() { if(!($this->getBackupJob()->getContains() & BackupJob::BACKUP_ACCOUNT_CONTAINS_HOMEDIR) || !Factory::getSettingsPerformance()->isGzipCompressArchive()) { $this->getLogController()->logMessage( 'GZIP Compression is disabled, skipping!' ); $this->getQueueItem()->updateStatus(Queue::STATUS_BACKUP_ACCOUNT_SEND_TO_DESTINATION); return; } $this->getLogController()->logMessage('Execution time: ' . $this->getTask()->getExecutionTimeElapsed()); $this->getLogController()->logMessage('TTL time: ' . $this->getTask()->getExecutionTimeLimit()); $this->getQueueItem()->updateStatus(Queue::STATUS_BACKUP_ACCOUNT_COMPRESSING); $this->getQueueItem()->updateProgress("Compressing backup"); parent::_compressFiles(); } /** * @throws ArchiveException */ public function _archiveFiles($source):void { if (!( $this->getBackupJob()->getContains() & BackupJob::BACKUP_ACCOUNT_CONTAINS_HOMEDIR)) { $this->getLogController()->logMessage('Skipping files backup'); return; } $this->getLogController()->logMessage('Execution time: ' . $this->getTask()->getExecutionTimeElapsed()); $this->getLogController()->logMessage('TTL time: ' . $this->getTask()->getExecutionTimeLimit()); $this->getQueueItem()->updateStatus(Queue::STATUS_BACKUP_ACCOUNT_ARCHIVING); $this->getQueueItem()->getProgress()->setSubMessage(''); $this->getQueueItem()->updateProgress("Archiving data"); parent::_archiveFiles($source); } /** * @return void */ public function _prepareWorkingSpace():void { $this->getLogController()->logMessage('Execution time: ' . $this->getTask()->getExecutionTimeElapsed()); $this->getLogController()->logMessage('TTL time: ' . $this->getTask()->getExecutionTimeLimit()); $this->getQueueItem()->updateStatus(Queue::STATUS_PREPARING); $this->getQueueItem()->updateProgress("Preparing"); $this->getLogController()->logMessage('Status 2 Preparing work space'); $this->getLogController()->logMessage("Execution mode: " . (Helper::isCLI() ? "CLI" : "WEB")); $this->getLogController()->logMessage("Plugin Version: " . JetBackup::VERSION); $this->getLogController()->logMessage("\t -- Backup Environment:"); foreach ( System::getSystemInfo() as $key => $value ) { $this->getLogController()->logMessage("\t\t$key: $value"); } $this->_createWorkspace(); $this->_exportConfig(); } private function _exportConfig () { // export WordPress wp-config & htaccess file $helper = Factory::getWPHelper(); $config_files = [ sprintf(self::HTACCESS_FILE, $helper->getWordPressHomedir()), sprintf(self::WP_CONFIG_FILE, $helper->getWordPressHomedir()), ]; foreach ($config_files as $file) { if (!file_exists($file)) continue; $target = $this->getSnapshotDirectory() . JetBackup::SEP . Snapshot::SKELETON_CONFIG_DIRNAME . JetBackup::SEP . basename($file); copy($file, $target); chmod($target, 0600); } } protected function getSnapshotItems():array { $multisite = []; foreach(Wordpress::getMultisiteBlogs() as $blog) $multisite[] = $blog->getData(); $db_prefix = Wordpress::getDB()->getPrefix(); $output = []; $path = self::SKELETON_FILES_DIRNAME . JetBackup::SEP . Snapshot::SKELETON_FILES_ARCHIVE_NAME . (Factory::getSettingsPerformance()->isGzipCompressArchive() ? '.gz' : ''); $homedir = $this->getSnapshotDirectory() . JetBackup::SEP . $path; $size = file_exists($homedir) ? filesize($homedir) : 0; // Files Item $item = new SnapshotItem(); $item->setBackupType(BackupJob::TYPE_ACCOUNT); $item->setBackupContains(BackupJob::BACKUP_ACCOUNT_CONTAINS_HOMEDIR); $item->setCreated(time()); $item->setName(''); $item->setSize($size); $item->setPath($path); $output[] = $item; // Database Tables Items foreach($this->_getDBTables() as $table) { $path = self::SKELETON_DATABASE_DIRNAME . JetBackup::SEP . $table . '.sql' . (Factory::getSettingsPerformance()->isGzipCompressDB() ? '.gz' : ''); $file = $this->getSnapshotDirectory() . JetBackup::SEP . $path; $size = file_exists($file) ? filesize($file) : 0; $item = new SnapshotItem(); $item->setBackupType(BackupJob::TYPE_ACCOUNT); $item->setBackupContains(BackupJob::BACKUP_ACCOUNT_CONTAINS_DATABASE); $item->setCreated(time()); $item->setName($table); $item->setSize($size); $item->setPath($path); $item->addParam(Snapshot::PARAM_DB_PREFIX, $db_prefix); $item->addParam(Snapshot::PARAM_DB_EXCLUDED, in_array($table, $this->getBackupJob()->getExcludeDatabases())); $output[] = $item; } // Full Item $item = new SnapshotItem(); $item->setBackupType(BackupJob::TYPE_ACCOUNT); $item->setBackupContains(BackupJob::BACKUP_ACCOUNT_CONTAINS_FULL); $item->setCreated(time()); $item->setName(''); $item->setPath(''); $item->setSize(0); $item->addParam(Snapshot::PARAM_MULTISITE, $multisite); $item->addParam(Snapshot::PARAM_SITE_URL, Wordpress::getSiteURL()); $output[] = $item; return $output; } /** * @return array */ public function _getDBTables():array { return $this->getTask()->func(function () { return Wordpress::getDB()->listTables(); }, [], '_getDBTables'); } /** * @return array */ public function _getExcludedTables(): array { return $this->getTask()->func(function () { $excludes = $this->getBackupJob()->getExcludeDatabases(); if (Factory::getSettingsPerformance()->isUseDefaultDBExcludes()) { $tables = $this->_getDBTables(); foreach (self::DEFAULT_EXCLUDE_TABLES as $table) { $table = Wordpress::getDB()->getPrefix() . $table; if (!in_array($table, $tables)) continue; $excludes[] = $table; } } return $excludes; }, [], '_getExcludedTables'); } /** * @throws Exception */ public function _dumpDB() { $log = $this->getLogController(); if (!($this->getBackupJob()->getContains() & BackupJob::BACKUP_ACCOUNT_CONTAINS_DATABASE)) { $log->logMessage('Skipping database backup'); return; } $queueItem = $this->getQueueItem(); $log->logDebug('[_dumpDB]'); $log->logMessage('Execution time: ' . $this->getTask()->getExecutionTimeElapsed()); $log->logMessage('TTL time: ' . $this->getTask()->getExecutionTimeLimit()); $queueItem->updateStatus(Queue::STATUS_BACKUP_ACCOUNT_DUMPING_DB); $queueItem->updateProgress("Dumping database"); $db_dump_folder = $this->getSnapshotDirectory() . JetBackup::SEP . BackupAccount::SKELETON_DATABASE_DIRNAME; if (Factory::getSettingsPerformance()->isSQLCleanupRevisionsEnabled()) { $this->getTask()->func(function() use ($log){ $log->logMessage('Revision cleanup is enabled, starting revision cleanup'); Wordpress::getDB()->clearPostRevisions(); $log->logMessage('Revision cleanup is done'); }, [], '_revisionCleanup'); } $this->getTask()->foreach($this->_getDBTables(), function($i, $tableName, $totalTables) use ($db_dump_folder, $log, $queueItem) { $currentCount = ($i+1); // Dump table $this->getTask()->func(function () use ($tableName, $totalTables, $db_dump_folder, $log, $queueItem, $currentCount) { $dump = new Mysqldump(DB_NAME, DB_USER, DB_PASSWORD, DB_HOST); $dump->setInclude([$tableName]); $dump->setExclude($this->_getExcludedTables()); $dump->setLogController($log); $log->logMessage("Exporting $tableName [$currentCount/$totalTables]"); $dump->start($db_dump_folder . JetBackup::SEP . $tableName . '.sql'); $progress = $queueItem->getProgress(); $progress->setSubMessage("Exporting $tableName"); $queueItem->save(); $this->getTask()->checkExecutionTime(function () use ($log, $queueItem) { $log->logDebug("[_dumpDB] exitOnLimitReached triggered"); $queueItem->getProgress()->setMessage('[ DB Dump ] Waiting for next cron iteration'); $queueItem->save(); }); $log->logDebug("[_dumpDB] Finished export loop, Current: $tableName, Total: $totalTables"); }, [], '_dumpTable' . $tableName); // Compress table $this->getTask()->func(function () use ($tableName, $totalTables, $db_dump_folder, $log, $queueItem, $currentCount) { if (!Factory::getSettingsPerformance()->isGzipCompressDB()) { $log->logMessage('Skipping database compression, option is disabled.'); return; } $currentFile = $db_dump_folder . JetBackup::SEP . $tableName . ".sql"; $log->logMessage("Compressing $tableName [$currentCount/$totalTables]"); if(!file_exists($currentFile)) { $log->logError("File $currentFile does not exist."); return; } Gzip::compress( $currentFile, Gzip::DEFAULT_COMPRESS_CHUNK_SIZE, Gzip::DEFAULT_COMPRESSION_LEVEL, function($byteRead) use ($tableName) { $progress = $this->getQueueItem()->getProgress(); $progress->setSubMessage("Compressing $tableName [$byteRead bytes]"); $this->getQueueItem()->save(); $this->getTask()->checkExecutionTime(function() { $this->getQueueItem()->getProgress()->setMessage('[ Gzip ] Waiting for next cron iteration'); $this->getQueueItem()->save(); }); } ); $log->logDebug("[_compressDB] Finished compress loop, Current: $tableName, Total: $totalTables"); }, [], '_compressTable' . $tableName); $progress = $queueItem->getProgress(); $progress->setTotalSubItems($totalTables); $progress->setCurrentSubItem($currentCount); $queueItem->save(); }); } }