PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 3.8.2
WP STAGING – WordPress Backup, Restore, Migration & Clone v3.8.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 5 years ago Exceptions 5 years ago Cancel.php 2 years ago CancelUpdate.php 2 years ago Cloning.php 2 years ago CloningProcess.php 2 years ago Data.php 2 years ago Database.php 2 years ago Delete.php 2 years ago Directories.php 2 years ago Files.php 2 years ago Finish.php 1 year ago Job.php 2 years ago JobExecutable.php 2 years ago Logs.php 3 years ago PreserveDataFirstStep.php 3 years ago PreserveDataSecondStep.php 2 years ago ProcessLock.php 2 years ago Scan.php 2 years ago SearchReplace.php 2 years ago TotalStepsAreNumberOfTables.php 5 years ago Updating.php 2 years ago
Job.php
461 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
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 $now = new DateTime();
195 $options->expiresAt = $now->add(new DateInterval('P1D'))->format('Y-m-d H:i:s');
196
197 if (!property_exists($options, 'jobIdentifier')) {
198 $options->jobIdentifier = rand(0, 2147483647); // 32 bits int max
199 }
200
201 // Ensure that it is an object
202 $options = json_decode(json_encode($options));
203 $result = $this->cloneOptionCache->save($options);
204
205 return $result !== false;
206 }
207
208 /**
209 * @return object
210 */
211 public function getOptions()
212 {
213 return $this->options;
214 }
215
216 /**
217 * Get current time in seconds
218 * @return float
219 */
220 protected function time()
221 {
222 $time = microtime();
223 $time = explode(' ', $time);
224 $time = (float)$time[1] + (float)$time[0];
225 return $time;
226 }
227
228 /**
229 * @return bool
230 */
231 public function isOverThreshold()
232 {
233 // Check if the memory is over threshold
234 $usedMemory = $this->getMemoryPeakUsage();
235 $maxMemoryLimit = $this->getMaxMemoryLimit();
236 $scriptMemoryLimit = $this->getScriptMemoryLimit();
237
238 $this->debugLog(
239 sprintf(
240 "Used Memory: %s Max Memory Limit: %s Max Script Memory Limit: %s",
241 size_format($usedMemory),
242 size_format($maxMemoryLimit),
243 size_format($scriptMemoryLimit)
244 ),
245 Logger::TYPE_DEBUG
246 );
247
248 if ($this->isMemoryLimit()) {
249 $this->log(
250 sprintf(
251 "Used Memory: %s Memory Limit: %s Max Script memory limit: %s",
252 size_format($usedMemory),
253 size_format($maxMemoryLimit),
254 size_format($scriptMemoryLimit)
255 ),
256 Logger::TYPE_ERROR
257 );
258
259 return true;
260 }
261
262 // Check if execution time is over threshold
263 if ($this->isTimeLimit()) {
264 $this->debugLog(
265 sprintf(
266 "RESET TIME: current time: %s, Start Time: %d, exec time limit: %s",
267 $this->getRunningTime(),
268 WPStaging::$startTime,
269 $this->findExecutionTimeLimit()
270 )
271 );
272 return true;
273 }
274
275 return false;
276 }
277
278 /**
279 * @param string $msg
280 * @param string $type
281 */
282 public function log($msg, $type = Logger::TYPE_INFO)
283 {
284 if ($this->logger === null) {
285 return;
286 }
287
288 $this->logger->setFileName($this->getLogFilename());
289
290 $this->logger->add($msg, $type);
291 }
292
293 /**
294 * @return string
295 */
296 protected function getFilesIndexCacheFilePath(): string
297 {
298 return trailingslashit($this->cache->getPath()) . self::FILES_INDEX_KEY . '.' . Cache::FILE_EXTENSION;
299 }
300
301 /**
302 * @return string
303 */
304 private function getLogFilename()
305 {
306 $uniqueId = $this->identifier->getIdentifier();
307 // If job is not cloning i.e. updating, resetting, pushing
308 if (!empty($this->options->mainJob) && $this->options->mainJob !== Job::STAGING) {
309 return $this->options->mainJob . '_' . $uniqueId . '_' . date('Y-m-d', time());
310 }
311
312 // If job is cloning
313 if (!empty($this->options->clone) && !empty($this->options->mainJob)) {
314 return $this->options->mainJob . '_' . $uniqueId . '_' . $this->options->clone . '_' . date('Y-m-d', time());
315 }
316
317 if (empty($this->options->clone) && !empty($this->options->mainJob)) {
318 return $this->options->mainJob . '_' . $uniqueId . '_unknown_clone_' . date('Y-m-d', time());
319 }
320
321 if (!empty($this->options->clone) && empty($this->options->mainJob)) {
322 return 'unknown_job_' . $uniqueId . '_' . $this->options->clone . '_' . date('Y-m-d', time());
323 }
324
325 return 'unknown_job_' . $uniqueId . '_' . date('Y-m-d', time());
326 }
327
328 /**
329 * @param string $msg
330 * @param string $type
331 */
332 public function debugLog($msg, $type = Logger::TYPE_INFO)
333 {
334 $this->logger->setFileName($this->getLogFilename());
335
336 if (isset($this->settings->debugMode)) {
337 $this->logger->add($msg, $type);
338 }
339 }
340
341 /**
342 * Throw an error message via json and stop further execution
343 * @param string $message
344 */
345 public function returnException($message = '')
346 {
347 wp_die(
348 json_encode(
349 [
350 'job' => isset($this->options->currentJob) ? $this->options->currentJob : '',
351 'status' => false,
352 'message' => esc_html($message),
353 'error' => true
354 ]
355 )
356 );
357 }
358
359 /**
360 * Is job running
361 * @return bool
362 */
363 protected function isRunning()
364 {
365 if (!isset($this->options) || !isset($this->options->isRunning) || !isset($this->options->expiresAt)) {
366 return false;
367 }
368
369 try {
370 $now = new DateTime();
371 $expiresAt = new DateTime($this->options->expiresAt);
372 return $this->options->isRunning === true && $now < $expiresAt;
373 } catch (Exception $e) {
374 }
375
376 return false;
377 }
378
379 protected function isPro()
380 {
381 return defined('WPSTGPRO_VERSION');
382 }
383
384 /**
385 * @return bool
386 */
387 protected function isMultisiteAndPro()
388 {
389 return $this->isPro() && is_multisite();
390 }
391
392 /**
393 * @return bool
394 */
395 public function isNetworkClone()
396 {
397 if (!isset($this->options->networkClone)) {
398 return false;
399 }
400
401 return $this->isMultisiteAndPro() && $this->options->networkClone;
402 }
403
404 /**
405 * Should exclude wp-config file during clone update
406 *
407 * @return bool
408 */
409 public function excludeWpConfigDuringUpdate()
410 {
411 return $this->options->mainJob === self::UPDATE;
412 }
413
414 /**
415 * Check if external database is used
416 * @return bool
417 */
418 protected function isExternalDatabase()
419 {
420 return !(empty($this->options->databaseUser) && empty($this->options->databasePassword));
421 }
422
423 /**
424 * @return bool
425 */
426 protected function isStagingDatabaseSameAsProductionDatabase()
427 {
428 if (empty($this->options->databaseUser) || empty($this->options->databaseServer) || empty($this->options->databaseDatabase)) {
429 return true;
430 }
431
432 if ($this->options->databaseServer === DB_HOST && $this->options->databaseDatabase === DB_NAME) {
433 return true;
434 }
435
436 $productionDb = WPStaging::make('wpdb');
437 $productionDbInfo = new WpDbInfo($productionDb);
438 $productionServer = $productionDbInfo->getServer();
439
440 $stagingDb = new \wpdb($this->options->databaseUser, str_replace("\\\\", "\\", $this->options->databasePassword), $this->options->databaseDatabase, $this->options->databaseServer);
441 $stagingDbInfo = new WpDbInfo($stagingDb);
442 $stagingServer = $stagingDbInfo->getServer();
443
444 if ($productionServer === $stagingServer && $this->options->databaseDatabase === DB_NAME) {
445 return true;
446 }
447
448 return false;
449 }
450
451 /**
452 * Is the current main job UPDATE or RESET
453 *
454 * @return bool
455 */
456 public function isUpdateOrResetJob(): bool
457 {
458 return isset($this->options->mainJob) && ($this->options->mainJob === self::RESET || $this->options->mainJob === self::UPDATE);
459 }
460 }
461