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