PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backups, Restore, Migration & Clone / 4.11.0
WP STAGING – WordPress Backups, Restore, Migration & Clone v4.11.0
4.11.0 4.10.0 4.9.5 4.9.4 4.9.3 4.9.2 4.9.1 4.9.0 4.8.1 trunk 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.10.0 3.2.0 3.3.1 3.3.2 3.3.3 3.4.1 3.4.3 3.5.0 3.6.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 4.0.0 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.8.0
wp-staging / Backend / Modules / Jobs / Job.php
wp-staging / Backend / Modules / Jobs Last commit date
Cleaners 1 day ago Exceptions 1 day ago Cancel.php 1 day ago CancelUpdate.php 1 day ago Cloning.php 1 day ago CloningProcess.php 1 day ago Data.php 1 day ago Database.php 1 day ago Delete.php 1 day ago Directories.php 1 day ago Files.php 1 day ago Finish.php 1 day ago Job.php 1 day ago JobExecutable.php 1 day ago Logs.php 1 day ago PreserveDataFirstStep.php 1 day ago PreserveDataSecondStep.php 1 day ago ProcessLock.php 1 day ago Scan.php 1 day ago SearchReplace.php 1 day ago TotalStepsAreNumberOfTables.php 1 day ago Updating.php 1 day ago
Job.php
652 lines
1 <?php
2
3 namespace WPStaging\Backend\Modules\Jobs;
4
5 use DateInterval;
6 use DateTime;
7 use Exception;
8 use stdClass;
9 use WPStaging\Core\DTO\Settings;
10 use WPStaging\Core\Utils\Logger;
11 use WPStaging\Core\WPStaging;
12 use WPStaging\Framework\Database\ExcludedTables;
13 use WPStaging\Framework\Database\ExternalDatabaseConfiguration;
14 use WPStaging\Framework\Interfaces\ShutdownableInterface;
15 use WPStaging\Framework\Traits\ResourceTrait;
16 use WPStaging\Framework\Utils\Math;
17 use WPStaging\Backend\Modules\SystemInfo;
18 use WPStaging\Framework\Database\WpDbInfo;
19 use WPStaging\Framework\Security\UniqueIdentifier;
20 use WPStaging\Framework\Utils\Cache\Cache;
21 use WPStaging\Staging\Sites;
22 use WPStaging\Staging\Service\StagingEngine;
23
24
25
26
27
28 abstract class Job implements ShutdownableInterface
29 {
30 use ResourceTrait;
31
32
33
34
35 const PUSH = 'push';
36
37
38
39
40 const STAGING = 'cloning';
41
42
43
44
45 const RESET = 'resetting';
46
47
48
49
50 const UPDATE = 'updating';
51
52
53
54
55
56 const FILES_INDEX_KEY = 'clone_files_index';
57
58
59
60
61
62 const CLONE_OPTIONS_KEY = 'clone_options';
63
64
65
66
67 protected $cloneOptionCache;
68
69
70
71
72 protected $filesIndexCache;
73
74
75
76
77 protected $cache;
78
79
80
81
82 protected $logger;
83
84
85
86
87 protected $options;
88
89
90
91
92 protected $settings;
93
94
95
96
97
98 protected $baseUrl;
99
100
101 protected $excludedTableService;
102
103
104 protected $identifier;
105
106
107 protected $utilsMath;
108
109
110 protected $systemInfo;
111
112
113 protected $externalDatabaseConfiguration;
114
115
116
117
118
119 public function __construct()
120 {
121 $this->utilsMath = new Math();
122
123 $this->excludedTableService = new ExcludedTables();
124 $this->externalDatabaseConfiguration = new ExternalDatabaseConfiguration();
125
126
127
128 $this->logger = WPStaging::getInstance()->get("logger");
129 $this->systemInfo = WPStaging::make(SystemInfo::class);
130 $this->identifier = WPStaging::make(UniqueIdentifier::class);
131
132 $this->setupCacheFiles();
133
134
135 $this->options = $this->cloneOptionCache->get();
136
137 $this->options = json_decode(json_encode($this->options));
138 $this->settings = (object)((new Settings())->setDefault());
139
140 if (!$this->options) {
141 $this->options = new stdClass();
142 }
143
144 if (isset($this->options->existingClones) && is_object($this->options->existingClones)) {
145 $this->options->existingClones = json_decode(json_encode($this->options->existingClones), true);
146 }
147
148 $this->initialize();
149 }
150
151
152
153
154
155 public function initialize()
156 {
157
158 }
159
160
161
162
163
164 public function onWpShutdown()
165 {
166
167 }
168
169 protected function setupCacheFiles()
170 {
171
172 $this->cloneOptionCache = WPStaging::make(Cache::class);
173 $this->cloneOptionCache->setLifetime(-1);
174 $this->cloneOptionCache->setPath(WPStaging::getContentDir());
175 $this->cloneOptionCache->setFileName(self::CLONE_OPTIONS_KEY);
176
177
178 $this->filesIndexCache = WPStaging::make(Cache::class);
179 $this->filesIndexCache->setLifetime(-1);
180 $this->filesIndexCache->setPath(WPStaging::getContentDir());
181 $this->filesIndexCache->setFileName(self::FILES_INDEX_KEY);
182
183
184 $this->cache = WPStaging::make(Cache::class);
185 $this->cache->setLifetime(-1);
186 $this->cache->setPath(WPStaging::getContentDir());
187 }
188
189
190
191
192
193
194 public function saveOptions($options = null)
195 {
196
197 if ($options === null) {
198 $options = $this->options;
199 }
200
201 if (!is_object($options)) {
202 return false;
203 }
204
205 $now = new DateTime();
206 $options->expiresAt = $now->add(new DateInterval('P1D'))->format('Y-m-d H:i:s');
207
208 if (!property_exists($options, 'jobIdentifier')) {
209 $options->jobIdentifier = rand(0, 2147483647);
210 }
211
212
213 $options = json_decode(json_encode($options));
214 $result = $this->cloneOptionCache->save($options);
215
216 return $result !== false;
217 }
218
219
220
221
222 public function getOptions()
223 {
224 return $this->options;
225 }
226
227
228
229
230 protected function markLegacyStagingEngine()
231 {
232 $this->options->stagingEngine = StagingEngine::ENGINE_LEGACY;
233 WPStaging::make(StagingEngine::class)->saveEngine(StagingEngine::ENGINE_LEGACY);
234 }
235
236
237
238
239
240
241
242
243
244 protected function loadLegacyExistingClones()
245 {
246 $existingClones = get_option(Sites::STAGING_SITES_OPTION, []);
247 $this->options->existingClones = is_array($existingClones)
248 ? array_change_key_case($existingClones, CASE_LOWER)
249 : [];
250 }
251
252
253
254
255
256
257
258
259
260
261 protected function initializeLegacyStagingRun($mainJob)
262 {
263 $this->options->mainJob = $mainJob;
264 $this->options->currentJob = 'PreserveDataFirstStep';
265 $this->options->currentStep = 0;
266 $this->options->totalSteps = 0;
267 $this->options->job = new stdClass();
268
269 $this->options->clonedTables = [];
270
271 if (!property_exists($this->options, 'excludedTables') || !is_array($this->options->excludedTables)) {
272 $this->options->excludedTables = [];
273 }
274
275 if (!property_exists($this->options, 'totalFiles')) {
276 $this->options->totalFiles = 0;
277 }
278
279 if (!property_exists($this->options, 'totalFileSize')) {
280 $this->options->totalFileSize = 0;
281 }
282
283 if (!property_exists($this->options, 'copiedFiles')) {
284 $this->options->copiedFiles = 0;
285 }
286
287 if (!property_exists($this->options, 'includedDirectories')) {
288 $this->options->includedDirectories = [];
289 }
290
291 if (!property_exists($this->options, 'includedExtraDirectories')) {
292 $this->options->includedExtraDirectories = [];
293 }
294
295 if (!property_exists($this->options, 'excludedDirectories')) {
296 $this->options->excludedDirectories = [];
297 }
298
299 if (!property_exists($this->options, 'extraDirectories')) {
300 $this->options->extraDirectories = [];
301 }
302
303 if (!property_exists($this->options, 'scannedDirectories')) {
304 $this->options->scannedDirectories = [];
305 }
306
307 if (!property_exists($this->options, 'root')) {
308 $this->options->root = str_replace(["\\", '/'], DIRECTORY_SEPARATOR, ABSPATH);
309 }
310
311 $this->markLegacyStagingEngine();
312 }
313
314
315
316
317
318 protected function time()
319 {
320 $time = microtime();
321 $time = explode(' ', $time);
322 $time = (float)$time[1] + (float)$time[0];
323 return $time;
324 }
325
326
327
328
329 public function isOverThreshold()
330 {
331
332 $usedMemory = $this->getMemoryPeakUsage();
333 $maxMemoryLimit = $this->getMaxMemoryLimit();
334 $scriptMemoryLimit = $this->getScriptMemoryLimit();
335
336 $this->debugLog(
337 sprintf(
338 "Used Memory: %s Max Memory Limit: %s Max Script Memory Limit: %s",
339 size_format($usedMemory),
340 size_format($maxMemoryLimit),
341 size_format($scriptMemoryLimit)
342 ),
343 Logger::TYPE_DEBUG
344 );
345
346 if ($this->isMemoryLimit()) {
347 $this->log(
348 sprintf(
349 "Used Memory: %s Memory Limit: %s Max Script memory limit: %s",
350 size_format($usedMemory),
351 size_format($maxMemoryLimit),
352 size_format($scriptMemoryLimit)
353 ),
354 Logger::TYPE_ERROR
355 );
356
357 return true;
358 }
359
360
361 if ($this->isTimeLimit()) {
362 $this->debugLog(
363 sprintf(
364 "RESET TIME: current time: %s, Start Time: %d, exec time limit: %s",
365 $this->getRunningTime(),
366 WPStaging::$startTime,
367 $this->findExecutionTimeLimit()
368 )
369 );
370 return true;
371 }
372
373 return false;
374 }
375
376
377
378
379
380 public function log($msg, $type = Logger::TYPE_INFO)
381 {
382 if ($this->logger === null) {
383 return;
384 }
385
386 $this->logger->setFileName($this->getLogFilename());
387
388 $this->logger->add($msg, $type);
389 }
390
391
392
393
394 protected function getFilesIndexCacheFilePath(): string
395 {
396 return trailingslashit($this->cache->getPath()) . self::FILES_INDEX_KEY . '.' . Cache::FILE_EXTENSION;
397 }
398
399
400
401
402 private function getLogFilename()
403 {
404 $uniqueId = $this->identifier->getIdentifier();
405
406 if (!empty($this->options->mainJob) && $this->options->mainJob !== Job::STAGING) {
407 return $this->options->mainJob . '_' . $uniqueId . '_' . date('Y-m-d', time());
408 }
409
410
411 if (!empty($this->options->clone) && !empty($this->options->mainJob)) {
412 return $this->options->mainJob . '_' . $uniqueId . '_' . $this->options->clone . '_' . date('Y-m-d', time());
413 }
414
415 if (empty($this->options->clone) && !empty($this->options->mainJob)) {
416 return $this->options->mainJob . '_' . $uniqueId . '_unknown_clone_' . date('Y-m-d', time());
417 }
418
419 if (!empty($this->options->clone) && empty($this->options->mainJob)) {
420 return 'unknown_job_' . $uniqueId . '_' . $this->options->clone . '_' . date('Y-m-d', time());
421 }
422
423 return 'unknown_job_' . $uniqueId . '_' . date('Y-m-d', time());
424 }
425
426
427
428
429
430 public function debugLog($msg, $type = Logger::TYPE_INFO)
431 {
432 $this->logger->setFileName($this->getLogFilename());
433
434 if (isset($this->settings->debugMode)) {
435 $this->logger->add($msg, $type);
436 }
437 }
438
439
440
441
442
443 public function returnException($message = '')
444 {
445 wp_die(
446 json_encode(
447 [
448 'job' => isset($this->options->currentJob) ? $this->options->currentJob : '',
449 'status' => false,
450 'message' => esc_html($message),
451 'error' => true,
452 ]
453 )
454 );
455 }
456
457
458
459
460
461 protected function isRunning()
462 {
463 if (!isset($this->options) || !isset($this->options->isRunning) || !isset($this->options->expiresAt)) {
464 return false;
465 }
466
467 try {
468 $now = new DateTime();
469 $expiresAt = new DateTime($this->options->expiresAt);
470 return $this->options->isRunning === true && $now < $expiresAt;
471 } catch (Exception $e) {
472 }
473
474 return false;
475 }
476
477 protected function isPro()
478 {
479 return defined('WPSTGPRO_VERSION');
480 }
481
482
483
484
485 protected function isMultisiteAndPro()
486 {
487 return $this->isPro() && is_multisite();
488 }
489
490
491
492
493 public function isNetworkClone()
494 {
495 if (!isset($this->options->networkClone)) {
496 return false;
497 }
498
499 return $this->isMultisiteAndPro() && $this->options->networkClone;
500 }
501
502
503
504
505
506
507 public function excludeWpConfigDuringUpdate()
508 {
509 return $this->options->mainJob === self::UPDATE;
510 }
511
512
513
514
515
516 protected function isExternalDatabase()
517 {
518 return $this->externalDatabaseConfiguration->isEnabled($this->options);
519 }
520
521
522
523
524 protected function isStagingDatabaseSameAsProductionDatabase()
525 {
526 if (!$this->isExternalDatabase()) {
527 return true;
528 }
529
530 if (!$this->externalDatabaseConfiguration->hasConnectionTarget($this->options)) {
531 return false;
532 }
533
534 if ($this->options->databaseServer === DB_HOST && $this->options->databaseDatabase === DB_NAME) {
535 return true;
536 }
537
538 $productionDb = WPStaging::make('wpdb');
539 $productionDbInfo = new WpDbInfo($productionDb);
540 $productionServer = $productionDbInfo->getServer();
541
542 $stagingDb = new \wpdb($this->options->databaseUser, str_replace("\\\\", "\\", $this->options->databasePassword), $this->options->databaseDatabase, $this->options->databaseServer);
543 $stagingDbInfo = new WpDbInfo($stagingDb);
544 $stagingServer = $stagingDbInfo->getServer();
545
546 if ($productionServer === $stagingServer && $this->options->databaseDatabase === DB_NAME) {
547 return true;
548 }
549
550 return false;
551 }
552
553
554
555
556
557
558 public function isUpdateOrResetJob(): bool
559 {
560 return isset($this->options->mainJob) && ($this->options->mainJob === self::RESET || $this->options->mainJob === self::UPDATE);
561 }
562
563
564
565
566
567 protected function addJobSettingsToLogs(string $jobName = 'WP Staging Job')
568 {
569 $this->logger->add(sprintf('%s Settings', esc_html($jobName)), Logger::TYPE_INFO);
570 $this->logger->writeSelectedTablesToLogs($this->options->tables);
571 $this->logger->add('Excluded Directories', Logger::TYPE_INFO);
572 foreach ($this->options->excludedDirectories as $directory) {
573 $this->logger->add(sprintf('- %s', esc_html($directory)), Logger::TYPE_INFO_SUB);
574 }
575
576 if (!empty($this->options->excludeGlobRules)) {
577 $this->logger->add('Exclude Global Rule', Logger::TYPE_INFO);
578
579 foreach ($this->options->excludeGlobRules as $rule) {
580 $excludeRule = explode(':', $rule);
581 $ruleName = ucwords($excludeRule[0] ?? '');
582 $ruleDescription = ucwords(str_replace('_', ' ', !empty($excludeRule[1]) ? $excludeRule[1] : ''));
583 $this->logger->add(sprintf('- Exclude %s : %s', esc_html($ruleName), esc_html($ruleDescription)), Logger::TYPE_INFO_SUB);
584 }
585 }
586
587 if (!empty($this->options->excludeSizeRules)) {
588 $this->logger->add('Exclude Size Rule', Logger::TYPE_INFO);
589 foreach ($this->options->excludeSizeRules as $rule) {
590 $ruleDescription = ucwords(str_replace('_', ' ', !empty($rule) ? $rule : ''));
591 $this->logger->add(sprintf('- Exclude Size : %s', esc_html($ruleDescription)), Logger::TYPE_INFO_SUB);
592 }
593 }
594
595
596 $this->writeAdvancedSettingsToLogs();
597 $this->logger->writeGlobalSettingsToLogs();
598 }
599
600
601
602
603 private function writeAdvancedSettingsToLogs()
604 {
605 $this->logger->add('Advanced Settings', Logger::TYPE_INFO);
606
607 if (isset($this->options->useNewAdminAccount)) {
608 $this->logger->add(sprintf('- New Admin Account : %s', ($this->options->useNewAdminAccount ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
609 $this->logger->add(sprintf('- Email : %s', (!empty($this->options->adminEmail) ? $this->options->adminEmail : 'Not Set')), Logger::TYPE_INFO_SUB);
610 $this->logger->add(sprintf('- Password : %s', (!empty($this->options->adminPassword) ? '**************' : 'Not Set')), Logger::TYPE_INFO_SUB);
611 }
612
613 $this->logger->add(sprintf('- Database Server : %s', (!empty($this->options->databaseServer) ? $this->options->databaseServer : 'Not Set')), Logger::TYPE_INFO_SUB);
614 $this->logger->add(sprintf('- Database User : %s', (!empty($this->options->databaseUser) ? $this->options->databaseUser : 'Not Set')), Logger::TYPE_INFO_SUB);
615 $this->logger->add(sprintf('- Database Password : %s', (!empty($this->options->databasePassword) ? '*****************' : 'Not Set')), Logger::TYPE_INFO_SUB);
616 $this->logger->add(sprintf('- Database : %s', (!empty($this->options->databasePassword) ? $this->options->databaseDatabase : 'Not Set')), Logger::TYPE_INFO_SUB);
617 $this->logger->add(sprintf('- Database Prefix: %s', (!empty($this->options->databasePrefix) ? $this->options->databasePrefix : 'Not Set')), Logger::TYPE_INFO_SUB);
618 $this->logger->add(sprintf('- Database SSL: %s', ($this->options->databasePrefix ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
619 $this->logger->add(sprintf('- Clone Directory : %s', (!empty($this->options->cloneDir) ? $this->options->cloneDir : 'Not Set')), Logger::TYPE_INFO_SUB);
620 $this->logger->add(sprintf('- Clone Host : %s', (!empty($this->options->cloneHostname) ? $this->options->cloneHostname : 'Not Set')), Logger::TYPE_INFO_SUB);
621 $this->logger->add(sprintf('- Symlink Uploads Folder : %s', ($this->options->uploadsSymlinked ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
622
623 if (isset($this->options->isAutoUpdatePlugins)) {
624 $this->logger->add(sprintf('- Auto Update Plugins : %s', ($this->options->isAutoUpdatePlugins ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
625 }
626
627 if (isset($this->options->isCronEnabled)) {
628 $this->logger->add(sprintf('- Enable WP_CRON : %s', ($this->options->isCronEnabled ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
629 }
630
631 if (isset($this->options->isWooSchedulerEnabled)) {
632 $this->logger->add(sprintf('- Enable WooCommerce Scheduler : %s', ($this->options->isWooSchedulerEnabled ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
633 }
634
635 if (isset($this->options->isEmailsAllowed)) {
636 $this->logger->add(sprintf('- Allow Emails Sending : %s', ($this->options->isEmailsAllowed ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
637 }
638
639 if (isset($this->options->deletePluginsAndThemes)) {
640 $this->logger->add(sprintf('- Clean Plugins/Themes : %s', ($this->options->deletePluginsAndThemes ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
641 }
642
643 if (isset($this->options->deleteUploadsFolder)) {
644 $this->logger->add(sprintf('- Clean Uploads : %s', ($this->options->deleteUploadsFolder ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
645 }
646
647 if (isset($this->options->createBackupBeforePushing)) {
648 $this->logger->add(sprintf('- Create database backup : %s', ($this->options->createBackupBeforePushing ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
649 }
650 }
651 }
652