WP Staging – DB & File Duplicator & Migration - Version 2.3.0

Version Description

  • Security: Do not allow to create a new staging site into a subfolder which already exists
  • New: Option to allow adjustment of the allowed maximum size of files that are going to be copied while cloning.
  • New: Add multisite informations in system info log
  • New: Option to allow adjustment of the allowed maximum size of files that are going to be copied while cloning.
  • New: Use the new progress bar for clone updating process
  • Fix: Progress bar for step 'database' is not filling up to 100%
  • Fix: If cloning update process is interupted it may happen that staging site is not available any longer. (Updating the clone does not copy index.php to staging site again)
  • Fix: Progress bar not shown as intented for clone updating process
  • Fix: Can not open upload folder in file selection menu
  • Fix: Undefined object $this->tables
  • Fix: wp-config.php not copied when previous clone updating process has been failed
  • Fix: Parameter must be an array or an object that implements Callable
  • Fix: Skip search & replace for objects where key is null
  • Fix: Search & Replace not working if serialized object contains _PHP_IncompleteClass_Name
  • Tweaks: remove term "error" from several log entries
  • Tweak: Remove certain debugging notices from the default log window
Download this release

Release Info

Developer ReneHermi
Plugin Icon 128x128 WP Staging – DB & File Duplicator & Migration
Version 2.3.0
Comparing to
See all releases

Code changes from version 2.2.9 to 2.3.0

apps/Backend/Modules/Jobs/Cloning.php CHANGED
@@ -121,7 +121,7 @@ class Cloning extends Job
121
/**
122
* Create a new staging prefix which does not already exists in database
123
*/
124
- public function setStagingPrefix(){
125
126
// Get & find a new prefix that does not already exist in database.
127
// Loop through up to 1000 different possible prefixes should be enough here;)
121
/**
122
* Create a new staging prefix which does not already exists in database
123
*/
124
+ private function setStagingPrefix() {
125
126
// Get & find a new prefix that does not already exist in database.
127
// Loop through up to 1000 different possible prefixes should be enough here;)
apps/Backend/Modules/Jobs/Data.php CHANGED
@@ -10,6 +10,7 @@ if( !defined( "WPINC" ) ) {
10
use WPStaging\Utils\Logger;
11
use WPStaging\WPStaging;
12
use WPStaging\Utils\Helper;
13
14
/**
15
* Class Data
@@ -33,6 +34,12 @@ class Data extends JobExecutable {
33
*/
34
private $homeUrl;
35
36
/**
37
* Initialize
38
*/
@@ -41,6 +48,8 @@ class Data extends JobExecutable {
41
42
$this->prefix = $this->options->prefix;
43
44
$helper = new Helper();
45
46
$this->homeUrl = $helper->get_home_url();
@@ -52,12 +61,23 @@ class Data extends JobExecutable {
52
}
53
}
54
55
/**
56
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
57
* @return void
58
*/
59
protected function calculateTotalSteps() {
60
- $this->options->totalSteps = 11;
61
}
62
63
/**
@@ -186,9 +206,15 @@ class Data extends JobExecutable {
186
protected function step1() {
187
$this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
188
189
if( false === $this->isTable( $this->prefix . 'options' ) ) {
190
return true;
191
}
192
193
// Installed in sub-directory
194
if( $this->isSubDir() ) {
@@ -227,9 +253,15 @@ class Data extends JobExecutable {
227
228
$this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
229
230
if( false === $this->isTable( $this->prefix . 'options' ) ) {
231
return true;
232
}
233
234
$result = $this->db->query(
235
$this->db->prepare(
@@ -263,10 +295,17 @@ class Data extends JobExecutable {
263
264
$this->log( "Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}" );
265
266
if( false === $this->isTable( $this->prefix . 'options' ) ) {
267
return true;
268
}
269
270
$result = $this->db->query(
271
$this->db->prepare(
272
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", ' '
@@ -287,12 +326,19 @@ class Data extends JobExecutable {
287
* @return bool
288
*/
289
protected function step4() {
290
- $this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. Error: {$this->db->last_error}" );
291
292
if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
293
return true;
294
}
295
296
$update = $this->db->query(
297
$this->db->prepare(
298
"UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, %s, %s) WHERE meta_key LIKE %s", $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
@@ -305,37 +351,36 @@ class Data extends JobExecutable {
305
return false;
306
}
307
308
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
309
- return true;
310
- }
311
-
312
- $this->log( "Updating db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
313
-
314
- // Filter the rows below. Do not update them!
315
- $filters = array(
316
- 'wp_mail_smtp',
317
- 'wp_mail_smtp_version',
318
- 'wp_mail_smtp_debug',
319
- );
320
-
321
- $filters = apply_filters('wpstg_filter_options_replace', $filters);
322
-
323
- $where = "";
324
- foreach($filters as $filter){
325
- $where .= " AND option_name <> '" . $filter . "'";
326
- }
327
-
328
- $resultUserMeta = $this->db->query(
329
- $this->db->prepare(
330
- "UPDATE IGNORE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s" . $where, $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
331
- )
332
- );
333
-
334
- if( !$resultUserMeta ) {
335
- $this->log( "Preparing Data Step4: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_ERROR );
336
- $this->returnException( "Data Crunching Step 4: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
337
- return false;
338
- }
339
340
return true;
341
}
@@ -425,10 +470,17 @@ class Data extends JobExecutable {
425
426
$this->log( "Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}" );
427
428
if( false === $this->isTable( $this->prefix . 'options' ) ) {
429
return true;
430
}
431
432
$result = $this->db->query(
433
$this->db->prepare(
434
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'", ' '
@@ -453,10 +505,17 @@ class Data extends JobExecutable {
453
454
$this->log( "Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}" );
455
456
if( false === $this->isTable( $this->prefix . 'options' ) ) {
457
return true;
458
}
459
460
$result = $this->db->query(
461
$this->db->prepare(
462
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'", ' '
@@ -485,6 +544,12 @@ class Data extends JobExecutable {
485
return true;
486
}
487
488
$result = $this->db->query(
489
$this->db->prepare(
490
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'blog_public'", '0'
@@ -586,6 +651,56 @@ class Data extends JobExecutable {
586
return true;
587
}
588
589
/**
590
* Return URL to staging site
591
* @return string
10
use WPStaging\Utils\Logger;
11
use WPStaging\WPStaging;
12
use WPStaging\Utils\Helper;
13
+ use WPStaging\Utils\Strings;
14
15
/**
16
* Class Data
34
*/
35
private $homeUrl;
36
37
+ /**
38
+ * Tables e.g wpstg3_options
39
+ * @var array
40
+ */
41
+ private $tables;
42
+
43
/**
44
* Initialize
45
*/
48
49
$this->prefix = $this->options->prefix;
50
51
+ $this->getTables();
52
+
53
$helper = new Helper();
54
55
$this->homeUrl = $helper->get_home_url();
61
}
62
}
63
64
+ /**
65
+ * Get a list of tables to copy
66
+ */
67
+ private function getTables() {
68
+ $strings = new Strings();
69
+ $this->tables = array();
70
+ foreach ( $this->options->tables as $table ) {
71
+ $this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->db->prefix, null, $table );
72
+ }
73
+ }
74
+
75
/**
76
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
77
* @return void
78
*/
79
protected function calculateTotalSteps() {
80
+ $this->options->totalSteps = 12;
81
}
82
83
/**
206
protected function step1() {
207
$this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
208
209
+ // Skip - Table does not exist
210
if( false === $this->isTable( $this->prefix . 'options' ) ) {
211
return true;
212
}
213
+ // Skip - Table is not selected or updated
214
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
215
+ $this->log( "Preparing Data Step1: Skipping" );
216
+ return true;
217
+ }
218
219
// Installed in sub-directory
220
if( $this->isSubDir() ) {
253
254
$this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
255
256
+ // Skip - Table does not exist
257
if( false === $this->isTable( $this->prefix . 'options' ) ) {
258
return true;
259
}
260
+ // Skip - Table is not selected or updated
261
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
262
+ $this->log( "Preparing Data Step2: Skipping" );
263
+ return true;
264
+ }
265
266
$result = $this->db->query(
267
$this->db->prepare(
295
296
$this->log( "Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}" );
297
298
+ // Skip - Table does not exist
299
if( false === $this->isTable( $this->prefix . 'options' ) ) {
300
return true;
301
}
302
303
+ // Skip - Table is not selected or updated
304
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
305
+ $this->log( "Preparing Data Step3: Skipping" );
306
+ return true;
307
+ }
308
+
309
$result = $this->db->query(
310
$this->db->prepare(
311
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", ' '
326
* @return bool
327
*/
328
protected function step4() {
329
+ $this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. " );
330
331
+ // Skip - Table does not exist
332
if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
333
return true;
334
}
335
336
+ // Skip - Table is not selected or updated
337
+ if( !in_array( $this->prefix . 'usermeta', $this->tables ) ) {
338
+ $this->log( "Preparing Data Step4: Skipping" );
339
+ return true;
340
+ }
341
+
342
$update = $this->db->query(
343
$this->db->prepare(
344
"UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, %s, %s) WHERE meta_key LIKE %s", $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
351
return false;
352
}
353
354
+ // if( false === $this->isTable( $this->prefix . 'options' ) ) {
355
+ // return true;
356
+ // }
357
+ // $this->log( "Updating db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
358
+ //
359
+ // // Filter the rows below. Do not update them!
360
+ // $filters = array(
361
+ // 'wp_mail_smtp',
362
+ // 'wp_mail_smtp_version',
363
+ // 'wp_mail_smtp_debug',
364
+ // );
365
+ //
366
+ // $filters = apply_filters('wpstg_filter_options_replace', $filters);
367
+ //
368
+ // $where = "";
369
+ // foreach($filters as $filter){
370
+ // $where .= " AND option_name <> '" . $filter . "'";
371
+ // }
372
+ //
373
+ // $updateOptions = $this->db->query(
374
+ // $this->db->prepare(
375
+ // "UPDATE IGNORE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s" . $where, $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
376
+ // )
377
+ // );
378
+ //
379
+ // if( !$updateOptions ) {
380
+ // $this->log( "Preparing Data Step4: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_ERROR );
381
+ // $this->returnException( "Data Crunching Step 4: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
382
+ // return false;
383
+ // }
384
385
return true;
386
}
470
471
$this->log( "Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}" );
472
473
+ // Skip - Table does not exist
474
if( false === $this->isTable( $this->prefix . 'options' ) ) {
475
return true;
476
}
477
478
+ // Skip - Table is not selected or updated
479
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
480
+ $this->log( "Preparing Data Step7: Skipping" );
481
+ return true;
482
+ }
483
+
484
$result = $this->db->query(
485
$this->db->prepare(
486
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'", ' '
505
506
$this->log( "Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}" );
507
508
+ // Skip - Table does not exist
509
if( false === $this->isTable( $this->prefix . 'options' ) ) {
510
return true;
511
}
512
513
+ // Skip - Table is not selected or updated
514
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
515
+ $this->log( "Preparing Data Step8: Skipping" );
516
+ return true;
517
+ }
518
+
519
$result = $this->db->query(
520
$this->db->prepare(
521
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'", ' '
544
return true;
545
}
546
547
+ // Skip - Table is not selected or updated
548
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
549
+ $this->log( "Preparing Data Step9: Skipping" );
550
+ return true;
551
+ }
552
+
553
$result = $this->db->query(
554
$this->db->prepare(
555
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'blog_public'", '0'
651
return true;
652
}
653
654
+ /**
655
+ * Update Table Prefix in wp_options
656
+ * @return bool
657
+ */
658
+ protected function step12() {
659
+ $this->log( "Preparing Data Step12: Updating db prefix in {$this->prefix}options. Error: {$this->db->last_error}" );
660
+
661
+ // Skip - Table does not exist
662
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
663
+ return true;
664
+ }
665
+
666
+ // Skip - Table is not selected or updated
667
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
668
+ $this->log( "Preparing Data Step12: Skipping" );
669
+ return true;
670
+ }
671
+
672
+
673
+ $this->log( "Updating db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
674
+
675
+ // Filter the rows below. Do not update them!
676
+ $filters = array(
677
+ 'wp_mail_smtp',
678
+ 'wp_mail_smtp_version',
679
+ 'wp_mail_smtp_debug',
680
+ );
681
+
682
+ $filters = apply_filters( 'wpstg_filter_options_replace', $filters );
683
+
684
+ $where = "";
685
+ foreach ( $filters as $filter ) {
686
+ $where .= " AND option_name <> '" . $filter . "'";
687
+ }
688
+
689
+ $updateOptions = $this->db->query(
690
+ $this->db->prepare(
691
+ "UPDATE IGNORE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s" . $where, $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
692
+ )
693
+ );
694
+
695
+ if( !$updateOptions ) {
696
+ $this->log( "Preparing Data Step12: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_ERROR );
697
+ $this->returnException( "Data Crunching Step 15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
698
+ return false;
699
+ }
700
+
701
+ return true;
702
+ }
703
+
704
/**
705
* Return URL to staging site
706
* @return string
apps/Backend/Modules/Jobs/Database.php CHANGED
@@ -35,9 +35,25 @@ class Database extends JobExecutable
35
// Variables
36
$this->total = count($this->options->tables);
37
$this->db = WPStaging::getInstance()->get("wpdb");
38
}
39
40
/**
41
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
42
* @return void
43
*/
@@ -262,4 +278,5 @@ class Database extends JobExecutable
262
)
263
);
264
}
265
}
35
// Variables
36
$this->total = count($this->options->tables);
37
$this->db = WPStaging::getInstance()->get("wpdb");
38
+ $this->isFatalError();
39
+
40
}
41
42
+
43
/**
44
+ * Return fatal error and stops here if subfolder already exists
45
+ * and mainJob is not updating the clone
46
+ * @return boolean
47
+ */
48
+ private function isFatalError(){
49
+ $path = trailingslashit(get_home_path()) . $this->options->cloneDirectoryName;
50
+ if (isset($this->options->mainJob) && $this->options->mainJob !== 'updating' && is_dir($path)){
51
+ $this->returnException( " Can not continue! Change the name of the clone or delete existing folder. Then try again. Folder already exists: " . $path );
52
+ }
53
+ return false;
54
+ }
55
+
56
+ /**
57
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
58
* @return void
59
*/
278
)
279
);
280
}
281
+
282
}
apps/Backend/Modules/Jobs/Delete.php CHANGED
@@ -367,71 +367,15 @@ class Delete extends Job {
367
);
368
}
369
370
- /**
371
- * Delete contents of the directory if there are no directories in it and then delete itself
372
- * @param string $path
373
- * @return mixed
374
- */
375
- // private function processDirectory($path) {
376
- // // We hit the limit, stop
377
- // if ($this->shouldStop($path)) {
378
- // $this->updateJob();
379
- // return false;
380
- // }
381
- //
382
- // $this->totalRecursion++;
383
- //
384
- // $contents = new \DirectoryIterator($path);
385
- //
386
- // foreach ($contents as $content => $value) {
387
- //
388
- // // Skip dots
389
- // if ($content->isDot())
390
- //
391
- //
392
- // // Get into the directory
393
- // if (!$content->isLink() && $content->isDir()) {
394
- // return $this->processDirectory($content->getRealPath());
395
- // }
396
- //
397
- // // Delete file
398
- // if ($content->isFile()) {
399
- // @unlink($content->getRealPath());
400
- // }
401
- // }
402
- //
403
- // // Delete directory
404
- // $this->job->lastDeletedDirectory = realpath($path . "/..");
405
- // @rmdir($path);
406
- // $this->updateJob();
407
- // $this->processDirectory($this->job->nextDirectoryToDelete);
408
- // }
409
410
- /**
411
- * @param string $path
412
- * @return bool
413
- */
414
- // private function shouldStop($path) {
415
- // // Just to make sure the root dir is never deleted!
416
- // if ($path === get_home_path()) {
417
- // $this->log("Fatal Error: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL);
418
- // return true;
419
- // }
420
- //
421
- // // Check if threshold is reached and is valid dir
422
- // return (
423
- // $this->isOverThreshold() ||
424
- // !is_dir($path) ||
425
- // $this->isDirectoryDeletingFinished()
426
- // );
427
- // }
428
429
/**
430
*
431
* @return boolean
432
*/
433
- public function isFatalError() {
434
- if (rtrim($this->clone->path, "/") == rtrim(get_home_path(), "/")) {
435
return true;
436
}
437
return false;
@@ -465,18 +409,6 @@ class Delete extends Job {
465
wp_die(json_encode($response));
466
}
467
468
- /**
469
- * Get json response
470
- * return json
471
- */
472
- // private function returnException($message = ''){
473
- // wp_die( json_encode(array(
474
- // 'job' => 'delete',
475
- // 'status' => false,
476
- // 'message' => $message,
477
- // 'error' => true
478
- // )));
479
- // }
480
/**
481
* Get json response
482
* return json
367
);
368
}
369
370
371
372
/**
373
*
374
* @return boolean
375
*/
376
+ public function isFatalError(){
377
+ $homePath = rtrim(get_home_path(), "/");
378
+ if (rtrim($this->clone->path,"/") == $homePath){
379
return true;
380
}
381
return false;
409
wp_die(json_encode($response));
410
}
411
412
/**
413
* Get json response
414
* return json
apps/Backend/Modules/Jobs/Files.php CHANGED
@@ -166,19 +166,24 @@ class Files extends JobExecutable {
166
167
// File is excluded
168
if ($this->isFileExcluded($file)) {
169
- $this->log("Skipping file by rule: {$file}", Logger::TYPE_INFO);
170
return false;
171
}
172
173
// File is over maximum allowed file size (8MB)
174
- if ($fileSize >= 8000000) {
175
$this->log("Skipping big file: {$file}", Logger::TYPE_INFO);
176
return false;
177
}
178
179
// Invalid file, skipping it as if succeeded
180
- if (!is_file($file) || !is_readable($file)) {
181
- $this->log("Can't read file or file doesn't exist {$file}");
182
return true;
183
}
184
@@ -225,58 +230,8 @@ class Files extends JobExecutable {
225
return $destinationPath;
226
}
227
228
- /**
229
- * Copy File using PHP
230
- * @param string $file
231
- * @param string $destination
232
- * @return bool
233
- */
234
- // private function copy($file, $destination) {
235
- // // Get file size
236
- // $fileSize = filesize($file);
237
- //
238
- //
239
- // // File is over batch size
240
- // if ($fileSize >= $this->settings->batchSize) {
241
- // $this->log("Trying to copy big file: {$file} -> {$destination}", Logger::TYPE_INFO);
242
- // return $this->copyBig($file, $destination, $this->settings->batchSize);
243
- // }
244
- //
245
- // // Attempt to copy
246
- // if (!@copy($file, $destination)) {
247
- // $this->log("Failed to copy file to destination: {$file} -> {$destination}", Logger::TYPE_ERROR);
248
- // return false;
249
- // }
250
- //
251
- // return true;
252
- // }
253
254
- /**
255
- * Copy bigger files than $this->settings->batchSize
256
- * @param string $file
257
- * @param string $destination
258
- * @return bool
259
- *
260
- * @deprecated since version 2.0.0 (Supported only in php 5.5.11 and later)
261
- */
262
- // private function copyBig($file, $destination)
263
- // {
264
- // $bytes = 0;
265
- // $fileInput = new \SplFileObject($file, "rb");
266
- // $fileOutput = new \SplFileObject($destination, 'w');
267
- //
268
- // $this->log("Copying big file; {$file} -> {$destination}");
269
- //
270
- // while (!$fileInput->eof())
271
- // {
272
- // $bytes += $fileOutput->fwrite($fileInput->fread($this->settings->batchSize));
273
- // }
274
- //
275
- // $fileInput = null;
276
- // $fileOutput= null;
277
- //
278
- // return ($bytes > 0);
279
- // }
280
281
/**
282
* Copy bigger files than $this->settings->batchSize
@@ -326,6 +281,14 @@ class Files extends JobExecutable {
326
break;
327
}
328
}
329
return $excluded;
330
}
331
@@ -354,7 +317,7 @@ class Files extends JobExecutable {
354
* @param string $directory
355
* @return boolean
356
*/
357
- protected function isExtraDirectory($directory) {
358
foreach ($this->options->extraDirectories as $extraDirectory) {
359
if (strpos($directory, $extraDirectory) === 0) {
360
return true;
166
167
// File is excluded
168
if ($this->isFileExcluded($file)) {
169
+ $this->debugLog("Skipping file by rule: {$file}", Logger::TYPE_INFO);
170
return false;
171
}
172
173
// File is over maximum allowed file size (8MB)
174
+ if ($fileSize >= $this->settings->maxFileSize * 1000000) {
175
$this->log("Skipping big file: {$file}", Logger::TYPE_INFO);
176
return false;
177
}
178
179
// Invalid file, skipping it as if succeeded
180
+ if (!is_file($file)) {
181
+ $this->debugLog("Not a file {$file}");
182
+ return true;
183
+ }
184
+ // Invalid file, skipping it as if succeeded
185
+ if (!is_readable($file)) {
186
+ $this->log("Can't read file {$file}");
187
return true;
188
}
189
230
return $destinationPath;
231
}
232
233
234
+
235
236
/**
237
* Copy bigger files than $this->settings->batchSize
281
break;
282
}
283
}
284
+
285
+ // Do not copy wp-config.php if the clone gets updated. This is for security purposes,
286
+ // because if the updating process fails, the staging site would not be accessable any longer
287
+ if (isset($this->options->mainJob ) && $this->options->mainJob == "updating" && stripos(strrev($file), strrev("wp-config.php")) === 0){
288
+ $excluded = true;
289
+ }
290
+
291
+
292
return $excluded;
293
}
294
317
* @param string $directory
318
* @return boolean
319
*/
320
+ private function isExtraDirectory($directory) {
321
foreach ($this->options->extraDirectories as $extraDirectory) {
322
if (strpos($directory, $extraDirectory) === 0) {
323
return true;
apps/Backend/Modules/Jobs/Job.php CHANGED
@@ -1,357 +1,337 @@
1
<?php
2
namespace WPStaging\Backend\Modules\Jobs;
3
4
// No Direct Access
5
- if (!defined("WPINC"))
6
- {
7
- die;
8
}
9
10
use WPStaging\Backend\Modules\Jobs\Interfaces\JobInterface;
11
use WPStaging\Utils\Logger;
12
use WPStaging\WPStaging;
13
use WPStaging\Utils\Cache;
14
15
/**
16
* Class Job
17
* @package WPStaging\Backend\Modules\Jobs
18
*/
19
- abstract class Job implements JobInterface
20
- {
21
-
22
- const EXECUTION_TIME_RATIO = 0.8;
23
-
24
- const MAX_MEMORY_RATIO = 0.8;
25
-
26
- /**
27
- * @var Cache
28
- */
29
- protected $cache;
30
-
31
- /**
32
- * @var Logger
33
- */
34
- protected $logger;
35
-
36
- /**
37
- * @var bool
38
- */
39
- protected $hasLoggedFileNameSet = false;
40
-
41
- /**
42
- * @var object
43
- */
44
- protected $options;
45
-
46
- /**
47
- * @var object
48
- */
49
- protected $settings;
50
-
51
- /**
52
- * System total maximum memory consumption
53
- * @var int
54
- */
55
- protected $maxMemoryLimit;
56
-
57
- /**
58
- * Script maximum memory consumption
59
- * @var int
60
- */
61
- protected $memoryLimit;
62
-
63
- /**
64
- * @var int
65
- */
66
- protected $maxExecutionTime;
67
-
68
-
69
- /**
70
- * @var int
71
- */
72
- protected $executionLimit;
73
-
74
- /**
75
- * @var int
76
- */
77
- protected $totalRecursion;
78
-
79
- /**
80
- * @var int
81
- */
82
- protected $maxRecursionLimit;
83
-
84
- /**
85
- * @var int
86
- */
87
- protected $start;
88
-
89
- /**
90
- * Job constructor.
91
- */
92
- public function __construct()
93
- {
94
- // Get max limits
95
- $this->start = $this->time();
96
- $this->maxMemoryLimit = $this->getMemoryInBytes(@ini_get("memory_limit"));
97
-
98
-
99
- //$this->maxExecutionTime = (int) ini_get("max_execution_time");
100
- $this->maxExecutionTime = (int) 30;
101
-
102
// if ($this->maxExecutionTime < 1 || $this->maxExecutionTime > 30)
103
// {
104
// $this->maxExecutionTime = 30;
105
// }
106
-
107
- // Services
108
- $this->cache = new Cache(-1, \WPStaging\WPStaging::getContentDir());
109
- $this->logger = WPStaging::getInstance()->get("logger");
110
-
111
- // Settings and Options
112
- $this->options = $this->cache->get("clone_options");
113
- //$this->settings = json_decode(json_encode(get_option("wpstg_settings", array())));
114
- $this->settings = (object) get_option("wpstg_settings", array());
115
-
116
- if (!$this->options)
117
- {
118
- $this->options = new \stdClass();
119
- }
120
-
121
- if (isset($this->options->existingClones) && is_object($this->options->existingClones))
122
- {
123
- $this->options->existingClones = json_decode(json_encode($this->options->existingClones), true);
124
- }
125
-
126
- // check default options
127
- if ( !isset($this->settings) ||
128
- !isset($this->settings->queryLimit) ||
129
- !isset($this->settings->querySRLimit) ||
130
- !isset($this->settings->batchSize) ||
131
- !isset($this->settings->cpuLoad) ||
132
- !isset($this->settings->fileLimit)
133
- )
134
-
135
- {
136
- $this->settings = new \stdClass();
137
- $this->setDefaultSettings();
138
- }
139
-
140
- // Set limits accordingly to CPU LIMITS
141
- $this->setLimits();
142
-
143
- $this->maxRecursionLimit = (int) ini_get("xdebug.max_nesting_level");
144
-
145
- /*
146
- * This is needed to make sure that maxRecursionLimit = -1
147
- * if xdebug is not used in production env.
148
- * For using xdebug, maxRecursionLimit must be larger
149
- * otherwise xdebug is throwing an error 500 while debugging
150
- */
151
- if ($this->maxRecursionLimit < 1)
152
- {
153
- $this->maxRecursionLimit = -1;
154
- }
155
- else
156
- {
157
- $this->maxRecursionLimit = $this->maxRecursionLimit - 50; // just to make sure
158
- }
159
-
160
- if (method_exists($this, "initialize"))
161
- {
162
- $this->initialize();
163
- }
164
- }
165
-
166
- /**
167
- * Job destructor
168
- */
169
- public function __destruct()
170
- {
171
- // Commit logs
172
- $this->logger->commit();
173
- }
174
-
175
- /**
176
- * Set default settings
177
- */
178
- protected function setDefaultSettings(){
179
- $this->settings->queryLimit = "5000";
180
- $this->settings->querySRLimit = "5000";
181
- $this->settings->fileLimit = "1";
182
- $this->settings->batchSize = "2";
183
- $this->settings->cpuLoad = 'medium';
184
- update_option('wpstg_settings', $this->settings);
185
- }
186
-
187
- /**
188
- * Set limits accordingly to
189
- */
190
- protected function setLimits()
191
- {
192
-
193
- if (!isset($this->settings->cpuLoad))
194
- {
195
- $this->settings->cpuLoad = "medium";
196
- }
197
-
198
- $memoryLimit= self::MAX_MEMORY_RATIO;
199
- $timeLimit = self::EXECUTION_TIME_RATIO;
200
-
201
- switch($this->settings->cpuLoad)
202
- {
203
- case "medium":
204
- //$memoryLimit= $memoryLimit / 2; // 0.4
205
- $timeLimit = $timeLimit / 2;
206
- break;
207
- case "low":
208
- //$memoryLimit= $memoryLimit / 4; // 0.2
209
- $timeLimit = $timeLimit / 4;
210
- break;
211
-
212
- case "fast": // 0.8
213
- default:
214
- break;
215
- }
216
-
217
- $this->memoryLimit = $this->maxMemoryLimit * $memoryLimit;
218
- $this->executionLimit = $this->maxExecutionTime * $timeLimit;
219
- }
220
-
221
- /**
222
- * Save options
223
- * @param null|array|object $options
224
- * @return bool
225
- */
226
- protected function saveOptions($options = null)
227
- {
228
- // Get default options
229
- if (null === $options)
230
- {
231
- $options = $this->options;
232
- }
233
-
234
- // Ensure that it is an object
235
- $options = json_decode(json_encode($options));
236
- return $this->cache->save("clone_options", $options);
237
- }
238
-
239
- /**
240
- * @return object
241
- */
242
- public function getOptions()
243
- {
244
- return $this->options;
245
- }
246
-
247
- /**
248
- * @param string $memory
249
- * @return int
250
- */
251
- protected function getMemoryInBytes($memory)
252
- {
253
- // Handle unlimited ones
254
- if (1 > (int) $memory)
255
- {
256
- //return (int) $memory;
257
- // 128 MB default value
258
- return (int) 134217728;
259
- }
260
-
261
- $bytes = (int) $memory; // grab only the number
262
- $size = trim(str_replace($bytes, null, strtolower($memory))); // strip away number and lower-case it
263
-
264
- // Actual calculation
265
- switch($size)
266
- {
267
- case 'k':
268
- $bytes *= 1024;
269
- break;
270
- case 'm':
271
- $bytes *= (1024 * 1024);
272
- break;
273
- case 'g':
274
- $bytes *= (1024 * 1024 * 1024);
275
- break;
276
- }
277
-
278
- return $bytes;
279
- }
280
-
281
- /**
282
- * Format bytes into ini_set favorable form
283
- * @param int $bytes
284
- * @return string
285
- */
286
- protected function formatBytes($bytes)
287
- {
288
- if ((int) $bytes < 1)
289
- {
290
- return '';
291
- }
292
-
293
- $units = array('B', 'K', 'M', 'G'); // G since PHP 5.1.x so we are good!
294
-
295
- $bytes = (int) $bytes;
296
- $base = log($bytes) / log(1000);
297
- $pow = pow(1000, $base - floor($base));
298
-
299
- return round($pow, 0) . $units[(int) floor($base)];
300
- }
301
-
302
- /**
303
- * Get current time in seconds
304
- * @return float
305
- */
306
- protected function time()
307
- {
308
- $time = microtime();
309
- $time = explode(' ', $time);
310
- $time = $time[1] + $time[0];
311
- return $time;
312
- }
313
-
314
- /**
315
- * @return bool
316
- */
317
- protected function isOverThreshold()
318
- {
319
- // Check if the memory is over threshold
320
- $usedMemory = (int) @memory_get_usage(true);
321
-
322
- $this->debugLog('Used Memory: ' . $this->formatBytes( $usedMemory ) . ' Max Memory Limit: ' . $this->formatBytes( $this->maxMemoryLimit ) . ' Max Script Memory Limit: ' . $this->formatBytes( $this->memoryLimit), Logger::TYPE_DEBUG );
323
-
324
- if ($usedMemory >= $this->memoryLimit)
325
- {
326
- $this->log('Used Memory: ' . $this->formatBytes($usedMemory) . ' Memory Limit: ' . $this->formatBytes($this->maxMemoryLimit) . ' Max Script memory limit: ' . $this->formatBytes( $this->memoryLimit ), Logger::TYPE_ERROR );
327
- //$this->resetMemory();
328
- return true;
329
- }
330
-
331
- if ($this->isRecursionLimit())
332
- {
333
- //$this->log('RESET RECURSION');
334
- return true;
335
- }
336
-
337
- // Check if execution time is over threshold
338
- ///$time = round($this->start + $this->time(), 4);
339
- $time = round($this->time() - $this->start, 4);
340
-
341
- if ($time >= $this->executionLimit)
342
- {
343
- $this->debugLog('RESET TIME: current time: ' . $time . ', Start Time: ' . $this->start . ', exec time limit: ' . $this->executionLimit);
344
- return true;
345
- }
346
-
347
- return false;
348
- }
349
-
350
- /**
351
- * Attempt to reset memory
352
- * @return bool
353
- * memory
354
- */
355
// protected function resetMemory()
356
// {
357
// $newMemoryLimit = $this->maxMemoryLimit * 2;
@@ -379,13 +359,13 @@ abstract class Job implements JobInterface
379
// return true;
380
// }
381
382
- /**
383
- * Attempt to reset time
384
- * @return bool
385
- *
386
- * @deprecated since version 2.0.0
387
388
- */
389
// protected function resetTime()
390
// {
391
// // Attempt to reset timeout
@@ -400,12 +380,12 @@ abstract class Job implements JobInterface
400
// return true;
401
// }
402
403
- /**
404
- * Reset time limit and memory
405
- * @return bool
406
- *
407
- * @deprecated since version 2.0.0
408
- */
409
// protected function reset()
410
// {
411
// // Attempt to reset time
@@ -423,68 +403,64 @@ abstract class Job implements JobInterface
423
// return true;
424
// }
425
426
- /**
427
- * Checks if calls are over recursion limit
428
- * @return bool
429
- */
430
- protected function isRecursionLimit()
431
- {
432
- return ($this->maxRecursionLimit > 0 && $this->totalRecursion >= $this->maxRecursionLimit);
433
- }
434
-
435
- /**
436
- * @param string $msg
437
- * @param string $type
438
- */
439
- protected function log($msg, $type = Logger::TYPE_INFO)
440
- {
441
-
442
- if (!isset($this->options->clone)){
443
- $this->options->clone = date(DATE_ATOM, mktime(0, 0, 0, 7, 1, 2000));
444
- }
445
-
446
- if (false === $this->hasLoggedFileNameSet && 0 < strlen($this->options->clone))
447
- {
448
- $this->logger->setFileName($this->options->clone);
449
- $this->hasLoggedFileNameSet = true;
450
- }
451
-
452
- $this->logger->add($msg, $type);
453
- }
454
- /**
455
- * @param string $msg
456
- * @param string $type
457
- */
458
- protected function debugLog($msg, $type = Logger::TYPE_INFO)
459
- {
460
-
461
- if (!isset($this->options->clone)){
462
- $this->options->clone = date(DATE_ATOM, mktime(0, 0, 0, 7, 1, 2000));
463
- }
464
-
465
- if (false === $this->hasLoggedFileNameSet && 0 < strlen($this->options->clone))
466
- {
467
- $this->logger->setFileName($this->options->clone);
468
- $this->hasLoggedFileNameSet = true;
469
- }
470
-
471
-
472
- if (isset($this->settings->debugMode)){
473
- $this->logger->add($msg, $type);
474
- }
475
-
476
- }
477
-
478
- /**
479
- * Throw a errror message via json and stop further execution
480
- * @param string $message
481
- */
482
- protected function returnException($message = ''){
483
- wp_die( json_encode(array(
484
- 'job' => isset($this->options->currentJob) ? $this->options->currentJob : '',
485
- 'status' => false,
486
- 'message' => $message,
487
- 'error' => true
488
- )));
489
- }
490
- }
1
<?php
2
+
3
namespace WPStaging\Backend\Modules\Jobs;
4
5
// No Direct Access
6
+ if( !defined( "WPINC" ) ) {
7
+ die;
8
}
9
10
use WPStaging\Backend\Modules\Jobs\Interfaces\JobInterface;
11
use WPStaging\Utils\Logger;
12
use WPStaging\WPStaging;
13
use WPStaging\Utils\Cache;
14
+ use WPStaging\Utils\Multisite;
15
16
/**
17
* Class Job
18
* @package WPStaging\Backend\Modules\Jobs
19
*/
20
+ abstract class Job implements JobInterface {
21
+
22
+ const EXECUTION_TIME_RATIO = 0.8;
23
+ const MAX_MEMORY_RATIO = 0.8;
24
+
25
+ /**
26
+ * @var Cache
27
+ */
28
+ protected $cache;
29
+
30
+ /**
31
+ * @var Logger
32
+ */
33
+ protected $logger;
34
+
35
+ /**
36
+ * @var bool
37
+ */
38
+ protected $hasLoggedFileNameSet = false;
39
+
40
+ /**
41
+ * @var object
42
+ */
43
+ protected $options;
44
+
45
+ /**
46
+ * @var object
47
+ */
48
+ protected $settings;
49
+
50
+ /**
51
+ * System total maximum memory consumption
52
+ * @var int
53
+ */
54
+ protected $maxMemoryLimit;
55
+
56
+ /**
57
+ * Script maximum memory consumption
58
+ * @var int
59
+ */
60
+ protected $memoryLimit;
61
+
62
+ /**
63
+ * @var int
64
+ */
65
+ protected $maxExecutionTime;
66
+
67
+ /**
68
+ * @var int
69
+ */
70
+ protected $executionLimit;
71
+
72
+ /**
73
+ * @var int
74
+ */
75
+ protected $totalRecursion;
76
+
77
+ /**
78
+ * @var int
79
+ */
80
+ protected $maxRecursionLimit;
81
+
82
+ /**
83
+ * Multisite Home Url
84
+ * @var string
85
+ */
86
+ protected $multisiteHomeUrl;
87
+
88
+ /**
89
+ * @var int
90
+ */
91
+ protected $start;
92
+
93
+ /**
94
+ * Job constructor.
95
+ */
96
+ public function __construct() {
97
+ // Get max limits
98
+ $this->start = $this->time();
99
+ $this->maxMemoryLimit = $this->getMemoryInBytes( @ini_get( "memory_limit" ) );
100
+
101
+ $multisite = new Multisite;
102
+ $this->multisiteHomeUrl = $multisite->getHomeURL();
103
+
104
+ //$this->maxExecutionTime = (int) ini_get("max_execution_time");
105
+ $this->maxExecutionTime = ( int ) 30;
106
+
107
// if ($this->maxExecutionTime < 1 || $this->maxExecutionTime > 30)
108
// {
109
// $this->maxExecutionTime = 30;
110
// }
111
+ // Services
112
+ $this->cache = new Cache( -1, \WPStaging\WPStaging::getContentDir() );
113
+ $this->logger = WPStaging::getInstance()->get( "logger" );
114
+
115
+ // Settings and Options
116
+ $this->options = $this->cache->get( "clone_options" );
117
+
118
+ $this->settings = ( object ) get_option( "wpstg_settings", array() );
119
+
120
+ if( !$this->options ) {
121
+ $this->options = new \stdClass();
122
+ }
123
+
124
+ if( isset( $this->options->existingClones ) && is_object( $this->options->existingClones ) ) {
125
+ $this->options->existingClones = json_decode( json_encode( $this->options->existingClones ), true );
126
+ }
127
+
128
+ // check default options
129
+ if( !isset( $this->settings ) ||
130
+ !isset( $this->settings->queryLimit ) ||
131
+ !isset( $this->settings->querySRLimit ) ||
132
+ !isset( $this->settings->batchSize ) ||
133
+ !isset( $this->settings->cpuLoad ) ||
134
+ !isset( $this->settings->maxFileSize ) ||
135
+ !isset( $this->settings->fileLimit )
136
+ ) {
137
+ $this->settings = new \stdClass();
138
+ $this->setDefaultSettings();
139
+ }
140
+
141
+ // Set limits accordingly to CPU LIMITS
142
+ $this->setLimits();
143
+
144
+ $this->maxRecursionLimit = ( int ) ini_get( "xdebug.max_nesting_level" );
145
+
146
+ /*
147
+ * This is needed to make sure that maxRecursionLimit = -1
148
+ * if xdebug is not used in production env.
149
+ * For using xdebug, maxRecursionLimit must be larger
150
+ * otherwise xdebug is throwing an error 500 while debugging
151
+ */
152
+ if( $this->maxRecursionLimit < 1 ) {
153
+ $this->maxRecursionLimit = -1;
154
+ } else {
155
+ $this->maxRecursionLimit = $this->maxRecursionLimit - 50; // just to make sure
156
+ }
157
+
158
+ if( method_exists( $this, "initialize" ) ) {
159
+ $this->initialize();
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Job destructor
165
+ */
166
+ public function __destruct() {
167
+ // Commit logs
168
+ $this->logger->commit();
169
+ }
170
+
171
+ /**
172
+ * Set default settings
173
+ */
174
+ protected function setDefaultSettings() {
175
+ $this->settings->queryLimit = "20000";
176
+ $this->settings->querySRLimit = "5000";
177
+ $this->settings->fileLimit = "1";
178
+ $this->settings->batchSize = "2";
179
+ $this->settings->cpuLoad = 'medium';
180
+ $this->settings->maxFileSize = 8;
181
+ update_option( 'wpstg_settings', $this->settings );
182
+ }
183
+
184
+ /**
185
+ * Set limits accordingly to
186
+ */
187
+ protected function setLimits() {
188
+
189
+ if( !isset( $this->settings->cpuLoad ) ) {
190
+ $this->settings->cpuLoad = "medium";
191
+ }
192
+
193
+ $memoryLimit = self::MAX_MEMORY_RATIO;
194
+ $timeLimit = self::EXECUTION_TIME_RATIO;
195
+
196
+ switch ( $this->settings->cpuLoad ) {
197
+ case "medium":
198
+ //$memoryLimit= $memoryLimit / 2; // 0.4
199
+ $timeLimit = $timeLimit / 2;
200
+ break;
201
+ case "low":
202
+ //$memoryLimit= $memoryLimit / 4; // 0.2
203
+ $timeLimit = $timeLimit / 4;
204
+ break;
205
+
206
+ case "fast": // 0.8
207
+ default:
208
+ break;
209
+ }
210
+
211
+ $this->memoryLimit = $this->maxMemoryLimit * $memoryLimit;
212
+ $this->executionLimit = $this->maxExecutionTime * $timeLimit;
213
+ }
214
+
215
+ /**
216
+ * Save options
217
+ * @param null|array|object $options
218
+ * @return bool
219
+ */
220
+ protected function saveOptions( $options = null ) {
221
+ // Get default options
222
+ if( null === $options ) {
223
+ $options = $this->options;
224
+ }
225
+
226
+ // Ensure that it is an object
227
+ $options = json_decode( json_encode( $options ) );
228
+ return $this->cache->save( "clone_options", $options );
229
+ }
230
+
231
+ /**
232
+ * @return object
233
+ */
234
+ public function getOptions() {
235
+ return $this->options;
236
+ }
237
+
238
+ /**
239
+ * @param string $memory
240
+ * @return int
241
+ */
242
+ protected function getMemoryInBytes( $memory ) {
243
+ // Handle unlimited ones
244
+ if( 1 > ( int ) $memory ) {
245
+ //return (int) $memory;
246
+ // 128 MB default value
247
+ return ( int ) 134217728;
248
+ }
249
+
250
+ $bytes = ( int ) $memory; // grab only the number
251
+ $size = trim( str_replace( $bytes, null, strtolower( $memory ) ) ); // strip away number and lower-case it
252
+ // Actual calculation
253
+ switch ( $size ) {
254
+ case 'k':
255
+ $bytes *= 1024;
256
+ break;
257
+ case 'm':
258
+ $bytes *= (1024 * 1024);
259
+ break;
260
+ case 'g':
261
+ $bytes *= (1024 * 1024 * 1024);
262
+ break;
263
+ }
264
+
265
+ return $bytes;
266
+ }
267
+
268
+ /**
269
+ * Format bytes into ini_set favorable form
270
+ * @param int $bytes
271
+ * @return string
272
+ */
273
+ protected function formatBytes( $bytes ) {
274
+ if( ( int ) $bytes < 1 ) {
275
+ return '';
276
+ }
277
+
278
+ $units = array('B', 'K', 'M', 'G'); // G since PHP 5.1.x so we are good!
279
+
280
+ $bytes = ( int ) $bytes;
281
+ $base = log( $bytes ) / log( 1000 );
282
+ $pow = pow( 1000, $base - floor( $base ) );
283
+
284
+ return round( $pow, 0 ) . $units[( int ) floor( $base )];
285
+ }
286
+
287
+ /**
288
+ * Get current time in seconds
289
+ * @return float
290
+ */
291
+ protected function time() {
292
+ $time = microtime();
293
+ $time = explode( ' ', $time );
294
+ $time = $time[1] + $time[0];
295
+ return $time;
296
+ }
297
+
298
+ /**
299
+ * @return bool
300
+ */
301
+ protected function isOverThreshold() {
302
+ // Check if the memory is over threshold
303
+ $usedMemory = ( int ) @memory_get_usage( true );
304
+
305
+ $this->debugLog( 'Used Memory: ' . $this->formatBytes( $usedMemory ) . ' Max Memory Limit: ' . $this->formatBytes( $this->maxMemoryLimit ) . ' Max Script Memory Limit: ' . $this->formatBytes( $this->memoryLimit ), Logger::TYPE_DEBUG );
306
+
307
+ if( $usedMemory >= $this->memoryLimit ) {
308
+ $this->log( 'Used Memory: ' . $this->formatBytes( $usedMemory ) . ' Memory Limit: ' . $this->formatBytes( $this->maxMemoryLimit ) . ' Max Script memory limit: ' . $this->formatBytes( $this->memoryLimit ), Logger::TYPE_ERROR );
309
+ //$this->resetMemory();
310
+ return true;
311
+ }
312
+
313
+ if( $this->isRecursionLimit() ) {
314
+ //$this->log('RESET RECURSION');
315
+ return true;
316
+ }
317
+
318
+ // Check if execution time is over threshold
319
+ ///$time = round($this->start + $this->time(), 4);
320
+ $time = round( $this->time() - $this->start, 4 );
321
+
322
+ if( $time >= $this->executionLimit ) {
323
+ $this->debugLog( 'RESET TIME: current time: ' . $time . ', Start Time: ' . $this->start . ', exec time limit: ' . $this->executionLimit );
324
+ return true;
325
+ }
326
+
327
+ return false;
328
+ }
329
+
330
+ /**
331
+ * Attempt to reset memory
332
+ * @return bool
333
+ * memory
334
+ */
335
// protected function resetMemory()
336
// {
337
// $newMemoryLimit = $this->maxMemoryLimit * 2;
359
// return true;
360
// }
361
362
+ /**
363
+ * Attempt to reset time
364
+ * @return bool
365
+ *
366
+ * @deprecated since version 2.0.0
367
368
+ */
369
// protected function resetTime()
370
// {
371
// // Attempt to reset timeout
380
// return true;
381
// }
382
383
+ /**
384
+ * Reset time limit and memory
385
+ * @return bool
386
+ *
387
+ * @deprecated since version 2.0.0
388
+ */
389
// protected function reset()
390
// {
391
// // Attempt to reset time
403
// return true;
404
// }
405
406
+ /**
407
+ * Checks if calls are over recursion limit
408
+ * @return bool
409
+ */
410
+ protected function isRecursionLimit() {
411
+ return ($this->maxRecursionLimit > 0 && $this->totalRecursion >= $this->maxRecursionLimit);
412
+ }
413
+
414
+ /**
415
+ * @param string $msg
416
+ * @param string $type
417
+ */
418
+ protected function log( $msg, $type = Logger::TYPE_INFO ) {
419
+
420
+ if( !isset( $this->options->clone ) ) {
421
+ $this->options->clone = date( DATE_ATOM, mktime( 0, 0, 0, 7, 1, 2000 ) );
422
+ }
423
+
424
+ if( false === $this->hasLoggedFileNameSet && 0 < strlen( $this->options->clone ) ) {
425
+ $this->logger->setFileName( $this->options->clone );
426
+ $this->hasLoggedFileNameSet = true;
427
+ }
428
+
429
+ $this->logger->add( $msg, $type );
430
+ }
431
+
432
+ /**
433
+ * @param string $msg
434
+ * @param string $type
435
+ */
436
+ protected function debugLog( $msg, $type = Logger::TYPE_INFO ) {
437
+
438
+ if( !isset( $this->options->clone ) ) {
439
+ $this->options->clone = date( DATE_ATOM, mktime( 0, 0, 0, 7, 1, 2000 ) );
440
+ }
441
+
442
+ if( false === $this->hasLoggedFileNameSet && 0 < strlen( $this->options->clone ) ) {
443
+ $this->logger->setFileName( $this->options->clone );
444
+ $this->hasLoggedFileNameSet = true;
445
+ }
446
+
447
+
448
+ if( isset( $this->settings->debugMode ) ) {
449
+ $this->logger->add( $msg, $type );
450
+ }
451
+ }
452
+
453
+ /**
454
+ * Throw a errror message via json and stop further execution
455
+ * @param string $message
456
+ */
457
+ protected function returnException( $message = '' ) {
458
+ wp_die( json_encode( array(
459
+ 'job' => isset( $this->options->currentJob ) ? $this->options->currentJob : '',
460
+ 'status' => false,
461
+ 'message' => $message,
462
+ 'error' => true
463
+ ) ) );
464
+ }
465
+
466
+ }
apps/Backend/Modules/Jobs/Multisite/Data.php ADDED
@@ -0,0 +1,924 @@
1
+ <?php
2
+
3
+ namespace WPStaging\Backend\Modules\Jobs\Multisite;
4
+
5
+ // No Direct Access
6
+ if( !defined( "WPINC" ) ) {
7
+ die;
8
+ }
9
+
10
+ use WPStaging\Utils\Logger;
11
+ use WPStaging\WPStaging;
12
+ use WPStaging\Utils\Helper;
13
+ use WPStaging\Utils\Multisite;
14
+ use WPStaging\Utils\Strings;
15
+ use WPStaging\Backend\Modules\Jobs\JobExecutable;
16
+
17
+ /**
18
+ * Class Data
19
+ * @package WPStaging\Backend\Modules\Jobs
20
+ */
21
+ class Data extends JobExecutable {
22
+
23
+ /**
24
+ * @var \wpdb
25
+ */
26
+ private $db;
27
+
28
+ /**
29
+ * @var string
30
+ */
31
+ private $prefix;
32
+
33
+ /**
34
+ * Tables e.g wpstg3_options
35
+ * @var array
36
+ */
37
+ private $tables;
38
+
39
+ /**
40
+ * Initialize
41
+ */
42
+ public function initialize() {
43
+ $this->db = WPStaging::getInstance()->get( "wpdb" );
44
+
45
+ $this->prefix = $this->options->prefix;
46
+
47
+ $this->getTables();
48
+
49
+ // Fix current step
50
+ if( 0 == $this->options->currentStep ) {
51
+ $this->options->currentStep = 1;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Get a list of tables to copy
57
+ */
58
+ private function getTables(){
59
+ $strings = new Strings();
60
+ $this->tables = array();
61
+ foreach($this->options->tables as $table){
62
+ $this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->db->prefix, null, $table );
63
+ }
64
+ // Add extra global tables from main multisite (wpstgx_users and wpstgx_usermeta)
65
+ $this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->db->prefix, null, 'users' );
66
+ $this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->db->prefix, null, 'usermeta' );
67
+
68
+ }
69
+ /**
70
+ * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
71
+ * @return void
72
+ */
73
+ protected function calculateTotalSteps() {
74
+ $this->options->totalSteps = 15;
75
+ }
76
+
77
+ /**
78
+ * Start Module
79
+ * @return object
80
+ */
81
+ public function start() {
82
+ // Execute steps
83
+ $this->run();
84
+
85
+ // Save option, progress
86
+ $this->saveOptions();
87
+
88
+ return ( object ) $this->response;
89
+ }
90
+
91
+ /**
92
+ * Execute the Current Step
93
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
94
+ * @return bool
95
+ */
96
+ protected function execute() {
97
+ // Fatal error. Let this happen never and break here immediately
98
+ if( $this->isRoot() ) {
99
+ return false;
100
+ }
101
+
102
+ // Over limits threshold
103
+ if( $this->isOverThreshold() ) {
104
+ // Prepare response and save current progress
105
+ $this->prepareResponse( false, false );
106
+ $this->saveOptions();
107
+ return false;
108
+ }
109
+
110
+ // No more steps, finished
111
+ if( $this->isFinished() ) {
112
+ $this->prepareResponse( true, false );
113
+ return false;
114
+ }
115
+
116
+ // Execute step
117
+ $stepMethodName = "step" . $this->options->currentStep;
118
+ if( !$this->{$stepMethodName}() ) {
119
+ $this->prepareResponse( false, false );
120
+ return false;
121
+ }
122
+
123
+ // Prepare Response
124
+ $this->prepareResponse();
125
+
126
+ // Not finished
127
+ return true;
128
+ }
129
+
130
+ /**
131
+ * Checks Whether There is Any Job to Execute or Not
132
+ * @return bool
133
+ */
134
+ protected function isFinished() {
135
+ return (
136
+ $this->options->currentStep > $this->options->totalSteps ||
137
+ !method_exists( $this, "step" . $this->options->currentStep )
138
+ );
139
+ }
140
+
141
+ /**
142
+ * Check if current operation is done on the root folder or on the live DB
143
+ * @return boolean
144
+ */
145
+ protected function isRoot() {
146
+
147
+ // Prefix is the same as the one of live site
148
+ $wpdb = WPStaging::getInstance()->get( "wpdb" );
149
+ if( $wpdb->prefix === $this->prefix ) {
150
+ return true;
151
+ }
152
+
153
+ // CloneName is empty
154
+ $name = ( array ) $this->options->cloneDirectoryName;
155
+ if( empty( $name ) ) {
156
+ return true;
157
+ }
158
+
159
+ // Live Path === Staging path
160
+ if( $this->multisiteHomeUrl . $this->options->cloneDirectoryName === $this->multisiteHomeUrl ) {
161
+ return true;
162
+ }
163
+
164
+ return false;
165
+ }
166
+
167
+ /**
168
+ * Check if table exists
169
+ * @param string $table
170
+ * @return boolean
171
+ */
172
+ protected function isTable( $table ) {
173
+ if( $this->db->get_var( "SHOW TABLES LIKE '{$table}'" ) != $table ) {
174
+ $this->log( "Table {$table} does not exists", Logger::TYPE_ERROR );
175
+ return false;
176
+ }
177
+ return true;
178
+ }
179
+
180
+ /**
181
+ * Get the install sub directory if WP is installed in sub directory
182
+ * @return string
183
+ */
184
+ protected function getSubDir() {
185
+ return '/';
186
+
187
+ // $home = get_option( 'home' );
188
+ // $siteurl = get_option( 'siteurl' );
189
+ //
190
+ // if( empty( $home ) || empty( $siteurl ) ) {
191
+ // return '/';
192
+ // }
193
+ //
194
+ // $dir = str_replace( $home, '', $siteurl );
195
+ // return '/' . str_replace( '/', '', $dir ) . '/';
196
+ }
197
+
198
+ /**
199
+ * Get path to wp-config.php if it's located in parent folder and not root level
200
+ * @return mixed string | boolean
201
+ */
202
+ // protected function getPathWpConfig() {
203
+ // $dir = trailingslashit( dirname( ABSPATH ) );
204
+ //
205
+ // if( is_file( $dir . 'wp-config.php' ) ) {
206
+ // return $dir . 'wp-config.php';
207
+ // }
208
+ // return false;
209
+ // }
210
+
211
+ /**
212
+ * Copy wp-config.php if it is located outside of root one level up
213
+ * @todo Needs some more testing before it will be released
214
+ * @return boolean
215
+ */
216
+ // protected function step0(){
217
+ // $this->log( "Preparing Data Step0: Check if wp-config.php is located in root path", Logger::TYPE_INFO );
218
+ //
219
+ // $dir = trailingslashit( dirname( ABSPATH ) );
220
+ //
221
+ // $source = $dir . 'wp-config.php';
222
+ //
223
+ // $destination = trailingslashit(ABSPATH) . $this->clone->cloneDirectoryName . DIRECTORY_SEPARATOR . 'wp-config.php';
224
+ //
225
+ //
226
+ // // Do not do anything
227
+ // if( (!is_file( $source ) && !is_link($source)) || is_file($destination) ) {
228
+ // $this->log( "Preparing Data Step0: Skip it", Logger::TYPE_INFO );
229
+ // return true;
230
+ // }
231
+ //
232
+ // // Copy target of a symbolic link
233
+ // if (is_link($source)){
234
+ // $this->log( "Preparing Data Step0: Symbolic link found...", Logger::TYPE_INFO );
235
+ // if (!@copy(readlink($source), $destination)) {
236
+ // $this->log("Preparing Data Step0: Failed to copy wp-config.php {$source} -> {$destination}", Logger::TYPE_ERROR);
237
+ // return true;
238
+ // }
239
+ // }
240
+ //
241
+ // // regular copy of wp-config.php
242
+ // if (!@copy($source, $destination)) {
243
+ // $this->log("Preparing Data Step0: Failed to copy wp-config.php {$source} -> {$destination}", Logger::TYPE_ERROR);
244
+ // return true;
245
+ // }
246
+ // $this->log( "Preparing Data Step0: Successfull", Logger::TYPE_INFO );
247
+ // return true;
248
+ //
249
+ // }
250
+
251
+ /**
252
+ * Replace "siteurl" and "home"
253
+ * @return bool
254
+ */
255
+ protected function step1() {
256
+ $this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
257
+
258
+ // Skip - Table does not exist
259
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
260
+ return true;
261
+ }
262
+ // Skip - Table is not selected or updated
263
+ if (!in_array($this->prefix . 'options', $this->tables)){
264
+ $this->log("Preparing Data Step1: Skipping");
265
+ return true;
266
+ }
267
+
268
+ // Installed in sub-directory
269
+ if( $this->isSubDir() ) {
270
+ $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->multisiteHomeUrl, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName );
271
+ // Replace URLs
272
+ $result = $this->db->query(
273
+ $this->db->prepare(
274
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", rtrim( $this->multisiteHomeUrl, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName
275
+ )
276
+ );
277
+ } else {
278
+ $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->multisiteHomeUrl, "/" ) . '/' . $this->options->cloneDirectoryName );
279
+ // Replace URLs
280
+ $result = $this->db->query(
281
+ $this->db->prepare(
282
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->multisiteHomeUrl . '/' . $this->options->cloneDirectoryName
283
+ )
284
+ );
285
+ }
286
+
287
+
288
+ // All good
289
+ if( $result ) {
290
+ return true;
291
+ }
292
+
293
+ $this->log( "Preparing Data Step1: Failed to update siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
294
+ return false;
295
+ }
296
+
297
+ /**
298
+ * Update "wpstg_is_staging_site"
299
+ * @return bool
300
+ */
301
+ protected function step2() {
302
+
303
+ $this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
304
+
305
+ // Skip - Table does not exist
306
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
307
+ return true;
308
+ }
309
+ // Skip - Table is not selected or updated
310
+ if (!in_array($this->prefix . 'options', $this->tables)){
311
+ $this->log("Preparing Data Step2: Skipping");
312
+ return true;
313
+ }
314
+
315
+ $result = $this->db->query(
316
+ $this->db->prepare(
317
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'", "true"
318
+ )
319
+ );
320
+
321
+ // No errors but no option name such as wpstg_is_staging_site
322
+ if( '' === $this->db->last_error && 0 == $result ) {
323
+ $result = $this->db->query(
324
+ $this->db->prepare(
325
+ "INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_is_staging_site',%s)", "true"
326
+ )
327
+ );
328
+ }
329
+
330
+ // All good
331
+ if( $result ) {
332
+ return true;
333
+ }
334
+
335
+ $this->log( "Preparing Data Step2: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
336
+ return false;
337
+ }
338
+
339
+ /**
340
+ * Update rewrite_rules
341
+ * @return bool
342
+ */
343
+ protected function step3() {
344
+
345
+ $this->log( "Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}" );
346
+
347
+ // Skip - Table does not exist
348
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
349
+ return true;
350
+ }
351
+
352
+ // Skip - Table is not selected or updated
353
+ if (!in_array($this->prefix . 'options', $this->tables)){
354
+ $this->log( "Preparing Data Step3: Skipping" );
355
+ return true;
356
+ }
357
+
358
+ $result = $this->db->query(
359
+ $this->db->prepare(
360
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", ' '
361
+ )
362
+ );
363
+
364
+ // All good
365
+ if( $result ) {
366
+ return true;
367
+ }
368
+
369
+ $this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
370
+ return true;
371
+ }
372
+
373
+ /**
374
+ * Update Table Prefix in wp_usermeta and wp_options
375
+ * @return bool
376
+ */
377
+ protected function step4() {
378
+ $this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. {$this->db->last_error}" );
379
+
380
+ // Skip - Table does not exist
381
+ if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
382
+ return true;
383
+ }
384
+
385
+ // Skip - Table is not selected or updated
386
+ if (!in_array($this->prefix . 'usermeta', $this->tables)){
387
+ $this->log("Preparing Data Step4: Skipping");
388
+ return true;
389
+ }
390
+
391
+ $update = $this->db->query(
392
+ $this->db->prepare(
393
+ "UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, %s, %s) WHERE meta_key LIKE %s", $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
394
+ )
395
+ );
396
+
397
+ if( !$update ) {
398
+ $this->log( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
399
+ $this->returnException( "Data Crunching Step 4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}" );
400
+ return false;
401
+ }
402
+
403
+ // if( false === $this->isTable( $this->prefix . 'options' ) ) {
404
+ // return true;
405
+ // }
406
+
407
+ // $this->log( "Updating db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
408
+ //
409
+ // // Filter the rows below. Do not update them!
410
+ // $filters = array(
411
+ // 'wp_mail_smtp',
412
+ // 'wp_mail_smtp_version',
413
+ // 'wp_mail_smtp_debug',
414
+ // );
415
+ //
416
+ // $filters = apply_filters('wpstg_filter_options_replace', $filters);
417
+ //
418
+ // $where = "";
419
+ // foreach($filters as $filter){
420
+ // $where .= " AND option_name <> '" . $filter . "'";
421
+ // }
422
+ //
423
+ // $updateOptions = $this->db->query(
424
+ // $this->db->prepare(
425
+ // "UPDATE IGNORE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s" . $where, $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
426
+ // )
427
+ // );
428
+ //
429
+ // if( !$updateOptions ) {
430
+ // $this->log( "Preparing Data Step4: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_ERROR );
431
+ // $this->returnException( "Data Crunching Step 4: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
432
+ // return false;
433
+ // }
434
+
435
+ return true;
436
+ }
437
+
438
+ /**
439
+ * Update $table_prefix in wp-config.php
440
+ * @return bool
441
+ */
442
+ protected function step5() {
443
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
444
+
445
+ $this->log( "Preparing Data Step5: Updating table_prefix in {$path} to " . $this->prefix );
446
+ if( false === ($content = file_get_contents( $path )) ) {
447
+ $this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
448
+ return false;
449
+ }
450
+
451
+ // Replace table prefix
452
+ $content = str_replace( '$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content );
453
+
454
+ // Replace URLs
455
+ $content = str_replace( $this->multisiteHomeUrl, $this->multisiteHomeUrl . '/' . $this->options->cloneDirectoryName, $content );
456
+
457
+ if( false === @file_put_contents( $path, $content ) ) {
458
+ $this->log( "Preparing Data Step5: Failed to update $table_prefix in {$path} to " . $this->prefix . ". Can't save contents", Logger::TYPE_ERROR );
459
+ return false;
460
+ }
461
+
462
+ return true;
463
+ }
464
+
465
+ /**
466
+ * Reset index.php to original file
467
+ * This is needed if live site is located in subfolder
468
+ * Check first if main wordpress is used in subfolder and index.php in parent directory
469
+ * @see: https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory
470
+ * @return bool
471
+ */
472
+ protected function step6() {
473
+
474
+ if( !$this->isSubDir() ) {
475
+ $this->debugLog( "Preparing Data Step6: WP installation is not in a subdirectory! All good, skipping this step" );
476
+ return true;
477
+ }
478
+
479
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/index.php";
480
+
481
+ if( false === ($content = file_get_contents( $path )) ) {
482
+ $this->log( "Preparing Data Step6: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR );
483
+ return false;
484
+ }
485
+
486
+
487
+ if( !preg_match( "/(require(.*)wp-blog-header.php' \);)/", $content, $matches ) ) {
488
+ $this->log(
489
+ "Preparing Data Step6: Failed to reset index.php for sub directory; wp-blog-header.php is missing", Logger::TYPE_ERROR
490
+ );
491
+ return false;
492
+ }
493
+ $this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
494
+
495
+ $pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
496
+
497
+ $replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
498
+ $replace.= " // Changed by WP-Staging";
499
+
500
+
501
+
502
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
503
+ $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
504
+ return false;
505
+ }
506
+
507
+ if( false === @file_put_contents( $path, $content ) ) {
508
+ $this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
509
+ return false;
510
+ }
511
+ $this->Log( "Preparing Data: Finished Step 6 successfully" );
512
+ return true;
513
+ }
514
+
515
+ /**
516
+ * Update wpstg_rmpermalinks_executed
517
+ * @return bool
518
+ */
519
+ protected function step7() {
520
+
521
+ $this->log( "Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}" );
522
+
523
+ // Skip - Table does not exist
524
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
525
+ return true;
526
+ }
527
+
528
+ // Skip - Table is not selected or updated
529
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
530
+ $this->log("Preparing Data Step7: Skipping");
531
+ return true;
532
+ }
533
+
534
+ $result = $this->db->query(
535
+ $this->db->prepare(
536
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'", ' '
537
+ )
538
+ );
539
+
540
+ // All good
541
+ if( $result ) {
542
+ $this->Log( "Preparing Data Step7: Finished Step 7 successfully" );
543
+ return true;
544
+ }
545
+
546
+ $this->log( "Failed to update wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_WARNING );
547
+ return true;
548
+ }
549
+
550
+ /**
551
+ * Update permalink_structure
552
+ * @return bool
553
+ */
554
+ protected function step8() {
555
+
556
+ $this->log( "Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}" );
557
+
558
+ // Skip - Table does not exist
559
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
560
+ return true;
561
+ }
562
+
563
+ // Skip - Table is not selected or updated
564
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
565
+ $this->log( "Preparing Data Step8: Skipping" );
566
+ return true;
567
+ }
568
+
569
+ $result = $this->db->query(
570
+ $this->db->prepare(
571
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'", ' '
572
+ )
573
+ );
574
+
575
+ // All good
576
+ if( $result ) {
577
+ $this->Log( "Preparing Data Step8: Finished Step 8 successfully" );
578
+ return true;
579
+ }
580
+
581
+ $this->log( "Failed to update permalink_structure in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
582
+ return true;
583
+ }
584
+
585
+ /**
586
+ * Update blog_public option to not allow staging site to be indexed by search engines
587
+ * @return bool
588
+ */
589
+ protected function step9() {
590
+
591
+ $this->log( "Preparing Data Step9: Set staging site to noindex" );
592
+
593
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
594
+ return true;
595
+ }
596
+
597
+ // Skip - Table is not selected or updated
598
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
599
+ $this->log( "Preparing Data Step9: Skipping" );
600
+ return true;
601
+ }
602
+
603
+ $result = $this->db->query(
604
+ $this->db->prepare(
605
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'blog_public'", '0'
606
+ )
607
+ );
608
+
609
+ // All good
610
+ if( $result ) {
611
+ $this->Log( "Preparing Data Step9: Finished Step 9 successfully" );
612
+ return true;
613
+ }
614
+
615
+ $this->log( "Can not update staging site to noindex. Possible already done!", Logger::TYPE_WARNING );
616
+ return true;
617
+ }
618
+
619
+ /**
620
+ * Update WP_HOME in wp-config.php
621
+ * @return bool
622
+ */
623
+ protected function step10() {
624
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
625
+
626
+ $this->log( "Preparing Data Step10: Updating WP_HOME in wp-config.php to " . $this->getStagingSiteUrl() );
627
+
628
+ if( false === ($content = file_get_contents( $path )) ) {
629
+ $this->log( "Preparing Data Step10: Failed to update WP_HOME in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
630
+ return false;
631
+ }
632
+
633
+
634
+ // Get WP_HOME from wp-config.php
635
+ preg_match( "/define\s*\(\s*'WP_HOME'\s*,\s*(.*)\s*\);/", $content, $matches );
636
+
637
+ if( !empty( $matches[1] ) ) {
638
+ $matches[1];
639
+
640
+ $pattern = "/define\s*\(\s*'WP_HOME'\s*,\s*(.*)\s*\);/";
641
+
642
+ $replace = "define('WP_HOME','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
643
+ $replace.= " // Changed by WP-Staging";
644
+
645
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
646
+ $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
647
+ return false;
648
+ }
649
+ } else {
650
+ $this->log( "Preparing Data Step10: WP_HOME not defined in wp-config.php. Skipping this step." );
651
+ }
652
+
653
+ if( false === @file_put_contents( $path, $content ) ) {
654
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
655
+ return false;
656
+ }
657
+ $this->Log( "Preparing Data: Finished Step 11 successfully" );
658
+ return true;
659
+ }
660
+
661
+ /**
662
+ * Update WP_SITEURL in wp-config.php
663
+ * @return bool
664
+ */
665
+ protected function step11() {
666
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
667
+
668
+ $this->log( "Preparing Data Step11: Updating WP_SITEURL in wp-config.php to " . $this->getStagingSiteUrl() );
669
+
670
+ if( false === ($content = file_get_contents( $path )) ) {
671
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
672
+ return false;
673
+ }
674
+
675
+
676
+ // Get WP_SITEURL from wp-config.php
677
+ preg_match( "/define\s*\(\s*'WP_SITEURL'\s*,\s*(.*)\s*\);/", $content, $matches );
678
+
679
+ if( !empty( $matches[1] ) ) {
680
+ $matches[1];
681
+
682
+ $pattern = "/define\s*\(\s*'WP_SITEURL'\s*,\s*(.*)\s*\);/";
683
+
684
+ $replace = "define('WP_SITEURL','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
685
+ $replace.= " // Changed by WP-Staging";
686
+
687
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
688
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL", Logger::TYPE_ERROR );
689
+ return false;
690
+ }
691
+ } else {
692
+ $this->log( "Preparing Data Step11: WP_SITEURL not defined in wp-config.php. Skipping this step." );
693
+ }
694
+
695
+
696
+ if( false === @file_put_contents( $path, $content ) ) {
697
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
698
+ return false;
699
+ }
700
+ $this->Log( "Preparing Data: Finished Step 11 successfully" );
701
+ return true;
702
+ }
703
+
704
+ /**
705
+ * Update WP_ALLOW_MULTISITE constant in wp-config.php
706
+ * @return bool
707
+ */
708
+ protected function step12() {
709
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
710
+
711
+ $this->log( "Preparing Data Step12: Updating WP_ALLOW_MULTISITE in wp-config.php to false" );
712
+
713
+ if( false === ($content = file_get_contents( $path )) ) {
714
+ $this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
715
+ return false;
716
+ }
717
+
718
+
719
+ // Get WP_SITEURL from wp-config.php
720
+ preg_match( "/define\s*\(\s*'WP_ALLOW_MULTISITE'\s*,\s*(.*)\s*\);/", $content, $matches );
721
+
722
+ if( !empty( $matches[1] ) ) {
723
+ $matches[1];
724
+
725
+ $pattern = "/define\s*\(\s*'WP_ALLOW_MULTISITE'\s*,\s*(.*)\s*\);/";
726
+
727
+ $replace = "define('WP_ALLOW_MULTISITE',false); // " . $matches[1];
728
+ $replace.= " // Changed by WP-Staging";
729
+
730
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
731
+ $this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE", Logger::TYPE_ERROR );
732
+ return false;
733
+ }
734
+ } else {
735
+ $this->log( "Preparing Data Step12: WP_ALLOW_MULTISITE not defined in wp-config.php. Skipping this step." );
736
+ }
737
+
738
+
739
+ if( false === @file_put_contents( $path, $content ) ) {
740
+ $this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE. Can't save contents", Logger::TYPE_ERROR );
741
+ return false;
742
+ }
743
+ $this->Log( "Preparing Data: Finished Step 12 successfully" );
744
+ return true;
745
+ }
746
+
747
+ /**
748
+ * Update MULTISITE constant in wp-config.php
749
+ * @return bool
750
+ */
751
+ protected function step13() {
752
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
753
+
754
+ $this->log( "Preparing Data Step13: Updating MULTISITE in wp-config.php to false" );
755
+
756
+ if( false === ($content = file_get_contents( $path )) ) {
757
+ $this->log( "Preparing Data Step13: Failed to update MULTISITE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
758
+ return false;
759
+ }
760
+
761
+
762
+ // Get WP_SITEURL from wp-config.php
763
+ preg_match( "/define\s*\(\s*'MULTISITE'\s*,\s*(.*)\s*\);/", $content, $matches );
764
+
765
+ if( !empty( $matches[1] ) ) {
766
+ $matches[1];
767
+
768
+ $pattern = "/define\s*\(\s*'MULTISITE'\s*,\s*(.*)\s*\);/";
769
+
770
+ $replace = "define('MULTISITE',false); // " . $matches[1];
771
+ $replace.= " // Changed by WP-Staging";
772
+
773
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
774
+ $this->log( "Preparing Data Step13: Failed to update MULTISITE", Logger::TYPE_ERROR );
775
+ return false;
776
+ }
777
+ } else {
778
+ $this->log( "Preparing Data Step13: MULTISITE not defined in wp-config.php. Skipping this step." );
779
+ }
780
+
781
+
782