PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 4.7.0
WP STAGING – WordPress Backup, Restore, Migration & Clone v4.7.0
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 4 months ago Exceptions 5 years ago Cancel.php 7 months ago CancelUpdate.php 7 months ago Cloning.php 4 months ago CloningProcess.php 1 year ago Data.php 7 months ago Database.php 4 months ago Delete.php 5 months ago Directories.php 5 months ago Files.php 6 months ago Finish.php 5 months ago Job.php 5 months ago JobExecutable.php 7 months ago Logs.php 3 years ago PreserveDataFirstStep.php 5 months ago PreserveDataSecondStep.php 5 months ago ProcessLock.php 1 year ago Scan.php 5 months ago SearchReplace.php 6 months ago TotalStepsAreNumberOfTables.php 5 years ago Updating.php 5 months ago
Job.php
554 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\Interfaces\ShutdownableInterface;
14 use WPStaging\Framework\Traits\ResourceTrait;
15 use WPStaging\Framework\Utils\Math;
16 use WPStaging\Backend\Modules\SystemInfo;
17 use WPStaging\Framework\Database\WpDbInfo;
18 use WPStaging\Framework\Security\UniqueIdentifier;
19 use WPStaging\Framework\Utils\Cache\Cache;
20
21 /**
22 * Class Job
23 * @package WPStaging\Backend\Modules\Jobs
24 */
25 abstract class Job implements ShutdownableInterface
26 {
27 use ResourceTrait;
28
29 /**
30 * @var string
31 */
32 const PUSH = 'push';
33
34 /**
35 * @var string
36 */
37 const STAGING = 'cloning';
38
39 /**
40 * @var string
41 */
42 const RESET = 'resetting';
43
44 /**
45 * @var string
46 */
47 const UPDATE = 'updating';
48
49 /**
50 * Temp file base name for files index for cloning and push
51 * @var string
52 */
53 const FILES_INDEX_KEY = 'clone_files_index';
54
55 /**
56 * Temp file base name that contain clone related data for cloning and push
57 * @var string
58 */
59 const CLONE_OPTIONS_KEY = 'clone_options';
60
61 /**
62 * @var Cache
63 */
64 protected $cloneOptionCache;
65
66 /**
67 * @var Cache
68 */
69 protected $filesIndexCache;
70
71 /**
72 * @var Cache
73 */
74 protected $cache;
75
76 /**
77 * @var Logger
78 */
79 protected $logger;
80
81 /**
82 * @var object|null
83 */
84 protected $options;
85
86 /**
87 * @var object
88 */
89 protected $settings;
90
91 /**
92 * Multisite home domain without scheme
93 * @var string
94 */
95 protected $baseUrl;
96
97 /** @var ExcludedTables */
98 protected $excludedTableService;
99
100 /** @var UniqueIdentifier */
101 protected $identifier;
102
103 /** @var Math */
104 protected $utilsMath;
105
106 /** @var SystemInfo */
107 protected $systemInfo;
108
109 /**
110 * Job constructor.
111 * @throws Exception
112 */
113 public function __construct()
114 {
115 $this->utilsMath = new Math();
116
117 $this->excludedTableService = new ExcludedTables();
118
119 // Services
120 //$this->logger = WPStaging::make(Logger::class);
121 $this->logger = WPStaging::getInstance()->get("logger");
122 $this->systemInfo = WPStaging::make(SystemInfo::class);
123 $this->identifier = WPStaging::make(UniqueIdentifier::class);
124
125 $this->setupCacheFiles();
126
127 // Settings and Options
128 $this->options = $this->cloneOptionCache->get();
129 // Convert into object
130 $this->options = json_decode(json_encode($this->options));
131 $this->settings = (object)((new Settings())->setDefault());
132
133 if (!$this->options) {
134 $this->options = new stdClass();
135 }
136
137 if (isset($this->options->existingClones) && is_object($this->options->existingClones)) {
138 $this->options->existingClones = json_decode(json_encode($this->options->existingClones), true);
139 }
140
141 $this->initialize();
142 }
143
144 /**
145 * To be override by child classes
146 * @return void
147 */
148 public function initialize()
149 {
150 // do nothing
151 }
152
153 /**
154 * @todo can be removed?
155 * @return void
156 */
157 public function onWpShutdown()
158 {
159 // do nothing
160 }
161
162 protected function setupCacheFiles()
163 {
164 // For clone options
165 $this->cloneOptionCache = WPStaging::make(Cache::class);
166 $this->cloneOptionCache->setLifetime(-1); // Non-expireable file
167 $this->cloneOptionCache->setPath(WPStaging::getContentDir());
168 $this->cloneOptionCache->setFileName(self::CLONE_OPTIONS_KEY);
169
170 // For files index to copy files
171 $this->filesIndexCache = WPStaging::make(Cache::class);
172 $this->filesIndexCache->setLifetime(-1); // Non-expireable file
173 $this->filesIndexCache->setPath(WPStaging::getContentDir());
174 $this->filesIndexCache->setFileName(self::FILES_INDEX_KEY);
175
176 // For other purposes
177 $this->cache = WPStaging::make(Cache::class);
178 $this->cache->setLifetime(-1); // Non-expireable file
179 $this->cache->setPath(WPStaging::getContentDir());
180 }
181
182 /**
183 * @param null|array|object $options
184 * @return bool
185 * @throws Exception
186 */
187 public function saveOptions($options = null)
188 {
189 // Get default options
190 if ($options === null) {
191 $options = $this->options;
192 }
193
194 if (!is_object($options)) {
195 return false;
196 }
197
198 $now = new DateTime();
199 $options->expiresAt = $now->add(new DateInterval('P1D'))->format('Y-m-d H:i:s');
200
201 if (!property_exists($options, 'jobIdentifier')) {
202 $options->jobIdentifier = rand(0, 2147483647); // 32 bits int max
203 }
204
205 // Ensure that it is an object
206 $options = json_decode(json_encode($options));
207 $result = $this->cloneOptionCache->save($options);
208
209 return $result !== false;
210 }
211
212 /**
213 * @return object
214 */
215 public function getOptions()
216 {
217 return $this->options;
218 }
219
220 /**
221 * Get current time in seconds
222 * @return float
223 */
224 protected function time()
225 {
226 $time = microtime();
227 $time = explode(' ', $time);
228 $time = (float)$time[1] + (float)$time[0];
229 return $time;
230 }
231
232 /**
233 * @return bool
234 */
235 public function isOverThreshold()
236 {
237 // Check if the memory is over threshold
238 $usedMemory = $this->getMemoryPeakUsage();
239 $maxMemoryLimit = $this->getMaxMemoryLimit();
240 $scriptMemoryLimit = $this->getScriptMemoryLimit();
241
242 $this->debugLog(
243 sprintf(
244 "Used Memory: %s Max Memory Limit: %s Max Script Memory Limit: %s",
245 size_format($usedMemory),
246 size_format($maxMemoryLimit),
247 size_format($scriptMemoryLimit)
248 ),
249 Logger::TYPE_DEBUG
250 );
251
252 if ($this->isMemoryLimit()) {
253 $this->log(
254 sprintf(
255 "Used Memory: %s Memory Limit: %s Max Script memory limit: %s",
256 size_format($usedMemory),
257 size_format($maxMemoryLimit),
258 size_format($scriptMemoryLimit)
259 ),
260 Logger::TYPE_ERROR
261 );
262
263 return true;
264 }
265
266 // Check if execution time is over threshold
267 if ($this->isTimeLimit()) {
268 $this->debugLog(
269 sprintf(
270 "RESET TIME: current time: %s, Start Time: %d, exec time limit: %s",
271 $this->getRunningTime(),
272 WPStaging::$startTime,
273 $this->findExecutionTimeLimit()
274 )
275 );
276 return true;
277 }
278
279 return false;
280 }
281
282 /**
283 * @param string $msg
284 * @param string $type
285 */
286 public function log($msg, $type = Logger::TYPE_INFO)
287 {
288 if ($this->logger === null) {
289 return;
290 }
291
292 $this->logger->setFileName($this->getLogFilename());
293
294 $this->logger->add($msg, $type);
295 }
296
297 /**
298 * @return string
299 */
300 protected function getFilesIndexCacheFilePath(): string
301 {
302 return trailingslashit($this->cache->getPath()) . self::FILES_INDEX_KEY . '.' . Cache::FILE_EXTENSION;
303 }
304
305 /**
306 * @return string
307 */
308 private function getLogFilename()
309 {
310 $uniqueId = $this->identifier->getIdentifier();
311 // If job is not cloning i.e. updating, resetting, pushing
312 if (!empty($this->options->mainJob) && $this->options->mainJob !== Job::STAGING) {
313 return $this->options->mainJob . '_' . $uniqueId . '_' . date('Y-m-d', time());
314 }
315
316 // If job is cloning
317 if (!empty($this->options->clone) && !empty($this->options->mainJob)) {
318 return $this->options->mainJob . '_' . $uniqueId . '_' . $this->options->clone . '_' . date('Y-m-d', time());
319 }
320
321 if (empty($this->options->clone) && !empty($this->options->mainJob)) {
322 return $this->options->mainJob . '_' . $uniqueId . '_unknown_clone_' . date('Y-m-d', time());
323 }
324
325 if (!empty($this->options->clone) && empty($this->options->mainJob)) {
326 return 'unknown_job_' . $uniqueId . '_' . $this->options->clone . '_' . date('Y-m-d', time());
327 }
328
329 return 'unknown_job_' . $uniqueId . '_' . date('Y-m-d', time());
330 }
331
332 /**
333 * @param string $msg
334 * @param string $type
335 */
336 public function debugLog($msg, $type = Logger::TYPE_INFO)
337 {
338 $this->logger->setFileName($this->getLogFilename());
339
340 if (isset($this->settings->debugMode)) {
341 $this->logger->add($msg, $type);
342 }
343 }
344
345 /**
346 * Throw an error message via json and stop further execution
347 * @param string $message
348 */
349 public function returnException($message = '')
350 {
351 wp_die(
352 json_encode(
353 [
354 'job' => isset($this->options->currentJob) ? $this->options->currentJob : '',
355 'status' => false,
356 'message' => esc_html($message),
357 'error' => true,
358 ]
359 )
360 );
361 }
362
363 /**
364 * Is job running
365 * @return bool
366 */
367 protected function isRunning()
368 {
369 if (!isset($this->options) || !isset($this->options->isRunning) || !isset($this->options->expiresAt)) {
370 return false;
371 }
372
373 try {
374 $now = new DateTime();
375 $expiresAt = new DateTime($this->options->expiresAt);
376 return $this->options->isRunning === true && $now < $expiresAt;
377 } catch (Exception $e) {
378 }
379
380 return false;
381 }
382
383 protected function isPro()
384 {
385 return defined('WPSTGPRO_VERSION');
386 }
387
388 /**
389 * @return bool
390 */
391 protected function isMultisiteAndPro()
392 {
393 return $this->isPro() && is_multisite();
394 }
395
396 /**
397 * @return bool
398 */
399 public function isNetworkClone()
400 {
401 if (!isset($this->options->networkClone)) {
402 return false;
403 }
404
405 return $this->isMultisiteAndPro() && $this->options->networkClone;
406 }
407
408 /**
409 * Should exclude wp-config file during clone update
410 *
411 * @return bool
412 */
413 public function excludeWpConfigDuringUpdate()
414 {
415 return $this->options->mainJob === self::UPDATE;
416 }
417
418 /**
419 * Check if external database is used
420 * @return bool
421 */
422 protected function isExternalDatabase()
423 {
424 return !(empty($this->options->databaseUser) && empty($this->options->databasePassword));
425 }
426
427 /**
428 * @return bool
429 */
430 protected function isStagingDatabaseSameAsProductionDatabase()
431 {
432 if (empty($this->options->databaseUser) || empty($this->options->databaseServer) || empty($this->options->databaseDatabase)) {
433 return true;
434 }
435
436 if ($this->options->databaseServer === DB_HOST && $this->options->databaseDatabase === DB_NAME) {
437 return true;
438 }
439
440 $productionDb = WPStaging::make('wpdb');
441 $productionDbInfo = new WpDbInfo($productionDb);
442 $productionServer = $productionDbInfo->getServer();
443
444 $stagingDb = new \wpdb($this->options->databaseUser, str_replace("\\\\", "\\", $this->options->databasePassword), $this->options->databaseDatabase, $this->options->databaseServer);
445 $stagingDbInfo = new WpDbInfo($stagingDb);
446 $stagingServer = $stagingDbInfo->getServer();
447
448 if ($productionServer === $stagingServer && $this->options->databaseDatabase === DB_NAME) {
449 return true;
450 }
451
452 return false;
453 }
454
455 /**
456 * Is the current main job UPDATE or RESET
457 *
458 * @return bool
459 */
460 public function isUpdateOrResetJob(): bool
461 {
462 return isset($this->options->mainJob) && ($this->options->mainJob === self::RESET || $this->options->mainJob === self::UPDATE);
463 }
464
465 /**
466 * @param string $jobName
467 * @return void
468 */
469 protected function addJobSettingsToLogs(string $jobName = 'WP Staging Job')
470 {
471 $this->logger->add(sprintf('%s Settings', esc_html($jobName)), Logger::TYPE_INFO);
472 $this->logger->writeSelectedTablesToLogs($this->options->tables);
473 $this->logger->add('Excluded Directories', Logger::TYPE_INFO);
474 foreach ($this->options->excludedDirectories as $directory) {
475 $this->logger->add(sprintf('- %s', esc_html($directory)), Logger::TYPE_INFO_SUB);
476 }
477
478 if (!empty($this->options->excludeGlobRules)) {
479 $this->logger->add('Exclude Global Rule', Logger::TYPE_INFO);
480
481 foreach ($this->options->excludeGlobRules as $rule) {
482 $excludeRule = explode(':', $rule);
483 $ruleName = ucwords($excludeRule[0] ?? '');
484 $ruleDescription = ucwords(str_replace('_', ' ', !empty($excludeRule[1]) ? $excludeRule[1] : ''));
485 $this->logger->add(sprintf('- Exclude %s : %s', esc_html($ruleName), esc_html($ruleDescription)), Logger::TYPE_INFO_SUB);
486 }
487 }
488
489 if (!empty($this->options->excludeSizeRules)) {
490 $this->logger->add('Exclude Size Rule', Logger::TYPE_INFO);
491 foreach ($this->options->excludeSizeRules as $rule) {
492 $ruleDescription = ucwords(str_replace('_', ' ', !empty($rule) ? $rule : ''));
493 $this->logger->add(sprintf('- Exclude Size : %s', esc_html($ruleDescription)), Logger::TYPE_INFO_SUB);
494 }
495 }
496
497
498 $this->writeAdvancedSettingsToLogs();
499 $this->logger->writeGlobalSettingsToLogs();
500 }
501
502 /**
503 * @return void
504 */
505 private function writeAdvancedSettingsToLogs()
506 {
507 $this->logger->add('Advanced Settings', Logger::TYPE_INFO);
508
509 if (isset($this->options->useNewAdminAccount)) {
510 $this->logger->add(sprintf('- New Admin Account : %s', ($this->options->useNewAdminAccount ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
511 $this->logger->add(sprintf('- Email : %s', (!empty($this->options->adminEmail) ? $this->options->adminEmail : 'Not Set')), Logger::TYPE_INFO_SUB);
512 $this->logger->add(sprintf('- Password : %s', (!empty($this->options->adminPassword) ? '**************' : 'Not Set')), Logger::TYPE_INFO_SUB);
513 }
514
515 $this->logger->add(sprintf('- Database Server : %s', (!empty($this->options->databaseServer) ? $this->options->databaseServer : 'Not Set')), Logger::TYPE_INFO_SUB);
516 $this->logger->add(sprintf('- Database User : %s', (!empty($this->options->databaseUser) ? $this->options->databaseUser : 'Not Set')), Logger::TYPE_INFO_SUB);
517 $this->logger->add(sprintf('- Database Password : %s', (!empty($this->options->databasePassword) ? '*****************' : 'Not Set')), Logger::TYPE_INFO_SUB);
518 $this->logger->add(sprintf('- Database : %s', (!empty($this->options->databasePassword) ? $this->options->databaseDatabase : 'Not Set')), Logger::TYPE_INFO_SUB);
519 $this->logger->add(sprintf('- Database Prefix: %s', (!empty($this->options->databasePrefix) ? $this->options->databasePrefix : 'Not Set')), Logger::TYPE_INFO_SUB);
520 $this->logger->add(sprintf('- Database SSL: %s', ($this->options->databasePrefix ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
521 $this->logger->add(sprintf('- Clone Directory : %s', (!empty($this->options->cloneDir) ? $this->options->cloneDir : 'Not Set')), Logger::TYPE_INFO_SUB);
522 $this->logger->add(sprintf('- Clone Host : %s', (!empty($this->options->cloneHostname) ? $this->options->cloneHostname : 'Not Set')), Logger::TYPE_INFO_SUB);
523 $this->logger->add(sprintf('- Symlink Uploads Folder : %s', ($this->options->uploadsSymlinked ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
524
525 if (isset($this->options->isAutoUpdatePlugins)) {
526 $this->logger->add(sprintf('- Auto Update Plugins : %s', ($this->options->isAutoUpdatePlugins ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
527 }
528
529 if (isset($this->options->isCronEnabled)) {
530 $this->logger->add(sprintf('- Enable WP_CRON : %s', ($this->options->isCronEnabled ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
531 }
532
533 if (isset($this->options->isWooSchedulerEnabled)) {
534 $this->logger->add(sprintf('- Enable WooCommerce Scheduler : %s', ($this->options->isWooSchedulerEnabled ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
535 }
536
537 if (isset($this->options->isEmailsAllowed)) {
538 $this->logger->add(sprintf('- Allow Emails Sending : %s', ($this->options->isEmailsAllowed ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
539 }
540
541 if (isset($this->options->deletePluginsAndThemes)) {
542 $this->logger->add(sprintf('- Clean Plugins/Themes : %s', ($this->options->deletePluginsAndThemes ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
543 }
544
545 if (isset($this->options->deleteUploadsFolder)) {
546 $this->logger->add(sprintf('- Clean Uploads : %s', ($this->options->deleteUploadsFolder ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
547 }
548
549 if (isset($this->options->createBackupBeforePushing)) {
550 $this->logger->add(sprintf('- Create database backup : %s', ($this->options->createBackupBeforePushing ? 'True' : 'False')), Logger::TYPE_INFO_SUB);
551 }
552 }
553 }
554