PluginProbe ʕ •ᴥ•ʔ
JetBackup – Backup, Restore & Migrate / 3.1.22.3
JetBackup – Backup, Restore & Migrate v3.1.22.3
3.1.22.3 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.8.1 1.4.9 1.5.0 1.5.1 1.5.1.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.10 1.6.11 1.6.12 1.6.13 1.6.15 1.6.5.1 1.6.8.8 1.6.9 1.6.9.1 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7.5 2.0.8.7 2.0.9.11 2.0.9.14 2.0.9.15 2.0.9.6 2.0.9.7 2.0.9.9 3.1.10.7 3.1.11.1 3.1.12.3 3.1.13.4 3.1.14.17 3.1.15.4 3.1.16.1 3.1.17.5 3.1.18.10 3.1.18.8 3.1.18.9 3.1.19.8 3.1.20.3 3.1.21.3 3.1.7.9 3.1.9.2 trunk 1.1.90 1.1.91 1.2.0 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2
backup / src / JetBackup / Backup / BackupAccount.php
backup / src / JetBackup / Backup Last commit date
.htaccess 1 year ago Backup.php 4 months ago BackupAccount.php 1 year ago BackupConfig.php 1 year ago index.html 1 year ago web.config 1 year ago
BackupAccount.php
443 lines
1 <?php
2
3 namespace JetBackup\Backup;
4
5 use Exception;
6 use JetBackup\Archive\Gzip;
7 use JetBackup\BackupJob\BackupJob;
8 use JetBackup\Config\System;
9 use JetBackup\Data\Mysqldump;
10 use JetBackup\Entities\Util;
11 use JetBackup\Exception\ArchiveException;
12 use JetBackup\Exception\DBException;
13 use JetBackup\Exception\JBException;
14 use JetBackup\Factory;
15 use JetBackup\JetBackup;
16 use JetBackup\Notification\Notification;
17 use JetBackup\Queue\Queue;
18 use JetBackup\Queue\QueueItem;
19 use JetBackup\Snapshot\Snapshot;
20 use JetBackup\Snapshot\SnapshotItem;
21 use JetBackup\Wordpress\Helper;
22 use JetBackup\Wordpress\Wordpress;
23 use SleekDB\Exceptions\InvalidArgumentException;
24 use SleekDB\Exceptions\IOException;
25
26 if (!defined( '__JETBACKUP__')) die('Direct access is not allowed');
27
28 class BackupAccount extends Backup {
29
30 const SKELETON_FILES_DIRNAME = 'files';
31 const SKELETON_DATABASE_DIRNAME = 'database';
32
33 const DEFAULT_EXCLUDE_TABLES = [
34 // Shield security
35 'icwp_wpsf_events',
36 'icwp_wpsf_audit_trail',
37 'icwp_wpsf_sessions',
38 'icwp_wpsf_scan_results',
39 'icwp_wpsf_scan_items',
40 'icwp_wpsf_lockdown',
41
42 // Woocommerce
43 'woocommerce_sessions',
44 'actionscheduler_logs',
45 'woocommerce_log',
46
47 // Yoast
48 'yoast_seo_links',
49 'yoast_seo_meta',
50
51 // Wordfence
52 'wfLiveTrafficHuman',
53 'wfBlockedIPLog',
54 'wfCrawlers',
55 'wfFileChanges',
56 'wfFileMods',
57 'wfHits',
58 'wfIssues',
59 'wfKnownFileList',
60 'wfLocs',
61 'wfLogins',
62 'wfNet404s',
63 'wfNotifications',
64 'wfPendingIssues',
65 'wfReverseCache',
66 'wfSNIPCache',
67 'wfStatus',
68 'wfTrafficRates',
69
70 // UpdraftPlus (Temporary data for backup jobs)
71 'updraft_jobdata',
72
73 //Activity Log Plugins
74 'aryo_activity_log',
75 'wsal_occurrences',
76 'simple_history',
77 'wpml_mails',
78
79 //Redirection Plugins
80 'redirection_logs',
81 'redirection_404',
82
83 //WP Statistics
84 'statistics_events',
85 'statistics_exclusions',
86 'statistics_historical',
87 'statistics_pages',
88 'statistics_useronline',
89 'statistics_visit',
90 'statistics_visitor',
91 'statistics_visitor_relationships'
92 ];
93
94 /**
95 * @throws IOException
96 * @throws InvalidArgumentException|JBException
97 */
98 public function execute():void {
99
100 try {
101
102
103 $this->getTask()->func([$this, '_prepareWorkingSpace']);
104 $this->getTask()->func([$this, '_dumpDB']);
105 $this->getTask()->func([$this, '_archiveFiles'], [rtrim(Factory::getWPHelper()->getWordPressHomedir(), JetBackup::SEP)]);
106 $this->getTask()->func([$this, '_compressFiles']);
107 $this->getTask()->func([$this, '_transferToDestination']);
108
109 if($this->getQueueItem()->getStatus() < Queue::STATUS_DONE) {
110 $backup_destinations = sizeof($this->getBackupJob()->getDestinations());
111 $queue_destinations = sizeof($this->getQueueItemBackup()->getDestinations());
112
113 if($backup_destinations == $queue_destinations && !$this->getQueueItem()->getErrors()) $this->getQueueItem()->updateStatus(Queue::STATUS_DONE);
114 else $this->getQueueItem()->updateStatus(Queue::STATUS_PARTIALLY);
115 }
116
117 $this->getLogController()->logMessage('Completed!');
118
119 $this->getQueueItem()->updateProgress( 'Backup Completed! '. '['.$this->getBackupJob()->getName().']', QueueItem::PROGRESS_LAST_STEP);
120
121 $this->getBackupJob()->calculateNextRun();
122 $this->getBackupJob()->setLastRun(time());
123 $this->getBackupJob()->save();
124
125 //Send notification
126 Notification::message()
127 ->addParam('backup_domain', Wordpress::getSiteDomain())
128 ->addParam('job_name', $this->getBackupJob()->getName())
129 ->addParam('backup_date', Util::date('Y-m-d H:i:s', $this->getBackupJob()->getLastRun()))
130 ->addParam('backup_status', Queue::STATUS_NAMES[$this->getQueueItem()->getStatus()])
131 ->send('JetBackup Completed', 'job_completed');
132
133 //Add retention cleanup to the queue after job is done;
134 //This has to run AFTER reindex because the data is based on reindex table
135 $this->_retentionCleanup();
136
137 //Add to queue backups that have 'After job is done'
138 $this->_calculateAfterJobDone();
139 } catch( Exception $e) {
140 $this->getQueueItem()->updateStatus(Queue::STATUS_FAILED);
141 $this->getLogController()->logError($e->getMessage());
142 $this->getLogController()->logMessage('Failed!');
143
144 $progress = $this->getQueueItem()->getProgress();
145
146 $progress->setMessage('Backup Failed!');
147 $progress->setCurrentItem(7);
148 $this->getQueueItem()->save();
149
150 $this->getBackupJob()->calculateNextRun();
151 $this->getBackupJob()->setLastRun(time());
152 $this->getBackupJob()->save();
153 }
154
155 $this->getLogController()->logMessage('Total time: ' . $this->getTask()->getExecutionTimeElapsed());
156 }
157
158 public function _transferToDestination() {
159
160 $this->getLogController()->logMessage('Execution time: ' . $this->getTask()->getExecutionTimeElapsed());
161 $this->getLogController()->logMessage('TTL time: ' . $this->getTask()->getExecutionTimeLimit());
162
163 $this->getQueueItem()->updateStatus(Queue::STATUS_BACKUP_ACCOUNT_SEND_TO_DESTINATION);
164 $this->getQueueItem()->updateProgress("Transferring backup to destinations");
165
166 parent::_transferToDestination();
167 }
168
169 public function _compressFiles() {
170
171 if(!($this->getBackupJob()->getContains() & BackupJob::BACKUP_ACCOUNT_CONTAINS_HOMEDIR) || !Factory::getSettingsPerformance()->isGzipCompressArchive()) {
172 $this->getLogController()->logMessage( 'GZIP Compression is disabled, skipping!' );
173 $this->getQueueItem()->updateStatus(Queue::STATUS_BACKUP_ACCOUNT_SEND_TO_DESTINATION);
174 return;
175 }
176
177 $this->getLogController()->logMessage('Execution time: ' . $this->getTask()->getExecutionTimeElapsed());
178 $this->getLogController()->logMessage('TTL time: ' . $this->getTask()->getExecutionTimeLimit());
179
180 $this->getQueueItem()->updateStatus(Queue::STATUS_BACKUP_ACCOUNT_COMPRESSING);
181 $this->getQueueItem()->updateProgress("Compressing backup");
182
183 parent::_compressFiles();
184 }
185
186 /**
187 * @throws ArchiveException
188 */
189 public function _archiveFiles($source):void {
190
191 if (!( $this->getBackupJob()->getContains() & BackupJob::BACKUP_ACCOUNT_CONTAINS_HOMEDIR)) {
192 $this->getLogController()->logMessage('Skipping files backup');
193 return;
194 }
195
196 $this->getLogController()->logMessage('Execution time: ' . $this->getTask()->getExecutionTimeElapsed());
197 $this->getLogController()->logMessage('TTL time: ' . $this->getTask()->getExecutionTimeLimit());
198
199 $this->getQueueItem()->updateStatus(Queue::STATUS_BACKUP_ACCOUNT_ARCHIVING);
200 $this->getQueueItem()->getProgress()->setSubMessage('');
201 $this->getQueueItem()->updateProgress("Archiving data");
202
203 parent::_archiveFiles($source);
204 }
205
206 /**
207 * @return void
208 */
209 public function _prepareWorkingSpace():void {
210
211 $this->getLogController()->logMessage('Execution time: ' . $this->getTask()->getExecutionTimeElapsed());
212 $this->getLogController()->logMessage('TTL time: ' . $this->getTask()->getExecutionTimeLimit());
213
214 $this->getQueueItem()->updateStatus(Queue::STATUS_PREPARING);
215 $this->getQueueItem()->updateProgress("Preparing");
216 $this->getLogController()->logMessage('Status 2 Preparing work space');
217 $this->getLogController()->logMessage("Execution mode: " . (Helper::isCLI() ? "CLI" : "WEB"));
218 $this->getLogController()->logMessage("Plugin Version: " . JetBackup::VERSION);
219 $this->getLogController()->logMessage("\t -- Backup Environment:");
220 foreach ( System::getSystemInfo() as $key => $value ) {
221 $this->getLogController()->logMessage("\t\t$key: $value");
222 }
223
224 $this->_createWorkspace();
225 $this->_exportConfig();
226 }
227
228
229 private function _exportConfig () {
230 // export WordPress wp-config & htaccess file
231
232 $helper = Factory::getWPHelper();
233
234 $config_files = [
235 sprintf(self::HTACCESS_FILE, $helper->getWordPressHomedir()),
236 sprintf(self::WP_CONFIG_FILE, $helper->getWordPressHomedir()),
237 ];
238
239 foreach ($config_files as $file) {
240 if (!file_exists($file)) continue;
241
242 $target = $this->getSnapshotDirectory() . JetBackup::SEP . Snapshot::SKELETON_CONFIG_DIRNAME . JetBackup::SEP . basename($file);
243 copy($file, $target);
244 chmod($target, 0600);
245 }
246 }
247
248 protected function getSnapshotItems():array {
249
250 $multisite = [];
251 foreach(Wordpress::getMultisiteBlogs() as $blog) $multisite[] = $blog->getData();
252 $db_prefix = Wordpress::getDB()->getPrefix();
253
254 $output = [];
255
256 $path = self::SKELETON_FILES_DIRNAME . JetBackup::SEP . Snapshot::SKELETON_FILES_ARCHIVE_NAME . (Factory::getSettingsPerformance()->isGzipCompressArchive() ? '.gz' : '');
257 $homedir = $this->getSnapshotDirectory() . JetBackup::SEP . $path;
258 $size = file_exists($homedir) ? filesize($homedir) : 0;
259
260 // Files Item
261 $item = new SnapshotItem();
262 $item->setBackupType(BackupJob::TYPE_ACCOUNT);
263 $item->setBackupContains(BackupJob::BACKUP_ACCOUNT_CONTAINS_HOMEDIR);
264 $item->setCreated(time());
265 $item->setName('');
266 $item->setSize($size);
267 $item->setPath($path);
268 $output[] = $item;
269
270 // Database Tables Items
271 foreach($this->_getDBTables() as $table) {
272
273 $path = self::SKELETON_DATABASE_DIRNAME . JetBackup::SEP . $table . '.sql' . (Factory::getSettingsPerformance()->isGzipCompressDB() ? '.gz' : '');
274 $file = $this->getSnapshotDirectory() . JetBackup::SEP . $path;
275 $size = file_exists($file) ? filesize($file) : 0;
276
277 $item = new SnapshotItem();
278 $item->setBackupType(BackupJob::TYPE_ACCOUNT);
279 $item->setBackupContains(BackupJob::BACKUP_ACCOUNT_CONTAINS_DATABASE);
280 $item->setCreated(time());
281 $item->setName($table);
282 $item->setSize($size);
283 $item->setPath($path);
284 $item->addParam(Snapshot::PARAM_DB_PREFIX, $db_prefix);
285 $item->addParam(Snapshot::PARAM_DB_EXCLUDED, in_array($table, $this->getBackupJob()->getExcludeDatabases()));
286 $output[] = $item;
287 }
288
289 // Full Item
290 $item = new SnapshotItem();
291 $item->setBackupType(BackupJob::TYPE_ACCOUNT);
292 $item->setBackupContains(BackupJob::BACKUP_ACCOUNT_CONTAINS_FULL);
293 $item->setCreated(time());
294 $item->setName('');
295 $item->setPath('');
296 $item->setSize(0);
297 $item->addParam(Snapshot::PARAM_MULTISITE, $multisite);
298 $item->addParam(Snapshot::PARAM_SITE_URL, Wordpress::getSiteURL());
299
300 $output[] = $item;
301
302 return $output;
303 }
304
305
306 /**
307 * @return array
308 */
309 public function _getDBTables():array {
310 return $this->getTask()->func(function () {
311 return Wordpress::getDB()->listTables();
312 }, [], '_getDBTables');
313 }
314
315 /**
316 * @return array
317 */
318 public function _getExcludedTables(): array {
319 return $this->getTask()->func(function () {
320 $excludes = $this->getBackupJob()->getExcludeDatabases();
321 if (Factory::getSettingsPerformance()->isUseDefaultDBExcludes()) {
322 $tables = $this->_getDBTables();
323 foreach (self::DEFAULT_EXCLUDE_TABLES as $table) {
324 $table = Wordpress::getDB()->getPrefix() . $table;
325 if (!in_array($table, $tables)) continue;
326 $excludes[] = $table;
327 }
328 }
329 return $excludes;
330 }, [], '_getExcludedTables');
331 }
332
333 /**
334 * @throws Exception
335 */
336 public function _dumpDB() {
337
338 $log = $this->getLogController();
339
340 if (!($this->getBackupJob()->getContains() & BackupJob::BACKUP_ACCOUNT_CONTAINS_DATABASE)) {
341 $log->logMessage('Skipping database backup');
342 return;
343 }
344
345 $queueItem = $this->getQueueItem();
346
347 $log->logDebug('[_dumpDB]');
348 $log->logMessage('Execution time: ' . $this->getTask()->getExecutionTimeElapsed());
349 $log->logMessage('TTL time: ' . $this->getTask()->getExecutionTimeLimit());
350
351 $queueItem->updateStatus(Queue::STATUS_BACKUP_ACCOUNT_DUMPING_DB);
352 $queueItem->updateProgress("Dumping database");
353 $db_dump_folder = $this->getSnapshotDirectory() . JetBackup::SEP . BackupAccount::SKELETON_DATABASE_DIRNAME;
354
355 if (Factory::getSettingsPerformance()->isSQLCleanupRevisionsEnabled()) {
356 $this->getTask()->func(function() use ($log){
357 $log->logMessage('Revision cleanup is enabled, starting revision cleanup');
358 Wordpress::getDB()->clearPostRevisions();
359 $log->logMessage('Revision cleanup is done');
360 }, [], '_revisionCleanup');
361 }
362
363 $this->getTask()->foreach($this->_getDBTables(), function($i, $tableName, $totalTables) use ($db_dump_folder, $log, $queueItem) {
364
365 $currentCount = ($i+1);
366
367 // Dump table
368 $this->getTask()->func(function () use ($tableName, $totalTables, $db_dump_folder, $log, $queueItem, $currentCount) {
369
370 $dump = new Mysqldump(DB_NAME, DB_USER, DB_PASSWORD, DB_HOST);
371 $dump->setInclude([$tableName]);
372 $dump->setExclude($this->_getExcludedTables());
373 $dump->setLogController($log);
374 $log->logMessage("Exporting $tableName [$currentCount/$totalTables]");
375
376 $dump->start($db_dump_folder . JetBackup::SEP . $tableName . '.sql');
377
378 $progress = $queueItem->getProgress();
379 $progress->setSubMessage("Exporting $tableName");
380 $queueItem->save();
381
382 $this->getTask()->checkExecutionTime(function () use ($log, $queueItem) {
383 $log->logDebug("[_dumpDB] exitOnLimitReached triggered");
384 $queueItem->getProgress()->setMessage('[ DB Dump ] Waiting for next cron iteration');
385 $queueItem->save();
386 });
387
388 $log->logDebug("[_dumpDB] Finished export loop, Current: $tableName, Total: $totalTables");
389
390
391 }, [], '_dumpTable' . $tableName);
392
393
394
395 // Compress table
396 $this->getTask()->func(function () use ($tableName, $totalTables, $db_dump_folder, $log, $queueItem, $currentCount) {
397
398 if (!Factory::getSettingsPerformance()->isGzipCompressDB()) {
399 $log->logMessage('Skipping database compression, option is disabled.');
400 return;
401 }
402
403 $currentFile = $db_dump_folder . JetBackup::SEP . $tableName . ".sql";
404
405 $log->logMessage("Compressing $tableName [$currentCount/$totalTables]");
406
407 if(!file_exists($currentFile)) {
408 $log->logError("File $currentFile does not exist.");
409 return;
410 }
411
412 Gzip::compress(
413 $currentFile,
414 Gzip::DEFAULT_COMPRESS_CHUNK_SIZE,
415 Gzip::DEFAULT_COMPRESSION_LEVEL,
416 function($byteRead) use ($tableName) {
417
418 $progress = $this->getQueueItem()->getProgress();
419 $progress->setSubMessage("Compressing $tableName [$byteRead bytes]");
420 $this->getQueueItem()->save();
421
422 $this->getTask()->checkExecutionTime(function() {
423 $this->getQueueItem()->getProgress()->setMessage('[ Gzip ] Waiting for next cron iteration');
424 $this->getQueueItem()->save();
425 });
426 }
427 );
428
429 $log->logDebug("[_compressDB] Finished compress loop, Current: $tableName, Total: $totalTables");
430
431 }, [], '_compressTable' . $tableName);
432
433
434 $progress = $queueItem->getProgress();
435 $progress->setTotalSubItems($totalTables);
436 $progress->setCurrentSubItem($currentCount);
437 $queueItem->save();
438
439 });
440
441
442 }
443 }