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

Version Description

  • Fix: Fatal Error: Remove previous implemented action to not search & replace over incomplete classes. This lead to high memory consumption and error 500 in step 2 of the cloning
  • Fix: Cloning process interupts if there is not data to change in last step of cloning
Download this release

Release Info

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

Code changes from version 2.5.6 to 2.5.7

apps/Backend/Modules/Jobs/Data.php CHANGED
@@ -4,7 +4,7 @@ namespace WPStaging\Backend\Modules\Jobs;
4
 
5
  // No Direct Access
6
  if( !defined( "WPINC" ) ) {
7
- die;
8
  }
9
 
10
  use WPStaging\Utils\Logger;
@@ -18,634 +18,631 @@ use WPStaging\Utils\Strings;
18
  */
19
  class Data extends JobExecutable {
20
 
21
- /**
22
- * @var \wpdb
23
- */
24
- private $db;
25
 
26
- /**
27
- * @var string
28
- */
29
- private $prefix;
30
 
31
- /**
32
- *
33
- * @var string
34
- */
35
- private $homeUrl;
36
 
37
- /**
38
- * Tables e.g wpstg3_options
39
- * @var array
40
- */
41
- private $tables;
42
 
43
- /**
44
- * Initialize
45
- */
46
- public function initialize() {
47
- $this->db = WPStaging::getInstance()->get( "wpdb" );
48
 
49
- $this->prefix = $this->options->prefix;
50
 
51
- $this->getTables();
52
 
53
  $helper = new Helper();
54
- $this->homeUrl = $helper->get_home_url();
55
-
56
- // Fix current step
57
- if( 0 == $this->options->currentStep ) {
58
- $this->options->currentStep = 0;
59
- }
60
- }
61
-
62
- /**
63
- * Get a list of tables to copy
64
- */
65
- private function getTables() {
66
- $strings = new Strings();
67
- $this->tables = array();
68
- foreach ( $this->options->tables as $table ) {
69
- $this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->db->prefix, null, $table );
70
- }
71
- }
72
-
73
- /**
74
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
75
- * @return void
76
- */
77
- protected function calculateTotalSteps() {
78
  $this->options->totalSteps = 17;
79
- }
80
-
81
- /**
82
- * Start Module
83
- * @return object
84
- */
85
- public function start() {
86
- // Execute steps
87
- $this->run();
88
-
89
- // Save option, progress
90
- $this->saveOptions();
91
-
92
- return ( object ) $this->response;
93
- }
94
-
95
- /**
96
- * Execute the Current Step
97
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
98
- * @return bool
99
- */
100
- protected function execute() {
101
- // Fatal error. Let this happen never and break here immediately
102
- if( $this->isRoot() ) {
103
- return false;
104
- }
105
-
106
- // Over limits threshold
107
- if( $this->isOverThreshold() ) {
108
- // Prepare response and save current progress
109
- $this->prepareResponse( false, false );
110
- $this->saveOptions();
111
- return false;
112
- }
113
-
114
- // No more steps, finished
115
- if( $this->isFinished() ) {
116
- $this->prepareResponse( true, false );
117
- return false;
118
- }
119
-
120
- // Execute step
121
- $stepMethodName = "step" . $this->options->currentStep;
122
- if( !$this->{$stepMethodName}() ) {
123
- $this->prepareResponse( false, false );
124
- return false;
125
- }
126
-
127
- // Prepare Response
128
- $this->prepareResponse();
129
-
130
- // Not finished
131
- return true;
132
- }
133
-
134
- /**
135
- * Checks Whether There is Any Job to Execute or Not
136
- * @return bool
137
- */
138
- protected function isFinished() {
139
- return (
140
- !isset($this->options->isRunning) ||
141
- $this->options->currentStep > $this->options->totalSteps ||
142
- !method_exists( $this, "step" . $this->options->currentStep )
143
- );
144
- }
145
-
146
- /**
147
- * Check if current operation is done on the root folder or on the live DB
148
- * @return boolean
149
- */
150
- protected function isRoot() {
151
-
152
- // Prefix is the same as the one of live site
153
- $wpdb = WPStaging::getInstance()->get( "wpdb" );
154
- if( $wpdb->prefix === $this->prefix ) {
155
- return true;
156
- }
157
-
158
- // CloneName is empty
159
- $name = ( array ) $this->options->cloneDirectoryName;
160
- if( empty( $name ) ) {
161
- return true;
162
- }
163
-
164
- // Live Path === Staging path
165
- if( $this->homeUrl . $this->options->cloneDirectoryName === $this->homeUrl ) {
166
- return true;
167
- }
168
-
169
- return false;
170
- }
171
-
172
- /**
173
- * Check if table exists
174
- * @param string $table
175
- * @return boolean
176
- */
177
- protected function isTable( $table ) {
178
- if( $this->db->get_var( "SHOW TABLES LIKE '{$table}'" ) != $table ) {
179
- $this->log( "Table {$table} does not exist", Logger::TYPE_ERROR );
180
- return false;
181
- }
182
- return true;
183
- }
184
-
185
- /**
186
  * Copy wp-config.php from the staging site if it is located outside of root one level up or
187
  * copy default wp-config.php if production site uses bedrock or any other boilerplate solution that stores wp default config data elsewhere.
188
- * @return boolean
189
- */
190
- protected function step0() {
191
  $this->log( "Preparing Data Step0: Copy wp-config.php file", Logger::TYPE_INFO );
192
 
193
- $dir = trailingslashit( dirname( ABSPATH ) );
194
 
195
- $source = $dir . 'wp-config.php';
196
 
197
  $destination = $this->options->destinationDir . 'wp-config.php';
198
 
199
 
200
- // Do not do anything
201
- if( (!is_file( $source ) && !is_link( $source )) || is_file( $destination ) ) {
202
- $this->log( "Preparing Data Step0: Skip it", Logger::TYPE_INFO );
203
- return true;
204
- }
 
 
 
 
 
 
 
 
 
 
205
 
206
- // Copy target of a symbolic link
207
- if( is_link( $source ) ) {
208
- $this->log( "Preparing Data Step0: Symbolic link found...", Logger::TYPE_INFO );
209
- if( !@copy( readlink( $source ), $destination ) ) {
210
  $errors = error_get_last();
211
  $this->log( "Preparing Data Step0: Failed to copy wp-config.php! Error: {$errors['message']} {$source} -> {$destination}", Logger::TYPE_ERROR );
212
  return true;
213
- }
214
- }
215
-
216
- // Copy file wp-config.php
217
- if( !@copy( $source, $destination ) ) {
218
- $errors = error_get_last();
219
- $this->log( "Preparing Data Step0: Failed to copy wp-config.php! Error: {$errors['message']} {$source} -> {$destination}", Logger::TYPE_ERROR );
220
- return true;
221
- }
222
- $this->log( "Preparing Data Step0: Successfull", Logger::TYPE_INFO );
223
- return true;
224
- }
225
-
226
- /**
227
- * Replace "siteurl" and "home"
228
- * @return bool
229
- */
230
- protected function step1() {
231
- $this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
232
-
233
- // Skip - Table does not exist
234
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
235
- return true;
236
- }
237
- // Skip - Table is not selected or updated
238
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
239
- $this->log( "Preparing Data Step1: Skipping" );
240
- return true;
241
- }
242
 
243
  $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . $this->getStagingSiteUrl() );
244
- // Replace URLs
245
- $result = $this->db->query(
246
- $this->db->prepare(
247
  "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->getStagingSiteUrl()
248
- )
249
- );
250
 
251
 
252
 
253
- // All good
254
- if( $result ) {
255
- return true;
256
- }
 
257
 
258
- $this->log( "Preparing Data Step1: Failed to update siteurl and homeurl in {$this->prefix}options. Probably already did! {$this->db->last_error}", Logger::TYPE_ERROR );
259
- return true;
260
- }
261
 
262
- /**
263
- * Update "wpstg_is_staging_site"
264
- * @return bool
265
- */
266
- protected function step2() {
267
 
268
- $this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
269
 
270
- // Skip - Table does not exist
271
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
272
- $this->log( "Preparing Data Step2: Skipping" );
273
- return true;
274
- }
275
- // Skip - Table is not selected or updated
276
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
277
- $this->log( "Preparing Data Step2: Skipping" );
278
- return true;
279
- }
280
 
281
  // $result = $this->db->query(
282
  // $this->db->prepare(
283
  // "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'", "true"
284
  // )
285
  // );
286
- $delete = $this->db->query(
287
- $this->db->prepare(
288
- "DELETE FROM `{$this->prefix}options` WHERE `option_name` = %s;", 'wpstg_is_staging_site'
289
- )
290
- );
291
-
292
-
293
- // No errors but no option name such as wpstg_is_staging_site
294
- //if( '' === $this->db->last_error && 0 == $result ) {
295
- $insert = $this->db->query(
296
- $this->db->prepare(
297
- "INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_is_staging_site',%s)", "true"
298
- )
299
- );
300
- //}
301
- // All good
302
- if( $insert ) {
303
- $this->log( "Preparing Data Step2: Successfull", Logger::TYPE_INFO );
304
- return true;
305
- }
306
-
307
- $this->log( "Preparing Data Step2: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
308
- return false;
309
- }
310
-
311
- /**
312
- * Update rewrite_rules
313
- * @return bool
314
- */
315
- protected function step3() {
316
-
317
- $this->log( "Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}" );
318
-
319
- // Skip - Table does not exist
320
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
321
- return true;
322
- }
323
-
324
- // Skip - Table is not selected or updated
325
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
326
- $this->log( "Preparing Data Step3: Skipping" );
327
- return true;
328
- }
329
-
330
- $result = $this->db->query(
331
- $this->db->prepare(
332
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", ' '
333
- )
334
- );
335
-
336
- // All good
337
- if( $result ) {
338
- return true;
339
- }
340
-
341
- $this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
342
- return true;
343
- }
344
-
345
- /**
346
- * Update Table Prefix in wp_usermeta and wp_options
347
- * @return bool
348
- */
349
- protected function step4() {
350
- $this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. " );
351
-
352
- // Skip - Table does not exist
353
- if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
354
- return true;
355
- }
356
-
357
- // Skip - Table is not selected or updated
358
- if( !in_array( $this->prefix . 'usermeta', $this->tables ) ) {
359
- $this->log( "Preparing Data Step4: Skipping" );
360
- return true;
361
- }
362
-
363
- $this->debugLog( "SQL: UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, {$this->db->prefix}, {$this->prefix}) WHERE meta_key LIKE {$this->db->prefix}_%" );
364
-
365
- $update = $this->db->query(
366
- $this->db->prepare(
367
- "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 . "_%"
368
- )
369
- );
370
-
371
- if( !$update ) {
372
- $this->log( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
373
- $this->returnException( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes {$this->db->last_error}" );
374
- return false;
375
- }
376
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
 
 
 
 
 
 
 
 
 
 
 
 
 
 
378
 
379
- return true;
380
- }
381
 
382
- /**
383
- * Update Table prefix in wp-config.php
384
- * @return bool
385
- */
386
- protected function step5() {
387
  $path = $this->options->destinationDir . "wp-config.php";
388
 
389
- $this->log( "Preparing Data Step5: Updating table_prefix in {$path} to " . $this->prefix );
390
- if( false === ($content = file_get_contents( $path )) ) {
391
- $this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
392
- return false;
393
- }
394
 
395
- // Replace table prefix
396
- $content = str_replace( '$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content );
397
 
398
- // Replace URLs
399
  $content = str_replace( $this->homeUrl, $this->getStagingSiteUrl(), $content );
400
 
401
- if( false === @file_put_contents( $path, $content ) ) {
402
- $this->log( "Preparing Data Step5: Failed to update $table_prefix in {$path} to " . $this->prefix . ". Can't save contents", Logger::TYPE_ERROR );
403
- return false;
404
- }
405
 
406
- return true;
407
- }
408
 
409
- /**
410
- * Reset index.php to original file
411
- * This is needed if live site is located in subfolder
412
- * Check first if main wordpress is used in subfolder and index.php in parent directory
413
- * @see: https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory
414
- * @return bool
415
- */
416
- protected function step6() {
417
 
418
- if( !$this->isSubDir() ) {
419
- $this->debugLog( "Preparing Data Step6: WP installation is not in a subdirectory! All good, skipping this step" );
420
- return true;
421
- }
422
 
423
  $path = $this->options->destinationDir . "index.php";
424
 
425
- if( false === ($content = file_get_contents( $path )) ) {
426
- $this->log( "Preparing Data Step6: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR );
427
- return false;
428
- }
429
 
430
 
431
- if( !preg_match( "/(require(.*)wp-blog-header.php' \);)/", $content, $matches ) ) {
432
- $this->log(
433
  "Preparing Data Step6: Failed to reset index.php for sub directory. Can not find line 'require(.*)wp-blog-header.php' in index.php", Logger::TYPE_ERROR
434
- );
435
- return false;
436
- }
437
- $this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
438
 
439
- $pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
440
 
441
- $replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
442
- $replace.= " // Changed by WP-Staging";
443
 
444
 
445
 
446
- if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
447
- $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
448
- return false;
449
- }
450
 
451
- if( false === @file_put_contents( $path, $content ) ) {
452
- $this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
453
- return false;
454
- }
455
  $this->Log( "Preparing Data Step 6: Finished successfully" );
456
- return true;
457
- }
458
-
459
- /**
460
- * Update wpstg_rmpermalinks_executed
461
- * @return bool
462
- */
463
- protected function step7() {
464
-
465
- $this->log( "Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}" );
466
-
467
- // Skip - Table does not exist
468
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
469
- return true;
470
- }
471
-
472
- // Skip - Table is not selected or updated
473
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
474
- $this->log( "Preparing Data Step7: Skipping" );
475
- return true;
476
- }
477
-
478
- $result = $this->db->query(
479
- $this->db->prepare(
480
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'", ' '
481
- )
482
- );
483
-
484
- // All good
485
- if( $result ) {
486
- $this->Log( "Preparing Data Step7: Finished successfully" );
487
- return true;
488
- }
489
-
490
- $this->log( "Failed to update wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_WARNING );
491
- return true;
492
- }
493
-
494
- /**
495
- * Update permalink_structure
496
- * @return bool
497
- */
498
- protected function step8() {
499
-
500
- $this->log( "Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}" );
501
-
502
- // Skip - Table does not exist
503
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
504
- return true;
505
- }
506
-
507
- // Skip - Table is not selected or updated
508
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
509
- $this->log( "Preparing Data Step8: Skipping" );
510
- return true;
511
- }
512
-
513
- $result = $this->db->query(
514
- $this->db->prepare(
515
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'", ' '
516
- )
517
- );
518
-
519
- // All good
520
- if( $result ) {
521
- $this->Log( "Preparing Data Step8: Finished successfully" );
522
- return true;
523
- }
524
-
525
- $this->log( "Failed to update permalink_structure in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
526
- return true;
527
- }
528
-
529
- /**
530
- * Update blog_public option to not allow staging site to be indexed by search engines
531
- * @return bool
532
- */
533
- protected function step9() {
534
-
535
- $this->log( "Preparing Data Step9: Set staging site to noindex" );
536
-
537
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
538
- return true;
539
- }
540
-
541
- // Skip - Table is not selected or updated
542
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
543
- $this->log( "Preparing Data Step9: Skipping" );
544
- return true;
545
- }
546
-
547
- $result = $this->db->query(
548
- $this->db->prepare(
549
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'blog_public'", '0'
550
- )
551
- );
552
-
553
- // All good
554
- if( $result ) {
555
- $this->Log( "Preparing Data Step9: Finished successfully" );
556
- return true;
557
- }
558
-
559
- $this->log( "Can not update staging site to noindex. Possible already done!", Logger::TYPE_WARNING );
560
- return true;
561
- }
562
-
563
- /**
564
- * Update WP_HOME in wp-config.php
565
- * @return bool
566
- */
567
- protected function step10() {
568
  $path = $this->options->destinationDir . "wp-config.php";
569
 
570
- $this->log( "Preparing Data Step10: Updating WP_HOME in wp-config.php to " . $this->getStagingSiteUrl() );
571
 
572
- if( false === ($content = file_get_contents( $path )) ) {
573
- $this->log( "Preparing Data Step10: Failed to update WP_HOME in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
574
- return false;
575
- }
576
 
577
 
578
- // Get WP_HOME from wp-config.php
579
  preg_match( "/define\s*\(\s*['\"]WP_HOME['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
580
 
581
- if( !empty( $matches[1] ) ) {
582
- $matches[1];
583
 
584
  $pattern = "/define\s*\(\s*['\"]WP_HOME['\"]\s*,\s*(.*)\s*\);/";
585
 
586
- $replace = "define('WP_HOME','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
587
- $replace.= " // Changed by WP-Staging";
 
 
 
 
 
 
 
 
588
 
589
- if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
590
- $this->log( "Preparing Data: Failed to update WP_HOME", Logger::TYPE_ERROR );
591
  return false;
592
- }
593
- } else {
594
- $this->log( "Preparing Data Step10: WP_HOME not defined in wp-config.php. Skipping this step." );
595
- }
596
-
597
- if( false === @file_put_contents( $path, $content ) ) {
598
- $this->log( "Preparing Data Step10: Failed to update WP_HOME. Can't save contents", Logger::TYPE_ERROR );
599
- return false;
600
- }
601
  $this->Log( "Preparing Data Step 10: Finished successfully" );
602
- return true;
603
- }
604
-
605
- /**
606
- * Update WP_SITEURL in wp-config.php
607
- * @return bool
608
- */
609
- protected function step11() {
610
  $path = $this->options->destinationDir . "wp-config.php";
611
 
612
- $this->log( "Preparing Data Step11: Updating WP_SITEURL in wp-config.php to " . $this->getStagingSiteUrl() );
613
 
614
- if( false === ($content = file_get_contents( $path )) ) {
615
- $this->log( "Preparing Data Step11: Failed to update WP_SITEURL in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
616
- return false;
617
- }
618
 
619
 
620
- // Get WP_SITEURL from wp-config.php
621
  preg_match( "/define\s*\(\s*['\"]WP_SITEURL['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
622
 
623
- if( !empty( $matches[1] ) ) {
624
- $matches[1];
625
 
626
  $pattern = "/define\s*\(\s*['\"]WP_SITEURL['\"]\s*,\s*(.*)\s*\);/";
627
 
628
- $replace = "define('WP_SITEURL','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
629
- $replace.= " // Changed by WP-Staging";
630
 
631
- if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
632
- $this->log( "Preparing Data Step11: Failed to update WP_SITEURL", Logger::TYPE_ERROR );
633
- return false;
634
- }
635
- } else {
636
- $this->log( "Preparing Data Step11: WP_SITEURL not defined in wp-config.php. Skipping this step." );
637
- }
638
 
639
 
640
- if( false === @file_put_contents( $path, $content ) ) {
641
- $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
642
- return false;
643
- }
644
  $this->Log( "Preparing Data Step 11: Finished successfully" );
645
- return true;
646
- }
647
 
648
- /**
649
  * Set true WP_DEBUG & WP_DEBUG_DISPLAY in wp-config.php
650
  * @return bool
651
  */
@@ -712,139 +709,139 @@ class Data extends JobExecutable {
712
  // }
713
 
714
  /**
715
- * Update Table Prefix in wp_options
716
- * @return bool
717
- */
718
- protected function step12() {
719
- $this->log( "Preparing Data Step12: Updating db prefix in {$this->prefix}options." );
720
 
721
- // Skip - Table does not exist
722
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
723
- return true;
724
- }
725
 
726
- // Skip - Table is not selected or updated
727
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
728
- $this->log( "Preparing Data Step12: Skipping" );
729
- return true;
730
- }
731
 
732
  $notice = !empty( $this->db->last_error ) ? 'Last error: ' . $this->db->last_error : '';
733
 
734
  //$this->log( "Updating option_name in {$this->prefix}options. {$notice}" );
735
- // Filter the rows below. Do not update them!
736
- $filters = array(
737
- 'wp_mail_smtp',
738
- 'wp_mail_smtp_version',
739
- 'wp_mail_smtp_debug',
740
- );
741
-
742
- $filters = apply_filters( 'wpstg_data_excl_rows', $filters );
743
-
744
- $where = "";
745
- foreach ( $filters as $filter ) {
746
- $where .= " AND option_name <> '" . $filter . "'";
747
- }
748
-
749
- $updateOptions = $this->db->query(
750
- $this->db->prepare(
751
- "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 . "_%"
752
- )
753
- );
754
-
755
- if( !$updateOptions ) {
756
  $this->log( "Preparing Data Step12: Failed to update option_name in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_ERROR );
757
  $this->log( "Preparing Data Step12: Query: UPDATE IGNORE {$this->prefix}options SET option_name= replace(option_name, {$this->db->prefix}, {$this->prefix}) WHERE option_name LIKE {$this->db->prefix} {$where}", Logger::TYPE_ERROR );
758
  $this->returnException( " Preparing Data Step12: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
759
  return false;
760
  }
761
  $this->Log( "Preparing Data Step 12: Finished successfully" );
762
- return true;
763
- }
764
 
765
- /**
766
- * Change upload_path in wp_options (if it is defined)
767
- * @return bool
768
- */
769
- protected function step13() {
770
- $this->log( "Preparing Data Step13: Updating upload_path {$this->prefix}options." );
771
 
772
- // Skip - Table does not exist
773
- if( false === $this->isTable( $this->prefix . 'options' ) ) {
774
- return true;
775
- }
776
 
777
- $newUploadPath = $this->getNewUploadPath();
778
 
779
- if( false === $newUploadPath ) {
780
  $this->log( "Preparing Data Step13: Skipped" );
781
- return true;
782
- }
783
 
784
- // Skip - Table is not selected or updated
785
- if( !in_array( $this->prefix . 'options', $this->tables ) ) {
786
  $this->log( "Preparing Data Step13: Skipped" );
787
- return true;
788
- }
789
 
790
- $error = isset( $this->db->last_error ) ? 'Last error: ' . $this->db->last_error : '';
791
 
792
- $this->log( "Updating upload_path in {$this->prefix}options. {$error}" );
793
 
794
- $updateOptions = $this->db->query(
795
- $this->db->prepare(
796
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'upload_path'", $newUploadPath
797
- )
798
- );
799
 
800
- if( !$updateOptions ) {
801
- $this->log( "Preparing Data Step13: Failed to update upload_path in {$this->prefix}options. {$error}", Logger::TYPE_ERROR );
802
- return true;
803
- }
804
  $this->Log( "Preparing Data Step 13: Finished successfully" );
805
- return true;
806
- }
807
-
808
- /**
809
- * Update WP_CACHE in wp-config.php
810
- * @return bool
811
- */
812
- protected function step14() {
813
  $path = $this->options->destinationDir . "wp-config.php";
814
 
815
- $this->log( "Preparing Data Step14: Set WP_CACHE in wp-config.php to false" );
816
 
817
- if( false === ($content = file_get_contents( $path )) ) {
818
- $this->log( "Preparing Data Step14: Failed to update WP_CACHE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
819
- return false;
820
- }
821
 
822
 
823
- // Get WP_CACHE from wp-config.php
824
  preg_match( "/define\s*\(\s*['\"]WP_CACHE['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
825
 
826
- if( !empty( $matches[1] ) ) {
827
 
828
  $pattern = "/define\s*\(\s*['\"]WP_CACHE['\"]\s*,\s*(.*)\s*\);/";
829
 
830
- $replace = "define('WP_CACHE',false); // " . $matches[1];
831
- $replace.= " // Changed by WP-Staging";
 
 
 
 
 
 
 
 
832
 
833
- if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
834
- $this->log( "Preparing Data: Failed to change WP_CACHE", Logger::TYPE_ERROR );
835
  return false;
836
- }
837
- } else {
838
- $this->log( "Preparing Data Step14: WP_CACHE not defined in wp-config.php. Skipping this step." );
839
- }
840
-
841
- if( false === @file_put_contents( $path, $content ) ) {
842
- $this->log( "Preparing Data Step14: Failed to update WP_CACHE. Can't save contents", Logger::TYPE_ERROR );
843
- return false;
844
- }
845
  $this->Log( "Preparing Data Step 14: Finished successfully" );
846
- return true;
847
- }
848
 
849
  /**
850
  * Remove WP_CONTENT_DIR in wp-config.php
@@ -926,52 +923,54 @@ class Data extends JobExecutable {
926
  return true;
927
  }
928
 
 
 
 
 
 
 
929
 
 
 
 
930
 
931
- protected function getNewUploadPath() {
932
- $uploadPath = get_option( 'upload_path' );
933
-
934
- if( !$uploadPath ) {
935
- return false;
936
- }
937
-
938
- $customSlug = str_replace( \WPStaging\WPStaging::getWPpath(), '', $uploadPath );
939
 
940
- $newUploadPath = $this->options->destinationDir . $customSlug;
941
 
942
- return $newUploadPath;
943
- }
944
 
945
- /**
946
- * Return URL to staging site
947
- * @return string
948
- */
949
- protected function getStagingSiteUrl() {
950
  if( !empty( $this->options->cloneHostname ) ) {
951
  return $this->options->cloneHostname;
952
  }
953
- if( $this->isSubDir() ) {
954
  return trailingslashit( $this->homeUrl ) . trailingslashit( $this->getSubDir() ) . $this->options->cloneDirectoryName;
955
- }
956
 
957
  return trailingslashit( $this->homeUrl ) . $this->options->cloneDirectoryName;
958
- }
959
 
960
- /**
961
- * Check if WP is installed in subdir
962
- * @return boolean
963
- */
964
- protected function isSubDir() {
965
  // Compare names without scheme to bypass cases where siteurl and home have different schemes http / https
966
  // This is happening much more often than you would expect
967
- $siteurl = preg_replace( '#^https?://#', '', rtrim( get_option( 'siteurl' ), '/' ) );
968
  $home = preg_replace( '#^https?://#', '', rtrim( get_option( 'home' ), '/' ) );
969
 
970
- if( $home !== $siteurl ) {
971
- return true;
972
- }
973
- return false;
974
- }
975
 
976
  /**
977
  * Get the install sub directory if WP is installed in sub directory
@@ -983,7 +982,7 @@ class Data extends JobExecutable {
983
 
984
  if( empty( $home ) || empty( $siteurl ) ) {
985
  return '';
986
- }
987
 
988
  $dir = str_replace( $home, '', $siteurl );
989
  return str_replace( '/', '', $dir );
4
 
5
  // No Direct Access
6
  if( !defined( "WPINC" ) ) {
7
+ die;
8
  }
9
 
10
  use WPStaging\Utils\Logger;
18
  */
19
  class Data extends JobExecutable {
20
 
21
+ /**
22
+ * @var \wpdb
23
+ */
24
+ private $db;
25
 
26
+ /**
27
+ * @var string
28
+ */
29
+ private $prefix;
30
 
31
+ /**
32
+ *
33
+ * @var string
34
+ */
35
+ private $homeUrl;
36
 
37
+ /**
38
+ * Tables e.g wpstg3_options
39
+ * @var array
40
+ */
41
+ private $tables;
42
 
43
+ /**
44
+ * Initialize
45
+ */
46
+ public function initialize() {
47
+ $this->db = WPStaging::getInstance()->get( "wpdb" );
48
 
49
+ $this->prefix = $this->options->prefix;
50
 
51
+ $this->getTables();
52
 
53
  $helper = new Helper();
54
+ $this->homeUrl = $helper->get_home_url();
55
+
56
+ // Fix current step
57
+ if( 0 == $this->options->currentStep ) {
58
+ $this->options->currentStep = 0;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Get a list of tables to copy
64
+ */
65
+ private function getTables() {
66
+ $strings = new Strings();
67
+ $this->tables = array();
68
+ foreach ( $this->options->tables as $table ) {
69
+ $this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->db->prefix, null, $table );
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
75
+ * @return void
76
+ */
77
+ protected function calculateTotalSteps() {
78
  $this->options->totalSteps = 17;
79
+ }
80
+
81
+ /**
82
+ * Start Module
83
+ * @return object
84
+ */
85
+ public function start() {
86
+ // Execute steps
87
+ $this->run();
88
+
89
+ // Save option, progress
90
+ $this->saveOptions();
91
+
92
+ return ( object ) $this->response;
93
+ }
94
+
95
+ /**
96
+ * Execute the Current Step
97
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
98
+ * @return bool
99
+ */
100
+ protected function execute() {
101
+ // Fatal error. Let this happen never and break here immediately
102
+ if( $this->isRoot() ) {
103
+ return false;
104
+ }
105
+
106
+ // Over limits threshold
107
+ if( $this->isOverThreshold() ) {
108
+ // Prepare response and save current progress
109
+ $this->prepareResponse( false, false );
110
+ $this->saveOptions();
111
+ return false;
112
+ }
113
+
114
+ // No more steps, finished
115
+ if( $this->isFinished() ) {
116
+ $this->prepareResponse( true, false );
117
+ return false;
118
+ }
119
+
120
+ // Execute step
121
+ $stepMethodName = "step" . $this->options->currentStep;
122
+ if( !$this->{$stepMethodName}() ) {
123
+ $this->prepareResponse( false, false );
124
+ return false;
125
+ }
126
+
127
+ // Prepare Response
128
+ $this->prepareResponse();
129
+
130
+ // Not finished
131
+ return true;
132
+ }
133
+
134
+ /**
135
+ * Checks Whether There is Any Job to Execute or Not
136
+ * @return bool
137
+ */
138
+ protected function isFinished() {
139
+ return (
140
+ !isset( $this->options->isRunning ) ||
141
+ $this->options->currentStep > $this->options->totalSteps ||
142
+ !method_exists( $this, "step" . $this->options->currentStep )
143
+ );
144
+ }
145
+
146
+ /**
147
+ * Check if current operation is done on the root folder or on the live DB
148
+ * @return boolean
149
+ */
150
+ protected function isRoot() {
151
+
152
+ // Prefix is the same as the one of live site
153
+ $wpdb = WPStaging::getInstance()->get( "wpdb" );
154
+ if( $wpdb->prefix === $this->prefix ) {
155
+ return true;
156
+ }
157
+
158
+ // CloneName is empty
159
+ $name = ( array ) $this->options->cloneDirectoryName;
160
+ if( empty( $name ) ) {
161
+ return true;
162
+ }
163
+
164
+ // Live Path === Staging path
165
+ if( $this->homeUrl . $this->options->cloneDirectoryName === $this->homeUrl ) {
166
+ return true;
167
+ }
168
+
169
+ return false;
170
+ }
171
+
172
+ /**
173
+ * Check if table exists
174
+ * @param string $table
175
+ * @return boolean
176
+ */
177
+ protected function isTable( $table ) {
178
+ if( $this->db->get_var( "SHOW TABLES LIKE '{$table}'" ) != $table ) {
179
+ $this->log( "Table {$table} does not exist", Logger::TYPE_ERROR );
180
+ return false;
181
+ }
182
+ return true;
183
+ }
184
+
185
+ /**
186
  * Copy wp-config.php from the staging site if it is located outside of root one level up or
187
  * copy default wp-config.php if production site uses bedrock or any other boilerplate solution that stores wp default config data elsewhere.
188
+ * @return boolean
189
+ */
190
+ protected function step0() {
191
  $this->log( "Preparing Data Step0: Copy wp-config.php file", Logger::TYPE_INFO );
192
 
193
+ $dir = trailingslashit( dirname( ABSPATH ) );
194
 
195
+ $source = $dir . 'wp-config.php';
196
 
197
  $destination = $this->options->destinationDir . 'wp-config.php';
198
 
199
 
200
+ // Do not do anything
201
+ if( (!is_file( $source ) && !is_link( $source )) || is_file( $destination ) ) {
202
+ $this->log( "Preparing Data Step0: Skip it", Logger::TYPE_INFO );
203
+ return true;
204
+ }
205
+
206
+ // Copy target of a symbolic link
207
+ if( is_link( $source ) ) {
208
+ $this->log( "Preparing Data Step0: Symbolic link found...", Logger::TYPE_INFO );
209
+ if( !@copy( readlink( $source ), $destination ) ) {
210
+ $errors = error_get_last();
211
+ $this->log( "Preparing Data Step0: Failed to copy wp-config.php! Error: {$errors['message']} {$source} -> {$destination}", Logger::TYPE_ERROR );
212
+ return true;
213
+ }
214
+ }
215
 
216
+ // Copy file wp-config.php
217
+ if( !@copy( $source, $destination ) ) {
 
 
218
  $errors = error_get_last();
219
  $this->log( "Preparing Data Step0: Failed to copy wp-config.php! Error: {$errors['message']} {$source} -> {$destination}", Logger::TYPE_ERROR );
220
  return true;
221
+ }
222
+ $this->log( "Preparing Data Step0: Successfull", Logger::TYPE_INFO );
223
+ return true;
224
+ }
225
+
226
+ /**
227
+ * Replace "siteurl" and "home"
228
+ * @return bool
229
+ */
230
+ protected function step1() {
231
+ $this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
232
+
233
+ // Skip - Table does not exist
234
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
235
+ return true;
236
+ }
237
+ // Skip - Table is not selected or updated
238
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
239
+ $this->log( "Preparing Data Step1: Skipping" );
240
+ return true;
241
+ }
 
 
 
 
 
 
 
 
242
 
243
  $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . $this->getStagingSiteUrl() );
244
+ // Replace URLs
245
+ $result = $this->db->query(
246
+ $this->db->prepare(
247
  "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->getStagingSiteUrl()
248
+ )
249
+ );
250
 
251
 
252
 
253
+ // All good
254
+ if( false === $result ) {
255
+ $this->log( "Preparing Data Step1: Failed to update siteurl and homeurl in {$this->prefix}options. Probably already did! {$this->db->last_error}", Logger::TYPE_WARNING );
256
+ return true;
257
+ }
258
 
259
+ return true;
260
+ }
 
261
 
262
+ /**
263
+ * Update "wpstg_is_staging_site"
264
+ * @return bool
265
+ */
266
+ protected function step2() {
267
 
268
+ $this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
269
 
270
+ // Skip - Table does not exist
271
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
272
+ $this->log( "Preparing Data Step2: Skipping" );
273
+ return true;
274
+ }
275
+ // Skip - Table is not selected or updated
276
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
277
+ $this->log( "Preparing Data Step2: Skipping" );
278
+ return true;
279
+ }
280
 
281
  // $result = $this->db->query(
282
  // $this->db->prepare(
283
  // "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'", "true"
284
  // )
285
  // );
286
+ $delete = $this->db->query(
287
+ $this->db->prepare(
288
+ "DELETE FROM `{$this->prefix}options` WHERE `option_name` = %s;", 'wpstg_is_staging_site'
289
+ )
290
+ );
291
+
292
+
293
+ // No errors but no option name such as wpstg_is_staging_site
294
+ //if( '' === $this->db->last_error && 0 == $result ) {
295
+ $insert = $this->db->query(
296
+ $this->db->prepare(
297
+ "INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_is_staging_site',%s)", "true"
298
+ )
299
+ );
300
+ //}
301
+ // All good
302
+ if( false === $insert ) {
303
+ $this->log( "Preparing Data Step2: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
304
+ return true;
305
+ }
306
+ $this->log( "Preparing Data Step2: Successfull", Logger::TYPE_INFO );
307
+ return true;
308
+ }
309
+
310
+ /**
311
+ * Update rewrite_rules
312
+ * @return bool
313
+ */
314
+ protected function step3() {
315
+
316
+ $this->log( "Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}" );
317
+
318
+ // Skip - Table does not exist
319
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
320
+ return true;
321
+ }
322
+
323
+ // Skip - Table is not selected or updated
324
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
325
+ $this->log( "Preparing Data Step3: Skipping" );
326
+ return true;
327
+ }
328
+
329
+ $result = $this->db->query(
330
+ $this->db->prepare(
331
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", ' '
332
+ )
333
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
 
335
+ // All good
336
+ if( false === $result ) {
337
+ $this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
338
+ return true;
339
+ }
340
+
341
+ return true;
342
+ }
343
+
344
+ /**
345
+ * Update Table Prefix in wp_usermeta and wp_options
346
+ * @return bool
347
+ */
348
+ protected function step4() {
349
+ $this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. " );
350
+
351
+ // Skip - Table does not exist
352
+ if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
353
+ return true;
354
+ }
355
+
356
+ // Skip - Table is not selected or updated
357
+ if( !in_array( $this->prefix . 'usermeta', $this->tables ) ) {
358
+ $this->log( "Preparing Data Step4: Skipping" );
359
+ return true;
360
+ }
361
 
362
+ $this->debugLog( "SQL: UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, {$this->db->prefix}, {$this->prefix}) WHERE meta_key LIKE {$this->db->prefix}_%" );
363
+
364
+ $update = $this->db->query(
365
+ $this->db->prepare(
366
+ "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 . "_%"
367
+ )
368
+ );
369
+
370
+ if( false === $update ) {
371
+ $this->log( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
372
+ $this->returnException( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes {$this->db->last_error}" );
373
+ return false;
374
+ }
375
 
376
+ return true;
377
+ }
378
 
379
+ /**
380
+ * Update Table prefix in wp-config.php
381
+ * @return bool
382
+ */
383
+ protected function step5() {
384
  $path = $this->options->destinationDir . "wp-config.php";
385
 
386
+ $this->log( "Preparing Data Step5: Updating table_prefix in {$path} to " . $this->prefix );
387
+ if( false === ($content = file_get_contents( $path )) ) {
388
+ $this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
389
+ return false;
390
+ }
391
 
392
+ // Replace table prefix
393
+ $content = str_replace( '$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content );
394
 
395
+ // Replace URLs
396
  $content = str_replace( $this->homeUrl, $this->getStagingSiteUrl(), $content );
397
 
398
+ if( false === @file_put_contents( $path, $content ) ) {
399
+ $this->log( "Preparing Data Step5: Failed to update $table_prefix in {$path} to " . $this->prefix . ". Can't save contents", Logger::TYPE_ERROR );
400
+ return false;
401
+ }
402
 
403
+ return true;
404
+ }
405
 
406
+ /**
407
+ * Reset index.php to original file
408
+ * This is needed if live site is located in subfolder
409
+ * Check first if main wordpress is used in subfolder and index.php in parent directory
410
+ * @see: https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory
411
+ * @return bool
412
+ */
413
+ protected function step6() {
414
 
415
+ if( !$this->isSubDir() ) {
416
+ $this->debugLog( "Preparing Data Step6: WP installation is not in a subdirectory! All good, skipping this step" );
417
+ return true;
418
+ }
419
 
420
  $path = $this->options->destinationDir . "index.php";
421
 
422
+ if( false === ($content = file_get_contents( $path )) ) {
423
+ $this->log( "Preparing Data Step6: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR );
424
+ return false;
425
+ }
426
 
427
 
428
+ if( !preg_match( "/(require(.*)wp-blog-header.php' \);)/", $content, $matches ) ) {
429
+ $this->log(
430
  "Preparing Data Step6: Failed to reset index.php for sub directory. Can not find line 'require(.*)wp-blog-header.php' in index.php", Logger::TYPE_ERROR
431
+ );
432
+ return false;
433
+ }
434
+ $this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
435
 
436
+ $pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
437
 
438
+ $replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
439
+ $replace.= " // Changed by WP-Staging";
440
 
441
 
442
 
443
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
444
+ $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
445
+ return false;
446
+ }
447
 
448
+ if( false === @file_put_contents( $path, $content ) ) {
449
+ $this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
450
+ return false;
451
+ }
452
  $this->Log( "Preparing Data Step 6: Finished successfully" );
453
+ return true;
454
+ }
455
+
456
+ /**
457
+ * Update wpstg_rmpermalinks_executed
458
+ * @return bool
459
+ */
460
+ protected function step7() {
461
+
462
+ $this->log( "Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}" );
463
+
464
+ // Skip - Table does not exist
465
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
466
+ return true;
467
+ }
468
+
469
+ // Skip - Table is not selected or updated
470
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
471
+ $this->log( "Preparing Data Step7: Skipping" );
472
+ return true;
473
+ }
474
+
475
+ $result = $this->db->query(
476
+ $this->db->prepare(
477
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'", ' '
478
+ )
479
+ );
480
+
481
+ // All good
482
+ if( false === $result ) {
483
+ $this->log( "Failed to update wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_WARNING );
484
+ return true;
485
+ }
486
+
487
+ $this->Log( "Preparing Data Step7: Finished successfully" );
488
+ return true;
489
+ }
490
+
491
+ /**
492
+ * Update permalink_structure
493
+ * @return bool
494
+ */
495
+ protected function step8() {
496
+
497
+ $this->log( "Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}" );
498
+
499
+ // Skip - Table does not exist
500
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
501
+ return true;
502
+ }
503
+
504
+ // Skip - Table is not selected or updated
505
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
506
+ $this->log( "Preparing Data Step8: Skipping" );
507
+ return true;
508
+ }
509
+
510
+ $result = $this->db->query(
511
+ $this->db->prepare(
512
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'", ' '
513
+ )
514
+ );
515
+
516
+ // All good
517
+ if( false === $result ) {
518
+ $this->log( "Failed to update permalink_structure in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
519
+ return true;
520
+ }
521
+
522
+ $this->Log( "Preparing Data Step8: Finished successfully" );
523
+ return true;
524
+ }
525
+
526
+ /**
527
+ * Update blog_public option to not allow staging site to be indexed by search engines
528
+ * @return bool
529
+ */
530
+ protected function step9() {
531
+
532
+ $this->log( "Preparing Data Step9: Set staging site to noindex" );
533
+
534
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
535
+ return true;
536
+ }
537
+
538
+ // Skip - Table is not selected or updated
539
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
540
+ $this->log( "Preparing Data Step9: Skipping" );
541
+ return true;
542
+ }
543
+
544
+ $result = $this->db->query(
545
+ $this->db->prepare(
546
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'blog_public'", '0'
547
+ )
548
+ );
549
+
550
+ // All good
551
+ if( false === $result ) {
552
+ $this->log( "Can not update staging site to noindex. Possible already done!", Logger::TYPE_WARNING );
553
+ return true;
554
+ }
555
+
556
+ $this->Log( "Preparing Data Step9: Finished successfully" );
557
+ return true;
558
+ }
559
+
560
+ /**
561
+ * Update WP_HOME in wp-config.php
562
+ * @return bool
563
+ */
564
+ protected function step10() {
565
  $path = $this->options->destinationDir . "wp-config.php";
566
 
567
+ $this->log( "Preparing Data Step10: Updating WP_HOME in wp-config.php to " . $this->getStagingSiteUrl() );
568
 
569
+ if( false === ($content = file_get_contents( $path )) ) {
570
+ $this->log( "Preparing Data Step10: Failed to update WP_HOME in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
571
+ return false;
572
+ }
573
 
574
 
575
+ // Get WP_HOME from wp-config.php
576
  preg_match( "/define\s*\(\s*['\"]WP_HOME['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
577
 
578
+ if( !empty( $matches[1] ) ) {
579
+ $matches[1];
580
 
581
  $pattern = "/define\s*\(\s*['\"]WP_HOME['\"]\s*,\s*(.*)\s*\);/";
582
 
583
+ $replace = "define('WP_HOME','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
584
+ $replace.= " // Changed by WP-Staging";
585
+
586
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
587
+ $this->log( "Preparing Data: Failed to update WP_HOME", Logger::TYPE_ERROR );
588
+ return false;
589
+ }
590
+ } else {
591
+ $this->log( "Preparing Data Step10: WP_HOME not defined in wp-config.php. Skipping this step." );
592
+ }
593
 
594
+ if( false === @file_put_contents( $path, $content ) ) {
595
+ $this->log( "Preparing Data Step10: Failed to update WP_HOME. Can't save contents", Logger::TYPE_ERROR );
596
  return false;
597
+ }
 
 
 
 
 
 
 
 
598
  $this->Log( "Preparing Data Step 10: Finished successfully" );
599
+ return true;
600
+ }
601
+
602
+ /**
603
+ * Update WP_SITEURL in wp-config.php
604
+ * @return bool
605
+ */
606
+ protected function step11() {
607
  $path = $this->options->destinationDir . "wp-config.php";
608
 
609
+ $this->log( "Preparing Data Step11: Updating WP_SITEURL in wp-config.php to " . $this->getStagingSiteUrl() );
610
 
611
+ if( false === ($content = file_get_contents( $path )) ) {
612
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
613
+ return false;
614
+ }
615
 
616
 
617
+ // Get WP_SITEURL from wp-config.php
618
  preg_match( "/define\s*\(\s*['\"]WP_SITEURL['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
619
 
620
+ if( !empty( $matches[1] ) ) {
621
+ $matches[1];
622
 
623
  $pattern = "/define\s*\(\s*['\"]WP_SITEURL['\"]\s*,\s*(.*)\s*\);/";
624
 
625
+ $replace = "define('WP_SITEURL','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
626
+ $replace.= " // Changed by WP-Staging";
627
 
628
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
629
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL", Logger::TYPE_ERROR );
630
+ return false;
631
+ }
632
+ } else {
633
+ $this->log( "Preparing Data Step11: WP_SITEURL not defined in wp-config.php. Skipping this step." );
634
+ }
635
 
636
 
637
+ if( false === @file_put_contents( $path, $content ) ) {
638
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
639
+ return false;
640
+ }
641
  $this->Log( "Preparing Data Step 11: Finished successfully" );
642
+ return true;
643
+ }
644
 
645
+ /**
646
  * Set true WP_DEBUG & WP_DEBUG_DISPLAY in wp-config.php
647
  * @return bool
648
  */
709
  // }
710
 
711
  /**
712
+ * Update Table Prefix in wp_options
713
+ * @return bool
714
+ */
715
+ protected function step12() {
716
+ $this->log( "Preparing Data Step12: Updating db prefix in {$this->prefix}options." );
717
 
718
+ // Skip - Table does not exist
719
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
720
+ return true;
721
+ }
722
 
723
+ // Skip - Table is not selected or updated
724
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
725
+ $this->log( "Preparing Data Step12: Skipping" );
726
+ return true;
727
+ }
728
 
729
  $notice = !empty( $this->db->last_error ) ? 'Last error: ' . $this->db->last_error : '';
730
 
731
  //$this->log( "Updating option_name in {$this->prefix}options. {$notice}" );
732
+ // Filter the rows below. Do not update them!
733
+ $filters = array(
734
+ 'wp_mail_smtp',
735
+ 'wp_mail_smtp_version',
736
+ 'wp_mail_smtp_debug',
737
+ );
738
+
739
+ $filters = apply_filters( 'wpstg_data_excl_rows', $filters );
740
+
741
+ $where = "";
742
+ foreach ( $filters as $filter ) {
743
+ $where .= " AND option_name <> '" . $filter . "'";
744
+ }
745
+
746
+ $updateOptions = $this->db->query(
747
+ $this->db->prepare(
748
+ "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 . "_%"
749
+ )
750
+ );
751
+
752
+ if( false === $updateOptions ) {
753
  $this->log( "Preparing Data Step12: Failed to update option_name in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_ERROR );
754
  $this->log( "Preparing Data Step12: Query: UPDATE IGNORE {$this->prefix}options SET option_name= replace(option_name, {$this->db->prefix}, {$this->prefix}) WHERE option_name LIKE {$this->db->prefix} {$where}", Logger::TYPE_ERROR );
755
  $this->returnException( " Preparing Data Step12: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
756
  return false;
757
  }
758
  $this->Log( "Preparing Data Step 12: Finished successfully" );
759
+ return true;
760
+ }
761
 
762
+ /**
763
+ * Change upload_path in wp_options (if it is defined)
764
+ * @return bool
765
+ */
766
+ protected function step13() {
767
+ $this->log( "Preparing Data Step13: Updating upload_path {$this->prefix}options." );
768
 
769
+ // Skip - Table does not exist
770
+ if( false === $this->isTable( $this->prefix . 'options' ) ) {
771
+ return true;
772
+ }
773
 
774
+ $newUploadPath = $this->getNewUploadPath();
775
 
776
+ if( false === $newUploadPath ) {
777
  $this->log( "Preparing Data Step13: Skipped" );
778
+ return true;
779
+ }
780
 
781
+ // Skip - Table is not selected or updated
782
+ if( !in_array( $this->prefix . 'options', $this->tables ) ) {
783
  $this->log( "Preparing Data Step13: Skipped" );
784
+ return true;
785
+ }
786
 
787
+ $error = isset( $this->db->last_error ) ? 'Last error: ' . $this->db->last_error : '';
788
 
789
+ $this->log( "Updating upload_path in {$this->prefix}options. {$error}" );
790
 
791
+ $updateOptions = $this->db->query(
792
+ $this->db->prepare(
793
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'upload_path'", $newUploadPath
794
+ )
795
+ );
796
 
797
+ if( false === $updateOptions ) {
798
+ $this->log( "Preparing Data Step13: Failed to update upload_path in {$this->prefix}options. {$error}", Logger::TYPE_ERROR );
799
+ return true;
800
+ }
801
  $this->Log( "Preparing Data Step 13: Finished successfully" );
802
+ return true;
803
+ }
804
+
805
+ /**
806
+ * Update WP_CACHE in wp-config.php
807
+ * @return bool
808
+ */
809
+ protected function step14() {
810
  $path = $this->options->destinationDir . "wp-config.php";
811
 
812
+ $this->log( "Preparing Data Step14: Set WP_CACHE in wp-config.php to false" );
813
 
814
+ if( false === ($content = file_get_contents( $path )) ) {
815
+ $this->log( "Preparing Data Step14: Failed to update WP_CACHE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
816
+ return false;
817
+ }
818
 
819
 
820
+ // Get WP_CACHE from wp-config.php
821
  preg_match( "/define\s*\(\s*['\"]WP_CACHE['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
822
 
823
+ if( !empty( $matches[1] ) ) {
824
 
825
  $pattern = "/define\s*\(\s*['\"]WP_CACHE['\"]\s*,\s*(.*)\s*\);/";
826
 
827
+ $replace = "define('WP_CACHE',false); // " . $matches[1];
828
+ $replace.= " // Changed by WP-Staging";
829
+
830
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
831
+ $this->log( "Preparing Data: Failed to change WP_CACHE", Logger::TYPE_ERROR );
832
+ return false;
833
+ }
834
+ } else {
835
+ $this->log( "Preparing Data Step14: WP_CACHE not defined in wp-config.php. Skipping this step." );
836
+ }
837
 
838
+ if( false === @file_put_contents( $path, $content ) ) {
839
+ $this->log( "Preparing Data Step14: Failed to update WP_CACHE. Can't save contents", Logger::TYPE_ERROR );
840
  return false;
841
+ }
 
 
 
 
 
 
 
 
842
  $this->Log( "Preparing Data Step 14: Finished successfully" );
843
+ return true;
844
+ }
845
 
846
  /**
847
  * Remove WP_CONTENT_DIR in wp-config.php
923
  return true;
924
  }
925
 
926
+ /**
927
+ * Get upload path
928
+ * @return boolean|string
929
+ */
930
+ protected function getNewUploadPath() {
931
+ $uploadPath = get_option( 'upload_path' );
932
 
933
+ if( !$uploadPath ) {
934
+ return false;
935
+ }
936
 
937
+ $customSlug = str_replace( \WPStaging\WPStaging::getWPpath(), '', $uploadPath );
 
 
 
 
 
 
 
938
 
939
+ $newUploadPath = $this->options->destinationDir . $customSlug;
940
 
941
+ return $newUploadPath;
942
+ }
943
 
944
+ /**
945
+ * Return URL to staging site
946
+ * @return string
947
+ */
948
+ protected function getStagingSiteUrl() {
949
  if( !empty( $this->options->cloneHostname ) ) {
950
  return $this->options->cloneHostname;
951
  }
952
+ if( $this->isSubDir() ) {
953
  return trailingslashit( $this->homeUrl ) . trailingslashit( $this->getSubDir() ) . $this->options->cloneDirectoryName;
954
+ }
955
 
956
  return trailingslashit( $this->homeUrl ) . $this->options->cloneDirectoryName;
957
+ }
958
 
959
+ /**
960
+ * Check if WP is installed in subdir
961
+ * @return boolean
962
+ */
963
+ protected function isSubDir() {
964
  // Compare names without scheme to bypass cases where siteurl and home have different schemes http / https
965
  // This is happening much more often than you would expect
966
+ $siteurl = preg_replace( '#^https?://#', '', rtrim( get_option( 'siteurl' ), '/' ) );
967
  $home = preg_replace( '#^https?://#', '', rtrim( get_option( 'home' ), '/' ) );
968
 
969
+ if( $home !== $siteurl ) {
970
+ return true;
971
+ }
972
+ return false;
973
+ }
974
 
975
  /**
976
  * Get the install sub directory if WP is installed in sub directory
982
 
983
  if( empty( $home ) || empty( $siteurl ) ) {
984
  return '';
985
+ }
986
 
987
  $dir = str_replace( $home, '', $siteurl );
988
  return str_replace( '/', '', $dir );
apps/Backend/Modules/Jobs/SearchReplace.php CHANGED
@@ -476,7 +476,8 @@ class SearchReplace extends JobExecutable {
476
  unset( $update_sql );
477
  unset( $where_sql );
478
  unset( $sql );
479
-
 
480
 
481
  // DB Flush
482
  $this->db->flush();
@@ -516,9 +517,10 @@ class SearchReplace extends JobExecutable {
516
  } elseif( is_object( $data ) ) {
517
 
518
  // Is no valid or is incomplete object
519
- if (!$this->isValidObject($data)){
520
- return $data;
521
- }
 
522
 
523
  $tmp = $data;
524
  $props = get_object_vars( $data );
476
  unset( $update_sql );
477
  unset( $where_sql );
478
  unset( $sql );
479
+ unset( $current_row );
480
+
481
 
482
  // DB Flush
483
  $this->db->flush();
517
  } elseif( is_object( $data ) ) {
518
 
519
  // Is no valid or is incomplete object
520
+ // Do not use it any longer because it increases the memory consumption and can lead to memory exhausted errors
521
+ // if (!$this->isValidObject($data)){
522
+ // return $data;
523
+ // }
524
 
525
  $tmp = $data;
526
  $props = get_object_vars( $data );
apps/Core/WPStaging.php CHANGED
@@ -29,7 +29,7 @@ final class WPStaging {
29
  /**
30
  * Plugin version
31
  */
32
- const VERSION = "2.5.6";
33
 
34
  /**
35
  * Plugin name
29
  /**
30
  * Plugin version
31
  */
32
+ const VERSION = "2.5.7";
33
 
34
  /**
35
  * Plugin name
readme.txt CHANGED
@@ -9,7 +9,7 @@ License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
  Tags: staging, duplication, cloning, clone, migration, sandbox, test site, testing, backup, post, admin, administration, duplicate posts
10
  Requires at least: 3.6+
11
  Tested up to: 5.1
12
- Stable tag: 2.5.6
13
  Requires PHP: 5.3
14
 
15
  A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators.
@@ -151,6 +151,10 @@ https://wp-staging.com
151
 
152
  == Changelog ==
153
 
 
 
 
 
154
  = 2.5.6 =
155
  * New: Compatible to WordPress 5.2
156
  * New: Allow adding file .wp-staging to root of website to determine if it's a staging or production website
@@ -213,25 +217,6 @@ https://wp-staging.com
213
  = 2.4.6 =
214
  * Fix: Can not login to staging site . Changed minimum user capability to 'manage_options' instead 'administrator'
215
 
216
- = 2.4.5 =
217
- * New: Compatible up to WordPress 5.0.1 Gutenberg
218
- * New: Show WP version of staging site in the sysinfo log
219
- * Fix: Make sure optimizer must-use plugin is updated as well after updating the main plugin
220
- * Fix: Plugin cannot be uninstalled if WP Staging Pro is activated
221
- * Fix: Prevent error $this not in object context in install.php
222
-
223
-
224
- = 2.4.4 =
225
- * Fix: Security, prevent downloading wp staging log files by third-party users from uploads folder
226
- * New: Compatible up to WordPress 5.0 Gutenberg
227
-
228
- = 2.4.3 =
229
- * Fix: Updating staging site does not exclude Windows IIS configuration file web.config and can lead to server error
230
- * Fix: Redirect to the correct URL after login to the staging site
231
- * Fix: Supports HTML entities in links created by WP Bakery Page Builder Plugin
232
- * New: Support for custom and non-default wp-content and uploads folder
233
- * New: Option to show again the rating notice in a week
234
-
235
 
236
  Complete changelog: [https://wp-staging.com/wp-staging-changelog](https://wp-staging.com/wp-staging-changelog)
237
 
9
  Tags: staging, duplication, cloning, clone, migration, sandbox, test site, testing, backup, post, admin, administration, duplicate posts
10
  Requires at least: 3.6+
11
  Tested up to: 5.1
12
+ Stable tag: 2.5.7
13
  Requires PHP: 5.3
14
 
15
  A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators.
151
 
152
  == Changelog ==
153
 
154
+ = 2.5.7 =
155
+ * Fix: Fatal Error: Remove previous implemented action to not search & replace over incomplete classes. This lead to high memory consumption and error 500 in step 2 of the cloning
156
+ * Fix: Cloning process interupts if there is not data to change in last step of cloning
157
+
158
  = 2.5.6 =
159
  * New: Compatible to WordPress 5.2
160
  * New: Allow adding file .wp-staging to root of website to determine if it's a staging or production website
217
  = 2.4.6 =
218
  * Fix: Can not login to staging site . Changed minimum user capability to 'manage_options' instead 'administrator'
219
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
 
221
  Complete changelog: [https://wp-staging.com/wp-staging-changelog](https://wp-staging.com/wp-staging-changelog)
222
 
wp-staging.php CHANGED
@@ -7,7 +7,7 @@
7
  * Author: WP-Staging
8
  * Author URI: https://wp-staging.com
9
  * Contributors: ReneHermi, ilgityildirim
10
- * Version: 2.5.6
11
  * Text Domain: wp-staging
12
  * Domain Path: /languages/
13
 
@@ -51,7 +51,7 @@ if( !defined( 'WPSTG_PLUGIN_URL' ) ) {
51
 
52
  // Version
53
  if( !defined( 'WPSTG_VERSION' ) ) {
54
- define( 'WPSTG_VERSION', '2.5.6' );
55
  }
56
 
57
  // Must use version of the optimizer
7
  * Author: WP-Staging
8
  * Author URI: https://wp-staging.com
9
  * Contributors: ReneHermi, ilgityildirim
10
+ * Version: 2.5.7
11
  * Text Domain: wp-staging
12
  * Domain Path: /languages/
13
 
51
 
52
  // Version
53
  if( !defined( 'WPSTG_VERSION' ) ) {
54
+ define( 'WPSTG_VERSION', '2.5.7' );
55
  }
56
 
57
  // Must use version of the optimizer