PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 2.1.7
Backup Migration v2.1.7
2.1.7 2.1.6 2.1.5.2 trunk 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.6.1 1.4.7 1.4.8 1.4.9 1.4.9.1 2.0.0 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.5.1
backup-backup / includes / extracter / extract.php
backup-backup / includes / extracter Last commit date
extract.php 2 weeks ago
extract.php
2567 lines
1 <?php
2
3 // Namespace
4 namespace BMI\Plugin\Extracter;
5
6 // Use
7 use BMI\Plugin\BMI_Logger as Logger;
8 use BMI\Plugin\Dashboard as Dashboard;
9 use BMI\Plugin\Database\BMI_Database as Database;
10 use BMI\Plugin\Database\BMI_Database_Importer as BetterDatabaseImport;
11 use BMI\Plugin\Database\BMI_Even_Better_Database_Restore as EvenBetterDatabaseImport;
12 use BMI\Plugin\Progress\BMI_ZipProgress as Progress;
13 use BMI\Plugin\Backup_Migration_Plugin as BMP;
14 use BMI\Plugin\Zipper\Zip as Zip;
15 use BMI\Plugin\Zipper\BMI_Zipper as ZipManager;
16 use BMI\Plugin\Database\BMI_Database_Sorting as SmartDatabaseSort;
17
18 // Exit on direct access
19 if (!defined('ABSPATH')) {
20 exit;
21 }
22
23 /**
24 * BMI_Extracter
25 */
26 class BMI_Extracter {
27
28 public function __construct($backup, &$migration, $tmptime = false, $isCLI = false, $options = []) {
29
30 // Globals
31 global $table_prefix;
32
33 // Requirements
34 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'manager.php';
35 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'better-restore.php';
36 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'smart-sort.php';
37
38 // IsCLI?
39 $this->isCLI = $isCLI;
40
41 // Backup name
42 $this->backup_name = $backup;
43
44 // Logger
45 $this->migration = $migration;
46
47 // Temp name
48 $this->tmptime = time();
49
50 // Use specified name if it is in batching mode
51 if (is_numeric($tmptime)) $this->tmptime = $tmptime;
52
53 // Splitting enabled?
54 $this->splitting = Dashboard\bmi_get_config('OTHER:RESTORE:SPLITTING') ? true : false;
55 $this->v3engine = Dashboard\bmi_get_config('OTHER:RESTORE:DB:V3') ? true : false;
56 $this->cleanupbefore = Dashboard\bmi_get_config('OTHER:RESTORE:BEFORE:CLEANUP') ? true : false;
57
58 // Restore start time
59 $this->start = intval(microtime(true));
60
61 // File amount by default 0 later we replace it with scan
62 $this->fileAmount = 0;
63 $this->recent_export_seek = 0;
64 $this->fileRestoreSeek = 0;
65 $this->fileRestoreCategory = 0;
66 $this->processData = [];
67 $this->conversionStats = [];
68
69 // Options
70 $this->batchStep = 0;
71 if (isset($options['amount'])) {
72 $this->fileAmount = intval($options['amount']);
73 }
74 if (isset($options['start'])) {
75 $this->start = intval($options['start']);
76 }
77 $this->continueFile = false;
78 if (isset($options['continueFile'])) {
79 $this->continueFile = $options['continueFile'];
80 }
81 $this->continueSeek = false;
82 if (isset($options['continueSeek'])) {
83 $this->continueSeek = $options['continueSeek'];
84 }
85 if (isset($options['step'])) {
86 $this->batchStep = intval($options['step']);
87 }
88 $this->databaseExist = false;
89 if (isset($options['databaseExist'])) {
90 $this->databaseExist = (($options['databaseExist'] == 'true' || $options['databaseExist'] === '1' || $options['databaseExist'] === 1 || $options['databaseExist'] === true) ? true : false);
91 }
92 $this->firstDB = true;
93 if (isset($options['firstDB'])) {
94 $this->firstDB = (($options['firstDB'] == 'true' || $options['firstDB'] === '1' || $options['firstDB'] === 1 || $options['firstDB'] === true) ? true : false);
95 }
96 $this->v3RestoreUsed = false;
97 if (isset($options['v3RestoreUsed'])) {
98 $this->v3RestoreUsed = (($options['v3RestoreUsed'] == 'true' || $options['v3RestoreUsed'] === '1' || $options['v3RestoreUsed'] === 1 || $options['v3RestoreUsed'] === true) ? true : false);
99 }
100 $this->firstExtract = true;
101 if (isset($options['firstExtract'])) {
102 $this->firstExtract = (($options['firstExtract'] == 'false' || $options['firstExtract'] === '1' || $options['firstExtract'] === 1 || $options['firstExtract'] === false) ? false : true);
103 }
104 $this->password = null;
105 if (isset($options['password'])) {
106 $this->password = $options['password'];
107 }
108
109 $this->db_xi = 0;
110 $this->ini_start = 0;
111 $this->table_names_alter = [];
112
113 if (isset($options['db_xi'])) {
114 $this->db_xi = ((is_numeric($options['db_xi'])) ? intval($options['db_xi']) : 0);
115 }
116 if (isset($options['ini_start'])) {
117 $this->ini_start = ((is_numeric($options['ini_start'])) ? intval($options['ini_start']) : microtime(true));
118 }
119 if (isset($options['table_names_alter'])) {
120 $this->table_names_alter = $options['table_names_alter'];
121 }
122 if (isset($options['recent_export_seek'])) {
123 $this->recent_export_seek = intval($options['recent_export_seek']);
124 }
125 if (isset($options['fileRestoreSeek'])) {
126 $this->fileRestoreSeek = intval($options['fileRestoreSeek']);
127 }
128 if (isset($options['fileRestoreCategory'])) {
129 $this->fileRestoreCategory = intval($options['fileRestoreCategory']);
130 }
131 $this->firstFileRestore = true;
132 if (isset($options['firstFileRestore'])) {
133 $this->firstFileRestore = (($options['firstFileRestore'] == 'false' || $options['firstFileRestore'] === false || $options['firstFileRestore'] === '0' || $options['firstFileRestore'] === 0) ? false : true);
134 }
135 if (isset($options['processData'])) {
136 $this->processData = $options['processData'];
137 }
138 if (isset($options['conversionStats'])) {
139 $this->conversionStats = $options['conversionStats'];
140 }
141
142 $this->tableIndex = 0;
143 $this->replaceStep = 0;
144 $this->totalReplacePage = 0;
145 $this->currentReplacePage = 0;
146 $this->fieldAdjustments = 0;
147 $this->dbFoundPrefix = 'wp_';
148
149 if (isset($options['replaceStep'])) {
150 $this->replaceStep = intval($options['replaceStep']);
151 }
152 if (isset($options['tableIndex'])) {
153 $this->tableIndex = intval($options['tableIndex']);
154 }
155 if (isset($options['currentReplacePage'])) {
156 $this->currentReplacePage = intval($options['currentReplacePage']);
157 }
158 if (isset($options['totalReplacePage'])) {
159 $this->totalReplacePage = intval($options['totalReplacePage']);
160 }
161 if (isset($options['fieldAdjustments'])) {
162 $this->fieldAdjustments = intval($options['fieldAdjustments']);
163 }
164 if (isset($options['dbFoundPrefix'])) {
165 $this->dbFoundPrefix = sanitize_text_field($options['dbFoundPrefix']);
166 }
167
168 // Name
169 // $this->tmp = untrailingslashit(ABSPATH) . DIRECTORY_SEPARATOR . 'backup-migration_' . $this->tmptime;
170 $this->tmp = BMI_TMP . DIRECTORY_SEPARATOR . 'backup-migration_' . $this->tmptime;
171 $GLOBALS['bmi_current_tmp_restore'] = $this->tmp;
172 $GLOBALS['bmi_current_tmp_restore_unique'] = $this->tmptime;
173
174 // Scan file
175 $this->scanFile = BMI_TMP . DIRECTORY_SEPARATOR . '.restore_scan_' . $this->tmptime;
176
177 // Prepare database connection
178 $this->db = new Database(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
179
180 // Save current wp-config to replace (only those required)
181 $this->DB_NAME = DB_NAME;
182 $this->DB_USER = DB_USER;
183 $this->DB_PASSWORD = DB_PASSWORD;
184 $this->DB_HOST = DB_HOST;
185 $this->DB_CHARSET = (defined('DB_CHARSET') ? DB_CHARSET : '');
186 $this->DB_COLLATE = (defined('DB_COLLATE') ? DB_COLLATE : '');
187
188 $this->AUTH_KEY = (defined('AUTH_KEY') ? AUTH_KEY : '');
189 $this->SECURE_AUTH_KEY = (defined('SECURE_AUTH_KEY') ? SECURE_AUTH_KEY : '');
190 $this->LOGGED_IN_KEY = (defined('LOGGED_IN_KEY') ? LOGGED_IN_KEY : '');
191 $this->NONCE_KEY = (defined('NONCE_KEY') ? NONCE_KEY : '');
192 $this->AUTH_SALT = (defined('AUTH_SALT') ? AUTH_SALT : '');
193 $this->SECURE_AUTH_SALT = (defined('SECURE_AUTH_SALT') ? SECURE_AUTH_SALT : '');
194 $this->LOGGED_IN_SALT = (defined('LOGGED_IN_SALT') ? LOGGED_IN_SALT : '');
195 $this->NONCE_SALT = (defined('NONCE_SALT') ? NONCE_SALT : '');
196
197 $this->ABSPATH = ABSPATH;
198 $this->WP_CONTENT_DIR = trailingslashit(WP_CONTENT_DIR);
199
200 $this->WP_DEBUG_LOG = WP_DEBUG_LOG;
201 $this->table_prefix = $table_prefix;
202 $this->code = get_option('z__bmi_xhria', false);
203 if (isset($options['code']) && $this->code == false) {
204 $this->code = $options['code'];
205 }
206
207 $this->backupStorage = get_option('BMI::STORAGE::LOCAL::PATH', false);
208 if (isset($options['storage'])) $this->backupStorage = $options['storage'];
209
210 $this->siteurl = get_option('siteurl');
211 $this->home = get_option('home');
212
213 $this->src = BMI_BACKUPS . DIRECTORY_SEPARATOR . $this->backup_name;
214
215 $this->v3Importer = null;
216 $this->usingDbEngineV4 = null;
217
218 $this->backupStorage = str_replace('/', DIRECTORY_SEPARATOR, $this->backupStorage);
219
220 }
221
222 public function removeUnwantedFiles() {
223 $preventMoveFiles = [
224 'wp-config.php',
225 'debug.log',
226 '.user.ini',
227 'php.ini',
228 '.bmi_staging',
229 '.htaccess'
230 ];
231
232 $base = $this->tmp . DIRECTORY_SEPARATOR . 'wordpress' . DIRECTORY_SEPARATOR;
233
234 foreach ($preventMoveFiles as $idx => $value) {
235 if (file_exists($base . $value)) @unlink($base . $value);
236 }
237 }
238
239 public function replacePath($path, $sub, $content) {
240 $path .= DIRECTORY_SEPARATOR . 'wordpress' . $sub;
241
242 // Handle only database backup
243 if (!file_exists($path)) return true;
244
245 $clent = strlen($content);
246 $sublen = strlen($path);
247
248 $preventMoveFiles = [
249 'wp-config.php',
250 'debug.log',
251 '.user.ini',
252 'php.ini',
253 '.htaccess'
254 ];
255 $catCounts = [0, 0, 0, 0, 0];
256 $catNames = ['WordPress core', 'Must-use plugins', 'Themes', 'Plugins', 'Other files'];
257
258
259 // First batch: scan, create directories, categorize files into per-category scan files
260 if ($this->firstFileRestore) {
261
262 $this->migration->log(__("Scanning and categorizing files for restoration...", 'backup-backup'), 'STEP');
263
264 // Category scan files:
265 // 0 = WordPress core, 1 = Must-use plugins, 2 = Themes, 3 = Plugins, 4 = Everything else
266 $catHandles = [];
267 for ($c = 0; $c < 5; $c++) {
268 $catHandles[$c] = fopen($this->getCategoryScanFile($c), 'w');
269 }
270
271 $ds = DIRECTORY_SEPARATOR;
272 $muPluginsPrefix = $ds . 'mu-plugins' . $ds;
273 $muPluginsExact = $ds . 'mu-plugins';
274 $themesPrefix = $ds . 'themes' . $ds;
275 $themesExact = $ds . 'themes';
276 $pluginsPrefix = $ds . 'plugins' . $ds;
277 $pluginsExact = $ds . 'plugins';
278
279 $rii = new \RecursiveIteratorIterator(
280 new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS),
281 \RecursiveIteratorIterator::SELF_FIRST
282 );
283
284 foreach ($rii as $fileEntry) {
285 if ($fileEntry->isDir()) {
286 $relPath = substr($fileEntry->getPathname(), $sublen);
287 if (strpos($relPath, $content) !== false) {
288 $dest = untrailingslashit($this->WP_CONTENT_DIR) . $sub . ltrim(substr($relPath, $clent), DIRECTORY_SEPARATOR);
289 } else {
290 $dest = untrailingslashit($this->ABSPATH) . $sub . ltrim($relPath, DIRECTORY_SEPARATOR);
291 }
292 $dest = untrailingslashit($dest);
293 if (!(file_exists($dest) && is_dir($dest))) {
294 try { @mkdir($dest, 0755, true); }
295 catch (\Exception $e) { /* Silence */ }
296 catch (\Throwable $t) { /* Silence */ }
297 }
298 } else {
299 $relPath = substr($fileEntry->getPathname(), $sublen);
300
301 // Categorize based on path
302 if (strpos($relPath, $content) === false) {
303 $cat = 0; // WordPress core (outside wp-content)
304 } else {
305 $afterContent = substr($relPath, strpos($relPath, $content) + strlen($content) - 1);
306 if (strpos($afterContent, $muPluginsPrefix) === 0 || $afterContent === $muPluginsExact) {
307 $cat = 1; // Must-use plugins
308 } elseif (strpos($afterContent, $themesPrefix) === 0 || $afterContent === $themesExact) {
309 $cat = 2; // Themes
310 } elseif (strpos($afterContent, $pluginsPrefix) === 0 || $afterContent === $pluginsExact) {
311 $cat = 3; // Plugins
312 } else {
313 $cat = 4; // Everything else (uploads, cache, languages, etc.)
314 }
315 }
316
317 fwrite($catHandles[$cat], $relPath . "\n");
318 $catCounts[$cat]++;
319 }
320 }
321
322 for ($c = 0; $c < 5; $c++) {
323 fclose($catHandles[$c]);
324 }
325
326 for ($c = 0; $c < 5; $c++) {
327 if ($catCounts[$c] > 0) {
328 $this->migration->log($catNames[$c] . ': ' . $catCounts[$c] . __(' files', 'backup-backup'), 'INFO');
329 }
330 }
331
332 $this->migration->log(__("File categorization complete.", 'backup-backup'), 'SUCCESS');
333 $this->firstFileRestore = false;
334
335 if (!$this->isCLI) {
336 $this->migration->progress(26);
337 return 'repeat';
338 }
339
340 }
341
342 // CLI mode: process all files at once (no batching needed)
343 if ($this->isCLI) {
344
345 $rii = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST);
346
347 $files = [];
348 foreach ($rii as $file) {
349 if (!$file->isDir()) {
350 $files[] = substr($file->getPathname(), $sublen);
351 }
352 }
353
354 $max = sizeof($files);
355 for ($i = 0; $i < $max; ++$i) {
356 $src = $path . $files[$i];
357 if (strpos($files[$i], $content) !== false) {
358 $dest = untrailingslashit($this->WP_CONTENT_DIR) . $sub . substr($files[$i], $clent);
359 } else {
360 $dest = untrailingslashit($this->ABSPATH) . $sub . $files[$i];
361 }
362
363 if (file_exists($src)) {
364 $fileDest = BMP::fixSlashes($dest);
365 $srcFileName = basename($src);
366
367 if (!in_array($srcFileName, $preventMoveFiles)) {
368 rename($src, $fileDest);
369 }
370 }
371
372 if ($i % 100 === 0 || ($i == ($max - 1))) {
373 $this->migration->progress(25 + intval((($i / $max) * 100) / 4));
374 if ($i != 0 && ($i % 500 === 0 || ($i == ($max - 1)))) {
375 if ($i == ($max - 1)) $i++;
376 $this->migration->log(sprintf(__('File replacement progress: %s/%s (%s%%)', 'backup-backup'), $i, $max, intval(($i / $max) * 100)));
377 }
378 }
379 }
380
381 // Cleanup category scan files created during the scan phase
382 for ($c = 0; $c < 5; $c++) {
383 $catFile = $this->getCategoryScanFile($c);
384 if (file_exists($catFile)) @unlink($catFile);
385 }
386
387 return true;
388
389 }
390
391 // Non-CLI: process categories sequentially
392 // Categories 0-3 (core, mu-plugins, themes, plugins) must each complete fully in a single request
393 // Category 4 (everything else) uses batched processing
394 $catNames = ['WordPress core', 'Must-use plugins', 'Themes', 'Plugins', 'Other files'];
395 $catProgressEnd = [31, 34, 37, 42, 50];
396
397 for ($cat = $this->fileRestoreCategory; $cat <= 4; $cat++) {
398 $catFile = $this->getCategoryScanFile($cat);
399
400 // Skip empty or missing categories
401 if (!file_exists($catFile) || filesize($catFile) === 0) {
402 if (file_exists($catFile)) @unlink($catFile);
403 continue;
404 }
405
406 if ($cat <= 3) {
407
408 // Categories 0-3: restore ALL files atomically via streaming (line-by-line)
409 $this->migration->log(__('Restoring: ', 'backup-backup') . $catNames[$cat], 'STEP');
410 $count = 0;
411 $fh = fopen($catFile, 'r');
412 if ($fh !== false) {
413 while (($line = fgets($fh)) !== false) {
414 $line = trim($line);
415 if ($line === '' || strlen($line) === 0) continue;
416
417 $src = $path . $line;
418 if (strpos($line, $content) !== false) {
419 $dest = untrailingslashit($this->WP_CONTENT_DIR) . $sub . substr($line, $clent);
420 } else {
421 $dest = untrailingslashit($this->ABSPATH) . $sub . $line;
422 }
423
424 if (file_exists($src)) {
425 $fileDest = BMP::fixSlashes($dest);
426 $srcFileName = basename($src);
427
428 if (!in_array($srcFileName, $preventMoveFiles)) {
429 rename($src, $fileDest);
430 }
431 }
432
433 $count++;
434 }
435 fclose($fh);
436 }
437
438 @unlink($catFile);
439 $this->migration->log(sprintf(__('%s restored: %d files', 'backup-backup'), $catNames[$cat], $count), 'SUCCESS');
440 $this->migration->progress($catProgressEnd[$cat]);
441 $this->fileRestoreCategory = $cat + 1;
442 return 'repeat';
443
444 }
445
446 // Category 4: batched processing for remaining files
447 if ($this->fileRestoreSeek == 0) {
448 $this->migration->log(__('Restoring: ', 'backup-backup') . $catNames[$cat], 'STEP');
449 }
450
451 $file = new \SplFileObject($catFile);
452 $file->seek($file->getSize());
453 $total_lines = $file->key() + 1;
454
455 $last_seek = $this->fileRestoreSeek;
456
457 // Determine batch size based on total file count
458 $batch = 500;
459 if ($total_lines > 36000) $batch = 1000;
460 if ($total_lines > 50000) $batch = 2500;
461 if ($total_lines > 100000) $batch = 5000;
462 if ($total_lines > 150000) $batch = 10000;
463 if ($total_lines > 200000) $batch = 20000;
464
465 if (defined('BMI_MAX_FILE_RESTORE_LIMIT')) {
466 $definedSize = BMI_MAX_FILE_RESTORE_LIMIT;
467 if (is_numeric($definedSize) && $definedSize > 50 && $definedSize < 20000) {
468 $batch = intval($definedSize);
469 }
470 }
471
472 if ($this->fileRestoreSeek == 0) {
473 $this->migration->log(__("Preparing batching technique for file restoration...", 'backup-backup'), 'INFO');
474 $this->migration->log(__('Files restored per batch: ', 'backup-backup') . $batch, 'INFO');
475 }
476
477 $shouldRepeat = false;
478 $seek_count = 0;
479 $recent_seek = $last_seek;
480
481 for ($i = $last_seek; $i < $total_lines; ++$i) {
482
483 $file->seek($i);
484 $line = trim($file->current());
485
486 if ($line && strlen($line) > 0) {
487
488 $src = $path . $line;
489 if (strpos($line, $content) !== false) {
490 $dest = untrailingslashit($this->WP_CONTENT_DIR) . $sub . substr($line, $clent);
491 } else {
492 $dest = untrailingslashit($this->ABSPATH) . $sub . $line;
493 }
494
495 if (file_exists($src)) {
496 $fileDest = BMP::fixSlashes($dest);
497 $srcFileName = basename($src);
498
499 if (!in_array($srcFileName, $preventMoveFiles)) {
500 rename($src, $fileDest);
501 }
502 }
503
504 }
505
506 $seek_count++;
507 $recent_seek = $i;
508 if ($seek_count > $batch) {
509
510 $shouldRepeat = true;
511 break;
512
513 }
514
515 }
516
517 // Progress reporting
518 $progressIndex = $recent_seek + 1;
519 $progressBase = $catProgressEnd[3]; // 42
520 $progressRange = $catProgressEnd[4] - $progressBase; // 50 - 42 = 8
521 $milestone = $progressBase + intval(($progressIndex / $total_lines) * $progressRange);
522 $this->migration->progress($milestone);
523
524 $plus = -1;
525 if ($shouldRepeat != true) $plus = 0;
526
527 $this->migration->log(sprintf(__('File replacement progress: %s/%s (%s%%)', 'backup-backup'), ($progressIndex + $plus), $total_lines, number_format(($progressIndex / $total_lines) * 100, 2)));
528
529 if ($shouldRepeat === true) {
530
531 $this->fileRestoreSeek = $recent_seek;
532 $this->fileRestoreCategory = 4;
533 return 'repeat';
534
535 } else {
536
537 // Cleanup scan file
538 @unlink($catFile);
539
540 $this->migration->log(__('All files replaced successfully.', 'backup-backup'), 'SUCCESS');
541 return true;
542
543 }
544
545 }
546
547 // All categories done (all scan files were empty/missing)
548 $this->migration->log(__('All files replaced successfully.', 'backup-backup'), 'SUCCESS');
549 return true;
550
551 }
552
553 private function getCategoryScanFile($category) {
554 return BMI_TMP . DIRECTORY_SEPARATOR . '.restore_cat_' . $this->tmptime . '_' . $category;
555 }
556
557 public function removePreviousSelectionsIfDatabaseIncluded() {
558 // DO NOTHING
559 }
560
561 public function replaceAll($content) {
562
563 $themedir = get_theme_root();
564 $tempTheme = $themedir . DIRECTORY_SEPARATOR . 'backup_migration_restoration_in_progress';
565 if (!(file_exists($tempTheme) && is_dir($tempTheme))) {
566 @mkdir($tempTheme, 0755, true);
567 }
568 if ($this->firstFileRestore) {
569
570 $visitLaterText = __('Site restoration in progress, please visit that website a bit later, thank you! :)', 'backup-backup');
571 file_put_contents($tempTheme . DIRECTORY_SEPARATOR . 'header.php', '<?php wp_head(); show_admin_bar(true);');
572 file_put_contents($tempTheme . DIRECTORY_SEPARATOR . 'footer.php', '<?php wp_footer(); get_footer();');
573 file_put_contents($tempTheme . DIRECTORY_SEPARATOR . 'index.php', '<?php get_header(); wp_body_open(); ?>' . $visitLaterText);
574 file_put_contents($tempTheme . DIRECTORY_SEPARATOR . '.previous_theme', get_option('template', ''));
575 file_put_contents($tempTheme . DIRECTORY_SEPARATOR . '.previous_stylesheet', get_option('stylesheet', ''));
576 file_put_contents($tempTheme . DIRECTORY_SEPARATOR . '.earlier_active_plugins', serialize(get_option('active_plugins')));
577
578 update_option('active_plugins', ['backup-backup/backup-backup.php']);
579 update_option('template', 'backup_migration_restoration_in_progress');
580 update_option('stylesheet', 'backup_migration_restoration_in_progress');
581
582 }
583 return $this->replacePath($this->tmp, DIRECTORY_SEPARATOR, $content);
584
585 }
586
587 public function cleanup() {
588
589 // Fix for automatic redirection module at TasteWP
590 if (strpos(site_url(), 'tastewp') !== false) {
591 if (function_exists('wp_load_alloptions')) wp_load_alloptions(true);
592 delete_option('__tastewp_redirection_performed', true);
593 delete_option('auto_smart_tastewp_redirect_performed', 1);
594 delete_option('tastewp_auto_activated', true);
595 delete_option('__tastewp_sub_requested', true);
596
597 if (function_exists('wp_load_alloptions')) wp_load_alloptions(true);
598 update_option('__tastewp_redirection_performed', true);
599 update_option('auto_smart_tastewp_redirect_performed', 1);
600 update_option('tastewp_auto_activated', true);
601 update_option('__tastewp_sub_requested', true);
602 }
603
604 delete_option('bmi_pro_cron_new_domain_done');
605
606 $options = [
607 'stylesheet',
608 'stylesheet_root',
609 'template',
610 'template_root'
611 ];
612 foreach ($options as $option) {
613 add_filter('pre_option_' . $option, [$this, 'filterUncachedOption'], 10, 2);
614 }
615
616 $filesToBeRemoved = [];
617 $dir = $this->tmp;
618
619 $themedir = get_theme_root();
620 $tempTheme = $themedir . DIRECTORY_SEPARATOR . 'backup_migration_restoration_in_progress';
621
622 $currentTemplate = get_option('template');
623 $currentStylesheet = get_option('stylesheet');
624
625 $templateExists = !empty($currentTemplate) && file_exists($themedir . DIRECTORY_SEPARATOR . $currentTemplate) && is_dir($themedir . DIRECTORY_SEPARATOR . $currentTemplate);
626 $stylesheetExists = !empty($currentStylesheet) && file_exists($themedir . DIRECTORY_SEPARATOR . $currentStylesheet) && is_dir($themedir . DIRECTORY_SEPARATOR . $currentStylesheet);
627
628 // If either the template or stylesheet is still the temporary restoration theme, or if either does not exist on disk,
629 // both template and stylesheet must be reverted together to maintain parent/child theme compatibility.
630 $shouldRevertThemeAndPlugins = ($currentTemplate === 'backup_migration_restoration_in_progress' ||
631 $currentStylesheet === 'backup_migration_restoration_in_progress' ||
632 !$templateExists ||
633 !$stylesheetExists);
634
635 if ($shouldRevertThemeAndPlugins) {
636 $prevTemplate = '';
637 $prevStylesheet = '';
638
639 if (file_exists($tempTheme . DIRECTORY_SEPARATOR . '.previous_theme')) {
640 $prevTemplate = trim(file_get_contents($tempTheme . DIRECTORY_SEPARATOR . '.previous_theme'));
641 }
642 if (file_exists($tempTheme . DIRECTORY_SEPARATOR . '.previous_stylesheet')) {
643 $prevStylesheet = trim(file_get_contents($tempTheme . DIRECTORY_SEPARATOR . '.previous_stylesheet'));
644 }
645
646 $prevTemplateExists = !empty($prevTemplate) && file_exists($themedir . DIRECTORY_SEPARATOR . $prevTemplate) && is_dir($themedir . DIRECTORY_SEPARATOR . $prevTemplate);
647 $prevStylesheetExists = !empty($prevStylesheet) && file_exists($themedir . DIRECTORY_SEPARATOR . $prevStylesheet) && is_dir($themedir . DIRECTORY_SEPARATOR . $prevStylesheet);
648
649 if ($prevTemplateExists && $prevStylesheetExists) {
650 update_option('template', $prevTemplate);
651 update_option('stylesheet', $prevStylesheet);
652 } elseif ($prevTemplateExists) {
653 update_option('template', $prevTemplate);
654 update_option('stylesheet', $prevTemplate);
655 } elseif (!($templateExists && $stylesheetExists && $currentTemplate !== 'backup_migration_restoration_in_progress' && $currentStylesheet !== 'backup_migration_restoration_in_progress')) {
656 if (function_exists('wp_get_themes')) {
657 $allThemes = wp_get_themes();
658 if (is_array($allThemes) && !empty($allThemes)) {
659 reset($allThemes);
660 $firstThemeSlug = key($allThemes);
661 if (!empty($firstThemeSlug) && file_exists($themedir . DIRECTORY_SEPARATOR . $firstThemeSlug) && is_dir($themedir . DIRECTORY_SEPARATOR . $firstThemeSlug)) {
662 update_option('template', $firstThemeSlug);
663 update_option('stylesheet', $firstThemeSlug);
664 }
665 }
666 }
667 }
668
669 if (file_exists($tempTheme . DIRECTORY_SEPARATOR . '.earlier_active_plugins')) {
670 $earlierPlugins = unserialize(file_get_contents($tempTheme . DIRECTORY_SEPARATOR . '.earlier_active_plugins'));
671 if (is_array($earlierPlugins)) {
672 $validPlugins = [];
673 foreach ($earlierPlugins as $plugin) {
674 if (is_string($plugin) && !empty($plugin) && file_exists(WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $plugin)) {
675 $validPlugins[] = $plugin;
676 }
677 }
678 if (!in_array('backup-backup/backup-backup.php', $validPlugins) && file_exists(WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'backup-backup/backup-backup.php')) {
679 $validPlugins[] = 'backup-backup/backup-backup.php';
680 }
681 update_option('active_plugins', array_values(array_unique($validPlugins)));
682 }
683 }
684 }
685
686 $filesToBeRemoved[] = $tempTheme;
687
688 if (is_dir($dir) && file_exists($dir)) {
689
690 $it = new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS);
691 $files = new \RecursiveIteratorIterator($it, \RecursiveIteratorIterator::CHILD_FIRST);
692
693 $this->migration->log(__('Removing ', 'backup-backup') . iterator_count($files) . __(' files', 'backup-backup'), 'INFO');
694 foreach ($files as $file) {
695 $pathReal = $file->getRealPath();
696 if (!file_exists($pathReal)) continue;
697 if ($file->isDir()) {
698 @rmdir($pathReal);
699 } else {
700 gc_collect_cycles();
701 @unlink($pathReal);
702 }
703 }
704
705 @rmdir($dir);
706
707 }
708
709 if (file_exists($this->scanFile)) {
710 @unlink($this->scanFile);
711 }
712
713 $sc = BMI_TMP . DIRECTORY_SEPARATOR . '.restore_secret';
714 if (file_exists($sc)) {
715 @unlink($sc);
716 }
717
718 $tblmap = BMI_TMP . DIRECTORY_SEPARATOR . '.table_map';
719 if (file_exists($tblmap)) {
720 @unlink($tblmap);
721 }
722 $allowedFiles = ['wp-config.php', '.htaccess', '.litespeed', '.default.json', 'driveKeys.php', 'dropboxKeys.php', '.autologin.php', '.migrationFinished', 'onedriveKeys.php', 'awsKeys.php', 'wasabiKeys.php', 'backupblissKeys.php', 'sftpKeys.php', 'pcloudKeys.php'];
723 foreach (glob(BMI_TMP . DIRECTORY_SEPARATOR . 'backup-migration_??????????') as $filename) {
724
725 $basename = basename($filename);
726
727 if (is_dir($filename) && !in_array($basename, ['.', '..'])) {
728 $filesToBeRemoved[] = $filename;
729 }
730
731 }
732
733 foreach (glob(BMI_TMP . DIRECTORY_SEPARATOR . '.*') as $filename) {
734
735 $basename = basename($filename);
736
737 if (in_array($basename, ['.', '..'])) continue;
738 if (is_file($filename) && !in_array($basename, $allowedFiles)) {
739 $filesToBeRemoved[] = $filename;
740 }
741
742 }
743
744 foreach (glob(BMI_TMP . DIRECTORY_SEPARATOR . 'restore_scan_*') as $filename) {
745
746 $basename = basename($filename);
747
748 if (in_array($basename, ['.', '..'])) continue;
749 if (is_file($filename) && !in_array($basename, $allowedFiles)) {
750 $filesToBeRemoved[] = $filename;
751 }
752
753 }
754
755 foreach (glob(untrailingslashit(ABSPATH) . DIRECTORY_SEPARATOR . 'wp-config.??????????.php') as $filename) {
756
757 $basename = basename($filename);
758
759 if (in_array($basename, ['.', '..'])) continue;
760 if (is_file($filename) && !in_array($filename, $allowedFiles)) {
761 $filesToBeRemoved[] = $filename;
762 }
763
764 }
765
766 if (is_array($filesToBeRemoved) || is_object($filesToBeRemoved)) {
767 foreach ((array) $filesToBeRemoved as $file) {
768 $this->rrmdir($file);
769 }
770 }
771
772 foreach ($options as $option) {
773 remove_filter('pre_option_' . $option, [$this, 'filterUncachedOption'], 10);
774 }
775
776 }
777
778 private function rrmdir($dir) {
779
780 if (is_dir($dir)) {
781
782 $objects = scandir($dir);
783 foreach ($objects as $object) {
784
785 if ($object != "." && $object != "..") {
786
787 if (is_dir($dir . DIRECTORY_SEPARATOR . $object) && !is_link($dir . DIRECTORY_SEPARATOR . $object)) {
788
789 $this->rrmdir($dir . DIRECTORY_SEPARATOR . $object);
790
791 } else {
792
793 @unlink($dir . DIRECTORY_SEPARATOR . $object);
794
795 }
796
797 }
798
799 }
800
801 @rmdir($dir);
802
803 } else {
804
805 if (file_exists($dir) && is_file($dir)) {
806
807 @unlink($dir);
808
809 }
810
811 }
812
813 }
814
815 public function fixDumbWindowsSlashes() {
816
817 // Extraction directory (no trailing slash)
818 $tmp = $this->tmp;
819
820 $files = scandir($tmp);
821 if (sizeof($files) > 10) {
822
823 $this->migration->log(__("Performing solution to Windows backslashes...", 'backup-backup'), 'STEP');
824
825 foreach ($files as $index => $file) {
826
827 if (strpos($file, '\\') !== false) {
828
829 $path = explode('\\', $file);
830 $filename = array_pop($path);
831 $dirname = $tmp . DIRECTORY_SEPARATOR . join(DIRECTORY_SEPARATOR, $path);
832
833 if (!(file_exists($dirname) && is_dir($dirname))) {
834 mkdir($dirname, 0755, true);
835 }
836
837 rename($tmp . DIRECTORY_SEPARATOR . $file, $dirname . DIRECTORY_SEPARATOR . $filename);
838
839 }
840
841 }
842
843 $this->migration->log(__("Windows file structure fixed...", 'backup-backup'), 'SUCCESS');
844
845 }
846
847 }
848
849 public function findTablePrefixByFiles($manifestPrefix) {
850
851 $tmp = $this->tmp . DIRECTORY_SEPARATOR . 'db_tables';
852 $manifestPrefixExist = false;
853
854 if (!(file_exists($tmp) && is_dir($tmp))) {
855 return $manifestPrefix;
856 }
857
858 $originalPrefix = [
859 file_exists($tmp . DIRECTORY_SEPARATOR . $manifestPrefix . 'options.sql'),
860 file_exists($tmp . DIRECTORY_SEPARATOR . $manifestPrefix . 'users.sql'),
861 file_exists($tmp . DIRECTORY_SEPARATOR . $manifestPrefix . 'usermeta.sql'),
862 file_exists($tmp . DIRECTORY_SEPARATOR . $manifestPrefix . 'posts.sql'),
863 file_exists($tmp . DIRECTORY_SEPARATOR . $manifestPrefix . 'postmeta.sql')
864 ];
865
866 $lowerPrefix = [
867 file_exists($tmp . DIRECTORY_SEPARATOR . strtolower($manifestPrefix) . 'options.sql'),
868 file_exists($tmp . DIRECTORY_SEPARATOR . strtolower($manifestPrefix) . 'users.sql'),
869 file_exists($tmp . DIRECTORY_SEPARATOR . strtolower($manifestPrefix) . 'usermeta.sql'),
870 file_exists($tmp . DIRECTORY_SEPARATOR . strtolower($manifestPrefix) . 'posts.sql'),
871 file_exists($tmp . DIRECTORY_SEPARATOR . strtolower($manifestPrefix) . 'postmeta.sql')
872 ];
873
874 if (count(array_filter($lowerPrefix)) == 5 || count(array_filter($originalPrefix)) == 5) {
875 return $manifestPrefix;
876 }
877
878 $files = scandir($tmp);
879 $prefixes = [];
880
881 foreach ($files as $index => $file) {
882
883 if ($file == '.' || $file == '..') continue;
884
885 if (substr($file, 0, strlen($manifestPrefix)) == $manifestPrefix) {
886 $manifestPrefixExist = true;
887 return $manifestPrefix;
888 }
889
890 foreach ($files as $index2 => $comparefile) {
891
892 if ($comparefile == $file) continue;
893 $currentTopPrefix = '';
894
895 for ($i = 0; $i < min(strlen($comparefile), strlen($file)); ++$i) {
896
897 if ($file[$i] == $comparefile[$i]) {
898 $currentTopPrefix .= $file[$i];
899 } else break;
900
901 }
902
903 if ($currentTopPrefix != '') {
904 if (isset($prefixes[$currentTopPrefix])) {
905 $prefixes[$currentTopPrefix]++;
906 } else {
907 $prefixes[$currentTopPrefix] = 1;
908 }
909 }
910
911 }
912
913 }
914
915 if (sizeof($prefixes) <= 0) return $manifestPrefix;
916 else return array_search(max($prefixes), $prefixes);
917
918 }
919
920 public function makeUnZIP() {
921
922 // Source
923 $src = $this->src;
924
925 // Extract
926 $this->zip = new Zip();
927
928 if ($this->isCLI) {
929
930 $isOk = $this->zip->unzip_file($src, $this->tmp, $this->migration, $this->password);
931
932 } else {
933
934 $last_seek = $this->recent_export_seek;
935
936 $file = new \SplFileObject($this->scanFile);
937 $file->seek($file->getSize());
938 $total_lines = $file->key() + 1;
939 $files = [];
940 $seek_begin = 0;
941 $recent_seek = $last_seek;
942 $shouldRepeat = false;
943
944 $batch = 50;
945 if ($total_lines > 1000) $batch = 100;
946 if ($total_lines > 2000) $batch = 200;
947 if ($total_lines > 6000) $batch = 300;
948 if ($total_lines > 12000) $batch = 500;
949 if ($total_lines > 36000) $batch = 1000;
950 if ($total_lines > 50000) $batch = 2500;
951 if ($total_lines > 100000) $batch = 5000;
952 if ($total_lines > 150000) $batch = 10000;
953 if ($total_lines > 200000) $batch = 20000;
954
955 if (defined('BMI_MAX_FILE_EXTRACTION_LIMIT')) {
956 $definedSize = BMI_MAX_FILE_EXTRACTION_LIMIT;
957 if (is_numeric($definedSize) && $definedSize > 50 && $definedSize < 20000) {
958 $batch = intval($definedSize);
959 }
960 }
961
962 if ($this->firstExtract == true) {
963 $this->migration->log(__("Preparing batching technique for extraction...", 'backup-backup'), 'STEP');
964 $this->migration->log(__('Files exported per batch: ', 'backup-backup') . $batch, 'INFO');
965 }
966
967 for ($i = $last_seek; $i < $total_lines; ++$i) {
968
969 $file->seek($i);
970 $line = trim($file->current());
971
972 if ($line && strlen($line) > 0) {
973
974 $files[] = $line;
975
976 }
977
978 $seek_begin++;
979 $recent_seek = $i;
980 if ($seek_begin > $batch) {
981
982 $shouldRepeat = true;
983 break;
984
985 }
986
987 }
988
989 $isOk = $this->zip->extract_files($src, $files, $this->tmp, $this->migration, $this->firstExtract, $this->password);
990
991 }
992
993
994 if (!$isOk) {
995
996 // Verbose
997 $this->migration->log(__('Failed to extract the files...', 'backup-backup'), 'WARN');
998 $this->cleanup();
999
1000 return false;
1001
1002 } else {
1003
1004 if (!$this->isCLI) {
1005
1006 $i = $recent_seek + 1;
1007 $milestone = intval((($i / $total_lines) * 100) / 4);
1008 $this->migration->progress($milestone);
1009
1010 $plus = -1;
1011 if ($shouldRepeat != true) $plus = 0;
1012
1013 $this->migration->log(__('Extraction milestone: ', 'backup-backup') . ($i + $plus) . '/' . $total_lines . ' (' . number_format(($i / $total_lines) * 100, 2) . '%)', 'INFO');
1014
1015 }
1016
1017 }
1018
1019 // Verbose
1020 if (!$this->isCLI && $shouldRepeat === true) {
1021
1022 $this->recent_export_seek = $recent_seek;
1023 return 'repeat';
1024
1025 } else {
1026
1027 $this->migration->log(__('Files extracted...', 'backup-backup'), 'SUCCESS');
1028 return true;
1029
1030 }
1031
1032 }
1033
1034 public function fixWPLogin(&$manifest) {
1035
1036 try {
1037
1038 global $wpdb;
1039
1040 $loginslug = false;
1041 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Identifier is safely escaped via escapeSQLIDentifier()
1042 $results = $wpdb->get_results("SELECT option_value FROM " . BMP::escapeSQLIDentifier($this->dbFoundPrefix . "options") . " WHERE option_name = 'bwpl_slug';");
1043
1044 if (sizeof($results) > 0) $loginslug = $results[0]->option_value;
1045
1046 if ($loginslug != false && is_string($loginslug) && strlen($loginslug) >= 1) {
1047
1048 $wploginfile = trailingslashit(ABSPATH) . 'wp-login.php';
1049 $blockedloginfile = trailingslashit(ABSPATH) . $loginslug . '-wp-login.php';
1050
1051 if (file_exists($wploginfile) && !file_exists($blockedloginfile)) {
1052 @copy($wploginfile, $blockedloginfile);
1053 }
1054
1055 }
1056
1057 }
1058 catch (\Exception $e) {}
1059 catch (\Throwable $e) {}
1060
1061 }
1062
1063 public function randomString($length = 64) {
1064
1065 $chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1066 $str = "";
1067
1068 for ($i = 0; $i < $length; ++$i) {
1069
1070 $str .= $chars[mt_rand(0, strlen($chars) - 1)];
1071
1072 }
1073
1074 return $str;
1075
1076 }
1077
1078 public function makeWPConfigCopy() {
1079
1080 $this->migration->log(__('Saving wp-config file...', 'backup-backup'), 'STEP');
1081 $configData = file_get_contents(ABSPATH . 'wp-config.php');
1082 if ($configData && strlen($configData) > 0) {
1083 file_put_contents(ABSPATH . 'wp-config.' . $this->tmptime . '.php', $configData);
1084 $this->migration->log(__('File wp-config saved', 'backup-backup'), 'SUCCESS');
1085 } else {
1086 $this->migration->log(__('Could not backup/read wp-config file.', 'backup-backup'), 'WARN');
1087 }
1088
1089 }
1090
1091 public function getCurrentManifest($first = false) {
1092
1093 if ($first == true) {
1094 $this->migration->log(__('Getting backup manifest...', 'backup-backup'), 'STEP');
1095 }
1096
1097 $manifest = json_decode(file_get_contents($this->tmp . DIRECTORY_SEPARATOR . 'bmi_backup_manifest.json'));
1098
1099 if ($first == true) {
1100 $this->migration->log(__('Manifest loaded', 'backup-backup'), 'SUCCESS');
1101 }
1102
1103 return $manifest;
1104
1105 }
1106
1107 public function restoreBackupFromFiles($manifest) {
1108
1109 $this->same_domain = untrailingslashit($manifest->dbdomain) == untrailingslashit($this->siteurl) ? true : false;
1110 if ($this->firstFileRestore) {
1111 $this->migration->log(__('Restoring files (this process may take a while)...', 'backup-backup'), 'STEP');
1112 }
1113 $contentDirectory = $this->WP_CONTENT_DIR;
1114 $pathtowp = DIRECTORY_SEPARATOR . 'wp-content';
1115 if (isset($manifest->config->WP_CONTENT_DIR) && isset($manifest->config->ABSPATH)) {
1116 $absi = $manifest->config->ABSPATH;
1117 $cotsi = $manifest->config->WP_CONTENT_DIR;
1118 if (strlen($absi) <= strlen($cotsi) && substr($cotsi, 0, strlen($absi)) == $absi) {
1119 $inside = true;
1120 $pathtowp = substr($cotsi, strlen($absi));
1121 } else {
1122 $inside = false;
1123 $pathtowp = $cotsi;
1124 }
1125 }
1126
1127 $result = $this->replaceAll($pathtowp);
1128 if ($result !== 'repeat') {
1129 $this->migration->log(__('All files restored successfully.', 'backup-backup'), 'SUCCESS');
1130 }
1131
1132 return $result;
1133
1134 }
1135
1136 public function restoreDatabaseV1(&$manifest) {
1137
1138 $this->migration->log(__('Older backup detected, using V1 engine to restore database...', 'backup-backup'), 'WARN');
1139 $this->migration->log(__('Database size: ' . BMP::humanSize(filesize($this->tmp . DIRECTORY_SEPARATOR . 'bmi_database_backup.sql')), 'backup-backup'), 'INFO');
1140 $old_domain = $manifest->dbdomain;
1141 $new_domain = $this->siteurl; // parse_url(home_url())['host'];
1142
1143 $abs = BMP::fixSlashes($manifest->config->ABSPATH);
1144 $newabs = BMP::fixSlashes(ABSPATH);
1145 $file = $this->tmp . DIRECTORY_SEPARATOR . 'bmi_database_backup.sql';
1146 $this->db->importDatabase($file, $old_domain, $new_domain, $abs, $newabs, $this->dbFoundPrefix, $this->siteurl, $this->home);
1147 $this->migration->log(__('Database restored', 'backup-backup'), 'SUCCESS');
1148
1149 }
1150
1151 public function setDBProgress($xi, $init_start, $table_names_alter) {
1152
1153 $this->db_xi = $xi;
1154 $this->ini_start = $init_start;
1155 $this->table_names_alter = $table_names_alter;
1156
1157 }
1158
1159 public function alter_tables(&$manifest) {
1160
1161 $storage = $this->tmp . DIRECTORY_SEPARATOR . 'db_tables';
1162
1163 $queriesAll = $manifest->total_queries;
1164 if (isset($this->conversionStats['total_queries'])) {
1165 $queriesAll = $this->conversionStats['total_queries'];
1166 }
1167
1168 // $manifest->total_queries # the other solution
1169 $importer = new BetterDatabaseImport($storage, $queriesAll, $manifest->config->ABSPATH, $manifest->dbdomain, $this->siteurl, $this->migration, $this->isCLI, $this->conversionStats);
1170
1171 $importer->xi = $this->db_xi;
1172 $importer->init_start = $this->ini_start;
1173 $importer->table_names_alter = $this->table_names_alter;
1174
1175 $importer->alter_names();
1176 $this->migration->log(__('Database restored', 'backup-backup'), 'SUCCESS');
1177
1178 }
1179
1180 public function search_replace_v3(&$manifest) {
1181
1182 $res = false;
1183 if (!$this->isCLI || $this->v3Importer == null) {
1184 $storage = $this->tmp . DIRECTORY_SEPARATOR . 'db_tables';
1185 $importer = new EvenBetterDatabaseImport($storage, false, $manifest, $this->migration, $this->splitting, $this->isCLI);
1186 $res = $importer->searchReplace($this->replaceStep, $this->tableIndex, $this->currentReplacePage, $this->totalReplacePage, $this->fieldAdjustments, $manifest->config->table_prefix);
1187 } else {
1188 $res = $this->v3Importer->searchReplace($this->replaceStep, $this->tableIndex, $this->currentReplacePage, $this->totalReplacePage, $this->fieldAdjustments, $manifest->config->table_prefix);
1189 }
1190
1191 if ($res && is_array($res) && $res['finished'] == true) {
1192 $this->migration->log(__('Database restored', 'backup-backup'), 'SUCCESS');
1193 }
1194
1195 return $res;
1196
1197 }
1198
1199 public function alter_tables_v3(&$manifest) {
1200
1201 if (!$this->isCLI || $this->v3Importer == null) {
1202 $storage = $this->tmp . DIRECTORY_SEPARATOR . 'db_tables';
1203 $importer = new EvenBetterDatabaseImport($storage, false, $manifest, $this->migration, $this->splitting, $this->isCLI);
1204 $importer->alter_tables();
1205
1206 // Modify the WP Config and replace
1207 $this->replaceDbPrefixInWPConfig($manifest);
1208
1209 $importer->enablePlugins();
1210 } else {
1211 $this->v3Importer->alter_tables();
1212
1213 // Modify the WP Config and replace
1214 $this->replaceDbPrefixInWPConfig($manifest);
1215
1216 $this->v3Importer->enablePlugins();
1217 }
1218
1219 $this->migration->log(__('Database restored', 'backup-backup'), 'SUCCESS');
1220
1221 }
1222
1223 public function restoreDatabaseV3(&$manifest) {
1224
1225 $storage = $this->tmp . DIRECTORY_SEPARATOR . 'db_tables';
1226 $this->v3Importer = new EvenBetterDatabaseImport($storage, $this->firstDB, $manifest, $this->migration, $this->splitting, $this->isCLI);
1227 $finished = $this->v3Importer->start();
1228
1229 if ($finished === true) {
1230
1231 return true;
1232
1233 } else {
1234
1235 return ['status' => 'new_file'];
1236
1237 }
1238
1239 }
1240
1241 public function restoreDatabaseV2(&$manifest) {
1242
1243 $storage = $this->tmp . DIRECTORY_SEPARATOR . 'db_tables';
1244
1245 if ($this->firstDB == true) {
1246 $this->migration->log(__('Successfully detected backup created with V2 engine, importing...', 'backup-backup'), 'INFO');
1247 $this->migration->log(__('Restoring database...', 'backup-backup'), 'STEP');
1248 }
1249
1250 $queriesAll = $manifest->total_queries;
1251 if (isset($this->conversionStats['total_queries'])) {
1252 $queriesAll = $this->conversionStats['total_queries'];
1253 }
1254 $importer = new BetterDatabaseImport($storage, $queriesAll, $manifest->config->ABSPATH, $manifest->dbdomain, $this->siteurl, $this->migration, $this->isCLI, $this->conversionStats);
1255
1256 if ($this->isCLI) {
1257
1258 $importer->showFirstLogs();
1259 $importer->import();
1260
1261 } else {
1262
1263 if ($this->firstDB == true) {
1264 $importer->showFirstLogs();
1265 }
1266
1267 $sqlFiles = $importer->get_sql_files($this->firstDB);
1268
1269 if ($this->firstDB != true) {
1270 $importer->xi = $this->db_xi;
1271 $importer->init_start = $this->ini_start;
1272 $importer->table_names_alter = $this->table_names_alter;
1273 }
1274
1275 if ($this->continueFile != false && $this->continueFile != '' && $this->continueSeek != false && $this->continueSeek != '') {
1276
1277 $import = $importer->restore_by_file($this->continueFile, $this->continueSeek);
1278 $importer->queries_ended();
1279 $this->continueFile = $this->continueFile;
1280 $this->setDBProgress($importer->xi, $importer->init_start, $importer->table_names_alter);
1281
1282 } else {
1283
1284 if (sizeof($sqlFiles) > 0) {
1285
1286 $import = $importer->restore_by_file($sqlFiles[0]);
1287 $importer->queries_ended();
1288 $this->continueFile = $sqlFiles[0];
1289 $this->setDBProgress($importer->xi, $importer->init_start, $importer->table_names_alter);
1290
1291 } else {
1292
1293 return true;
1294
1295 }
1296
1297 }
1298
1299 if ($import !== true) {
1300
1301 return ['status' => 'repeat', 'file' => $this->continueFile, 'seek' => $import];
1302
1303 } else {
1304
1305 return ['status' => 'new_file'];
1306
1307 }
1308
1309 }
1310
1311 $this->migration->log(__('Database restored', 'backup-backup'), 'SUCCESS');
1312
1313 }
1314
1315 public function restoreDatabaseDynamic(&$manifest) {
1316
1317 if ($this->firstDB == true) {
1318 $this->migration->log(__('Checking the database structure...', 'backup-backup'), 'STEP');
1319 }
1320
1321 if (is_dir($this->tmp . DIRECTORY_SEPARATOR . 'db_tables')) {
1322
1323 $forcev3Engine = false;
1324 if (isset($manifest->db_backup_engine) && $manifest->db_backup_engine === 'v4') {
1325 if ($this->v3engine == false) {
1326 $forcev3Engine = true;
1327
1328 if ($this->firstDB == true) {
1329 $this->migration->log(__('New search replace is disabled, nevertheless your backup does not support it, forcing to use new S&R engine.', 'backup-backup'), 'WARN');
1330 }
1331 }
1332 }
1333
1334 if ($this->v3engine || $forcev3Engine) {
1335
1336 if (!$this->isCLI) {
1337
1338 $this->v3RestoreUsed = true;
1339 $import = $this->restoreDatabaseV3($manifest);
1340 return $import;
1341
1342 } else {
1343
1344 $this->v3RestoreUsed = true;
1345 $this->restoreDatabaseV3($manifest);
1346
1347 }
1348
1349 } else {
1350
1351 if (!$this->isCLI) {
1352
1353 $import = $this->restoreDatabaseV2($manifest);
1354 return $import;
1355
1356 } else {
1357
1358 $this->restoreDatabaseV2($manifest);
1359
1360 }
1361
1362 }
1363
1364 } elseif (file_exists($this->tmp . DIRECTORY_SEPARATOR . 'bmi_database_backup.sql')) {
1365
1366 $this->restoreDatabaseV1($manifest);
1367
1368 } else {
1369
1370 $this->migration->log(__('This backup does not contain database copy, omitting...', 'backup-backup'), 'INFO');
1371 return false;
1372
1373 }
1374
1375 return true;
1376
1377 }
1378
1379 public function cleanupCurrentThemesAndPlugins() {
1380
1381 if ($this->cleanupbefore == true) {
1382
1383 $this->migration->log(__('Moving current themes and plugins.', 'backup-backup'), 'STEP');
1384
1385 $plugins_path = BMP::fixSlashes(WP_PLUGIN_DIR);
1386 $themes_path = BMP::fixSlashes(dirname(get_template_directory()));
1387
1388 $plugins = [];
1389 if (file_exists($plugins_path)) {
1390 $plugins = array_values(array_diff(scandir($plugins_path), ['..', '.', 'backup-backup', 'backup-backup-pro']));
1391 }
1392
1393 $themes = [];
1394 if (file_exists($themes_path)) {
1395 $themes = array_values(array_diff(scandir($themes_path), ['..', '.', 'backup-backup', 'backup-backup-pro']));
1396 }
1397
1398 $destination = BMI_TMP . DIRECTORY_SEPARATOR . 'clean-ups';
1399 $destination_unique = $destination . DIRECTORY_SEPARATOR . 'restoration_' . intval($this->start);
1400
1401 $destination_plugins = $destination_unique . DIRECTORY_SEPARATOR . 'plugins';
1402 $destination_themes = $destination_unique . DIRECTORY_SEPARATOR . 'themes';
1403
1404 if (!file_exists($destination)) @mkdir($destination, 0775, true);
1405 if (!file_exists($destination_unique)) @mkdir($destination_unique, 0775, true);
1406 if (!file_exists($destination_plugins)) @mkdir($destination_plugins, 0775, true);
1407 if (!file_exists($destination_themes)) @mkdir($destination_themes, 0775, true);
1408
1409 for ($i = 0; $i < sizeof($plugins); ++$i) {
1410 $pluginPath = trailingslashit($plugins_path) . $plugins[$i];
1411 $destPath = trailingslashit($destination_plugins) . $plugins[$i];
1412 rename($pluginPath, $destPath);
1413 }
1414
1415 for ($i = 0; $i < sizeof($themes); ++$i) {
1416 $themePath = trailingslashit($themes_path) . $themes[$i];
1417 $destPath = trailingslashit($destination_themes) . $themes[$i];
1418 rename($themePath, $destPath);
1419 }
1420
1421 $this->migration->log(__('Themes and plugins moved to safe directory.', 'backup-backup'), 'SUCCESS');
1422
1423 }
1424
1425 return true;
1426
1427 }
1428
1429 public function rescueCleanedThemesAndPlugins() {
1430
1431 if ($this->cleanupbefore == true) {
1432
1433 $this->migration->log(__('Restoring moved themes and plugins.', 'backup-backup'), 'INFO');
1434
1435 $plugins_path = BMP::fixSlashes(WP_PLUGIN_DIR);
1436 $themes_path = BMP::fixSlashes(dirname(get_template_directory()));
1437
1438 $destination = BMI_TMP . DIRECTORY_SEPARATOR . 'clean-ups';
1439 $destination_unique = $destination . DIRECTORY_SEPARATOR . 'restoration_' . intval($this->start);
1440
1441 $destination_plugins = $destination_unique . DIRECTORY_SEPARATOR . 'plugins';
1442 $destination_themes = $destination_unique . DIRECTORY_SEPARATOR . 'themes';
1443
1444 $plugins = [];
1445 if (file_exists($destination_plugins)) {
1446 $plugins = array_values(array_diff(scandir($destination_plugins), ['..', '.']));
1447 }
1448
1449 $themes = [];
1450 if (file_exists($destination_themes)) {
1451 $themes = array_values(array_diff(scandir($destination_themes), ['..', '.']));
1452 }
1453
1454 if (!file_exists($plugins_path)) @mkdir($plugins_path, 0775, true);
1455 if (!file_exists($themes_path)) @mkdir($themes_path, 0775, true);
1456
1457 for ($i = 0; $i < sizeof($plugins); ++$i) {
1458 $pluginPath = trailingslashit($destination_plugins) . $plugins[$i];
1459 $destPath = trailingslashit($plugins_path) . $plugins[$i];
1460 rename($pluginPath, $destPath);
1461 }
1462
1463 for ($i = 0; $i < sizeof($themes); ++$i) {
1464 $themePath = trailingslashit($destination_themes) . $themes[$i];
1465 $destPath = trailingslashit($themes_path) . $themes[$i];
1466 rename($themePath, $destPath);
1467 }
1468
1469 }
1470
1471 return true;
1472
1473 }
1474
1475 public function removeCleanedThemesAndPlugins() {
1476
1477 if (defined('BMI_KEEP_CLEANUPS') && BMI_KEEP_CLEANUPS == true) {
1478
1479 return true;
1480
1481 } else {
1482
1483 if ($this->cleanupbefore == true) {
1484
1485 $this->migration->log(__('Removing old plugins and themes moved before restoration.', 'backup-backup'), 'INFO');
1486
1487 $destination = BMI_TMP . DIRECTORY_SEPARATOR . 'clean-ups';
1488 $destination_unique = $destination . DIRECTORY_SEPARATOR . 'restoration_' . intval($this->start);
1489 $this->rrmdir($destination_unique);
1490
1491 }
1492
1493 }
1494
1495 }
1496
1497 public function replaceDbPrefixInWPConfig(&$manifest) {
1498
1499 $abs = untrailingslashit(ABSPATH);
1500 $curr_prefix = $this->table_prefix;
1501 $new_prefix = $this->dbFoundPrefix;
1502
1503 $this->migration->log('Detected table prefix: ' . $new_prefix, 'VERBOSE');
1504 $this->migration->log('Forwarded table prefix: ' . $curr_prefix, 'VERBOSE');
1505 $this->migration->log('Manifest table prefix: ' . $manifest->config->table_prefix, 'VERBOSE');
1506
1507 // if (strtolower($manifest->config->table_prefix) == strtolower($new_prefix)) {
1508 // $new_prefix = $manifest->config->table_prefix;
1509 // }
1510
1511 // if (strlen(trim($manifest->config->table_prefix)) == 0) {
1512 // return;
1513 // }
1514
1515 // if (strlen(trim($new_prefix)) == 0) {
1516 // return;
1517 // }
1518
1519 $new_prefix = $manifest->config->table_prefix;
1520
1521 $this->migration->log(__('Restoring wp-config file...', 'backup-backup'), 'STEP');
1522 $wpconfigDir = $abs . DIRECTORY_SEPARATOR . 'wp-config.' . $this->tmptime . '.php';
1523 if (file_exists($wpconfigDir) && is_readable($wpconfigDir) && is_writable($wpconfigDir)) {
1524
1525 // rename($abs . DIRECTORY_SEPARATOR . 'wp-config.' . $this->tmptime . '.php', $abs . DIRECTORY_SEPARATOR . 'wp-config.php');
1526 $wpconfig = file_get_contents($abs . DIRECTORY_SEPARATOR . 'wp-config.php');
1527 if (strpos($wpconfig, '"' . $curr_prefix . '";') !== false) {
1528 $wpconfig = str_replace('"' . $curr_prefix . '";', '"' . $new_prefix . '";', $wpconfig);
1529 } elseif (strpos($wpconfig, "'" . $curr_prefix . "';") !== false) {
1530 $wpconfig = str_replace("'" . $curr_prefix . "';", "'" . $new_prefix . "';", $wpconfig);
1531 }
1532
1533 file_put_contents($abs . DIRECTORY_SEPARATOR . 'wp-config.php', $wpconfig);
1534
1535 $this->migration->log(__('WP-Config restored', 'backup-backup'), 'SUCCESS');
1536
1537 } else {
1538
1539 $this->migration->log(__('Cannot write to WP-Config, if you need to change database prefix, please do it manually.', 'backup-backup'), 'WARN');
1540
1541 }
1542
1543 }
1544
1545 public function restoreOriginalWPConfig($remove = true) {
1546
1547 // $abs = untrailingslashit(ABSPATH);
1548 // $tmp_file_f = $abs . DIRECTORY_SEPARATOR . 'wp-config.' . $this->tmptime . '.php';
1549 // if (file_exists($tmp_file_f)) {
1550 // copy($tmp_file_f, $abs . DIRECTORY_SEPARATOR . 'wp-config.php');
1551 // if ($remove === true) @unlink($tmp_file_f);
1552 // }
1553 //
1554 // wp_load_alloptions(true);
1555
1556 }
1557
1558 public function makeNewLoginSession(&$manifest) {
1559 global $wpdb;
1560
1561 $prefix = sanitize_key($manifest->config->table_prefix);
1562 // Ensure correct prefix is used before anything
1563 $wpdb->set_prefix($manifest->config->table_prefix);
1564 wp_load_alloptions(true);
1565 $this->migration->log(__('Making new login session', 'backup-backup'), 'STEP');
1566
1567 $prefix = $wpdb->prefix;
1568 $cap_key = $prefix . 'capabilities';
1569 $uid = isset($manifest->uid) ? intval($manifest->uid) : 0;
1570
1571 // Check if provided UID is valid
1572 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Identifier is safely escaped via escapeSQLIDentifier()
1573 $is_valid_uid = $uid > 0 && $wpdb->get_var(
1574 $wpdb->prepare("SELECT ID FROM " . BMP::escapeSQLIDentifier($prefix . "users") . " WHERE ID = %d", $uid)
1575 );
1576
1577 // If no UID, cron mode, or invalid UID, find an administrator manually
1578 if (
1579 !$is_valid_uid ||
1580 $manifest->cron === true ||
1581 $manifest->cron === 'true'
1582 ) {
1583 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Identifier is safely escaped via escapeSQLIDentifier()
1584 $uid = $wpdb->get_var($wpdb->prepare("
1585 SELECT u.ID
1586 FROM " . BMP::escapeSQLIDentifier($prefix . "users") . " u
1587 INNER JOIN " . BMP::escapeSQLIDentifier($prefix . "usermeta") . " um ON u.ID = um.user_id
1588 WHERE um.meta_key = %s
1589 AND um.meta_value LIKE %s
1590 LIMIT 1
1591 ", $cap_key, '%administrator%'));
1592
1593 // Fallback to first user if no admin found
1594 if (!$uid) {
1595 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Identifier is safely escaped via escapeSQLIDentifier()
1596 $uid = $wpdb->get_var("SELECT ID FROM " . BMP::escapeSQLIDentifier($prefix . "users") . " LIMIT 1");
1597 }
1598
1599 $uid = intval($uid);
1600 }
1601
1602 // Get user login info from correct (possibly changed) users table
1603 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Identifier is safely escaped via escapeSQLIDentifier()
1604 $user = $wpdb->get_row(
1605 $wpdb->prepare("SELECT ID, user_login FROM " . BMP::escapeSQLIDentifier($prefix . "users") . " WHERE ID = %d", $uid)
1606 );
1607
1608 if ($user && isset($user->ID)) {
1609 remove_all_actions('wp_login', -1000);
1610
1611 clean_user_cache(get_current_user_id());
1612 clean_user_cache($user->ID);
1613
1614 wp_clear_auth_cookie();
1615 wp_set_current_user($user->ID, $user->user_login);
1616 wp_set_auth_cookie($user->ID, true, is_ssl());
1617
1618 // Manually trigger wp_login with minimal object
1619 $fake_user = (object) [
1620 'ID' => $user->ID,
1621 'user_login' => $user->user_login,
1622 ];
1623 do_action('wp_login', $user->user_login, $fake_user);
1624
1625 $manifest->uid = $user->ID;
1626 $this->migration->log(__('User should be logged in', 'backup-backup'), 'SUCCESS');
1627 } else {
1628 $this->migration->log(__('User login failed. Could not find user.', 'backup-backup'), 'ERROR');
1629 }
1630 }
1631
1632
1633 public function setOrUpdateXhria() {
1634
1635 // Update Original Local Storage Path
1636 if ($this->backupStorage && is_string($this->backupStorage)) {
1637 if (function_exists('wp_load_alloptions')) wp_load_alloptions(true);
1638 delete_option('BMI::STORAGE::LOCAL::PATH');
1639 if (function_exists('wp_load_alloptions')) wp_load_alloptions(true);
1640 update_option('BMI::STORAGE::LOCAL::PATH', $this->backupStorage);
1641 }
1642
1643 if ($this->code && is_string($this->code) && strlen($this->code) > 0) update_option('z__bmi_xhria', $this->code);
1644 else delete_option('z__bmi_xhria');
1645
1646 }
1647
1648 public function clearElementorCache( $force_manual = false ) {
1649 $cache_cleared_natively = false;
1650
1651 if ( !$force_manual && class_exists( '\Elementor\Plugin' ) ) {
1652 $elementor = \Elementor\Plugin::$instance;
1653
1654 if ( isset( $elementor->files_manager ) && method_exists( $elementor->files_manager, 'clear_cache' ) ) {
1655 try {
1656 $elementor->files_manager->clear_cache();
1657 $cache_cleared_natively = true;
1658 } catch ( \Exception $e ) {
1659 error_log( 'Elementor native cache clear failed: ' . $e->getMessage() );
1660 }
1661 }
1662 }
1663
1664 if ( !$cache_cleared_natively || $force_manual ) {
1665
1666 $upload_dir = wp_get_upload_dir();
1667 if ( empty( $upload_dir['error'] ) ) {
1668 $elementor_css_dir = trailingslashit( $upload_dir['basedir'] ) . 'elementor/css';
1669
1670 if ( is_dir( $elementor_css_dir ) ) {
1671 $files = new \RecursiveIteratorIterator(
1672 new \RecursiveDirectoryIterator( $elementor_css_dir, \RecursiveDirectoryIterator::SKIP_DOTS ),
1673 \RecursiveIteratorIterator::CHILD_FIRST
1674 );
1675
1676 foreach ( $files as $fileinfo ) {
1677 $action = ( $fileinfo->isDir() ? 'rmdir' : 'unlink' );
1678 @$action( $fileinfo->getRealPath() );
1679 }
1680 }
1681 }
1682
1683 delete_post_meta_by_key( '_elementor_css' );
1684 delete_option( '_elementor_global_css' );
1685 delete_option( 'elementor-custom-breakpoints-files' );
1686 $this->migration->log(__( 'Elementor CSS files deleted from: ', 'backup-backup' ) . $elementor_css_dir, 'INFO');
1687 }
1688
1689 }
1690
1691 public function finalCleanUP() {
1692
1693 $this->migration->log(__('Cleaning temporary files...', 'backup-backup'), 'STEP');
1694 $this->cleanup();
1695 $this->removeCleanedThemesAndPlugins();
1696 $this->migration->log(__('Temporary files cleaned', 'backup-backup'), 'SUCCESS');
1697
1698 }
1699
1700 public function handleError($e) {
1701
1702 // Restore moved themes and plugins
1703 $this->rescueCleanedThemesAndPlugins();
1704
1705 // On this tragedy at least remove tmp files
1706 $this->migration->log(__('Something bad happened...', 'backup-backup'), 'ERROR');
1707 if (method_exists($e, 'getMessage')) {
1708 $this->migration->log($e->getMessage(), 'ERROR');
1709 $this->migration->log($e->getLine() . ' @ ' . $e->getFile(), 'ERROR');
1710 }
1711 $this->cleanup();
1712
1713 }
1714
1715 public function makeTMPDirectory() {
1716
1717 // Make temp dir
1718 $this->migration->log(__('Making temporary directory', 'backup-backup'), 'INFO');
1719 if (!(is_dir($this->tmp) || file_exists($this->tmp))) {
1720 mkdir($this->tmp, 0755, true);
1721 }
1722
1723 // Deny read of this folder
1724 copy(BMI_INCLUDES . DIRECTORY_SEPARATOR . 'htaccess' . DIRECTORY_SEPARATOR . '.htaccess', $this->tmp . DIRECTORY_SEPARATOR . '.htaccess');
1725 touch($this->tmp . DIRECTORY_SEPARATOR . 'index.html');
1726 touch($this->tmp . DIRECTORY_SEPARATOR . 'index.php');
1727
1728 }
1729
1730 public function backupLocalOptions() {
1731 global $wpdb;
1732 $bmi_config_options = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options WHERE option_name LIKE '%bmi_%' OR option_name LIKE '%bmip_%' OR option_name LIKE '%bmi_pro_%'" );
1733 $tempConfigFile = BMI_TMP . DIRECTORY_SEPARATOR . 'bmi_local_options.json';
1734 $options = [];
1735 foreach ( $bmi_config_options as $option ) {
1736 $options[ $option->option_name ] = maybe_unserialize( $option->option_value );
1737 }
1738 $content = json_encode( $options );
1739 file_put_contents($tempConfigFile, $content);
1740
1741 }
1742
1743 public function restoreLocalPluginConfiguration() {
1744 $tempConfigFile = BMI_TMP . DIRECTORY_SEPARATOR . 'bmi_local_options.json';
1745
1746 if (!file_exists($tempConfigFile) || !is_readable($tempConfigFile)) {
1747 return;
1748 }
1749
1750 $json_data = file_get_contents($tempConfigFile);
1751 $options = json_decode($json_data, true);
1752 wp_cache_flush();
1753 wp_load_alloptions(true);
1754
1755 global $wpdb;
1756 $bmi_config_options = $wpdb->get_results( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE '%bmi_%' OR option_name LIKE '%bmip_%' OR option_name LIKE '%bmi_pro_%'" );
1757 foreach ( $bmi_config_options as $option ) {
1758 delete_option( $option->option_name );
1759 }
1760
1761 if (is_array($options)) {
1762 foreach ($options as $name => $value) {
1763 update_option($name, $value);
1764
1765 }
1766 }
1767
1768 wp_cache_flush();
1769 wp_load_alloptions(true);
1770
1771 unlink($tempConfigFile);
1772 }
1773
1774 private function makeRestoreSecret() {
1775
1776 $this->migration->log(__('Making new secret key for current restore process.', 'backup-backup'), 'STEP');
1777 $secret = $this->randomString();
1778 file_put_contents(BMI_TMP . DIRECTORY_SEPARATOR . '.restore_secret', $secret);
1779 $this->migration->log(__('Secret key generated, it will be returned to you (ping).', 'backup-backup'), 'SUCCESS');
1780
1781 return $secret;
1782
1783 }
1784
1785 public function listBackupContents() {
1786
1787 $manager = new ZipManager();
1788
1789 $save = $this->scanFile;
1790 $amount = $manager->getPartsToRestore($this->src, $save);
1791 if ($amount === false) {
1792 $amount = $manager->getZipContentList($this->src, $save);
1793 }
1794
1795 $this->migration->log(__('Scan found ', 'backup-backup') . $amount . __(' files inside the backup.', 'backup-backup'), 'INFO');
1796
1797 return $amount;
1798
1799 }
1800
1801 /**
1802 * Immediately flushes and rebuilds rewrite rules and physical server configuration files (.htaccess / web.config).
1803 * Bypasses local runtime option caching to ensure rules are generated from the newly restored database tables.
1804 *
1805 * @return void
1806 */
1807 public function flushImmediateRewriteRules() {
1808 $options = [
1809 'permalink_structure',
1810 'rewrite_rules',
1811 'page_on_front'
1812 ];
1813
1814 // Intercept pre_option filters to bypass runtime memory cache and query the database directly
1815 foreach ($options as $option) {
1816 add_filter('pre_option_' . $option, [$this, 'filterUncachedOption'], 10, 2);
1817 }
1818
1819 global $wp_rewrite;
1820 $wp_rewrite->init();
1821
1822 if (function_exists('save_mod_rewrite_rules')) {
1823 save_mod_rewrite_rules();
1824 }
1825 if (function_exists('iis7_save_url_rewrite_rules')) {
1826 iis7_save_url_rewrite_rules();
1827 }
1828
1829 foreach ($options as $option) {
1830 remove_filter('pre_option_' . $option, [$this, 'filterUncachedOption'], 10);
1831 }
1832 }
1833
1834 /**
1835 * Filter callback to fetch an option directly from the database table, bypassing wp_cache.
1836 *
1837 * @param mixed $pre_value The pre-filtered return value (default false).
1838 * @param string $option_name Name of the option being retrieved.
1839 * @return mixed Option value from database, or false if not found.
1840 */
1841 public function filterUncachedOption($pre_value, $option_name) {
1842 global $wpdb;
1843 $row = $wpdb->get_row(
1844 $wpdb->prepare("SELECT option_value FROM {$wpdb->options} WHERE option_name = %s LIMIT 1", $option_name)
1845 );
1846
1847 return is_object($row) ? $row->option_value : false;
1848 }
1849
1850
1851 public function extractTo($secret = null) {
1852
1853 try {
1854
1855 // Require Universal Zip Library
1856 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'zipper' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'zip.php';
1857
1858 // Make restore secret
1859 if ($this->batchStep == 0 && $this->isCLI) {
1860 $isProtected = ZipManager::isZipProtected($this->src);
1861 if ($isProtected) {
1862 if (defined('BMI_CLI_ARGUMENT_2') && BMI_CLI_ARGUMENT_2 && BMI_CLI_ARGUMENT_2 != false && BMI_CLI_ARGUMENT_2 !== 'false' && BMI_CLI_ARGUMENT_2 !== 'true') {
1863 if (ZipManager::validateZipPassword($this->src, BMI_CLI_ARGUMENT_2)) {
1864 $this->password = BMI_CLI_ARGUMENT_2;
1865 } else {
1866 throw new \Exception('Zip is password protected, but the provided password is incorrect.');
1867 }
1868 } else {
1869 throw new \Exception('Zip is password protected, please provide a password as the 3rd argument. Example: "php -f cli-handler.php ' . (defined('BMI_CLI_FUNCTION') ? BMI_CLI_FUNCTION : 'bmi_restore') . ' <backup_path> <password>"');
1870 }
1871 }
1872 }
1873 if (!$this->isCLI && $this->batchStep == 0) {
1874
1875 // Verbose
1876 Logger::log('Restoring site...');
1877
1878 if (ZipManager::isZipProtected($this->src) && $this->password == null) {
1879
1880 $this->migration->log(__('This backup is password protected, requesting password...', 'backup-backup'), 'STEP');
1881 BMP::res(['status' => 'password', 'tmp' => $this->tmptime, 'options' => [
1882 'code' => $this->code,
1883 'start' => $this->start,
1884 'step' => -1
1885 ]]);
1886 return;
1887 }
1888
1889 if ((gettype($secret) != 'string' || strlen($secret) != 64)) {
1890
1891 $secret = $this->makeRestoreSecret();
1892 BMP::res(['status' => 'secret', 'tmp' => $this->tmptime, 'secret' => $secret, 'options' => [
1893 'code' => $this->code,
1894 'start' => $this->start,
1895 'password' => $this->password,
1896 'step' => 0
1897 ]]);
1898 return;
1899
1900 } else {
1901
1902 // $this->migration->log(__('Secret key detected successfully (pong)!', 'backup-backup'), 'INFO');
1903
1904 }
1905
1906 }
1907
1908 // STEP: 1
1909 if ($this->isCLI || $this->batchStep == 1) {
1910
1911 if (!$this->isCLI) {
1912
1913 $this->migration->log(__('Secret key detected successfully (pong)!', 'backup-backup'), 'INFO');
1914
1915 }
1916
1917 // Make temporary directory
1918 $this->makeTMPDirectory();
1919
1920 // Migrate local options
1921 $this->backupLocalOptions();
1922
1923 // Time start
1924 $this->migration->log(__('Scanning archive...', 'backup-backup'), 'STEP');
1925
1926 if (!$this->isCLI) {
1927
1928 BMP::res(['status' => 'restore_ongoing', 'tmp' => $this->tmptime, 'secret' => $secret, 'options' => [
1929 'code' => $this->code,
1930 'start' => $this->start,
1931 'password' => $this->password,
1932 'step' => 1
1933 ]]);
1934
1935 return;
1936
1937 }
1938
1939 }
1940
1941 // STEP: 2
1942 if ($this->isCLI || $this->batchStep == 2) {
1943
1944 // Get ZIP contents for batch unzipping
1945 $this->fileAmount = $this->listBackupContents();
1946 $this->cleanupCurrentThemesAndPlugins();
1947
1948 if (!$this->isCLI) {
1949
1950 BMP::res(['status' => 'restore_ongoing', 'tmp' => $this->tmptime, 'secret' => $secret, 'options' => [
1951 'code' => $this->code,
1952 'start' => $this->start,
1953 'amount' => $this->fileAmount,
1954 'password' => $this->password,
1955 'step' => 2
1956 ]]);
1957
1958 return;
1959
1960 }
1961
1962 }
1963
1964 // STEP: 3
1965 if ($this->isCLI || $this->batchStep == 3) {
1966
1967 // UnZIP the backup
1968 try {
1969 $unzipped = $this->makeUnZIP();
1970 } catch (\Exception $e) {
1971 $this->handleError($e);
1972 return;
1973 } catch (\Throwable $t) {
1974 $this->handleError($t);
1975 return;
1976 }
1977
1978 if ($unzipped === false) {
1979
1980 $this->handleError(__('File extraction process failed.', 'backup-backup'));
1981 return;
1982
1983 }
1984
1985 if (!$this->isCLI) {
1986
1987 $shouldRepeat = false;
1988 if ($unzipped === 'repeat') {
1989
1990 $shouldRepeat = true;
1991
1992 } else {
1993
1994 $shouldRepeat = false;
1995
1996 }
1997
1998 BMP::res(['status' => 'restore_ongoing', 'tmp' => $this->tmptime, 'secret' => $secret, 'options' => [
1999 'code' => $this->code,
2000 'start' => $this->start,
2001 'amount' => $this->fileAmount,
2002 'recent_export_seek' => $this->recent_export_seek,
2003 'repeat_export' => $shouldRepeat,
2004 'firstExtract' => $this->firstExtract,
2005 'password' => $this->password,
2006 'step' => 3
2007 ]]);
2008
2009 return;
2010
2011 }
2012
2013 }
2014
2015 // STEP: 4
2016 if ($this->isCLI || $this->batchStep == 4) {
2017
2018 // Check if extracted files are not in backslashed Windows version, otherwise fix it
2019 $this->fixDumbWindowsSlashes();
2020 $this->removeUnwantedFiles();
2021
2022 // WP Config backup
2023 $this->makeWPConfigCopy();
2024
2025 if (!$this->isCLI) {
2026
2027 BMP::res(['status' => 'restore_ongoing', 'tmp' => $this->tmptime, 'secret' => $secret, 'options' => [
2028 'code' => $this->code,
2029 'start' => $this->start,
2030 'amount' => $this->fileAmount,
2031 'storage' => get_option('BMI::STORAGE::LOCAL::PATH', false),
2032 'password' => $this->password,
2033 'step' => 4
2034 ]]);
2035
2036 return;
2037
2038 }
2039
2040 }
2041
2042 // STEP: 5
2043 if ($this->isCLI || $this->batchStep == 5) {
2044
2045 // Get manifest
2046 $manifest = $this->getCurrentManifest($this->firstFileRestore);
2047
2048 if ($this->firstFileRestore){
2049 try {
2050 if (isset($manifest->version)) {
2051 $this->migration->log(__('Backup Migration version used for that backup: ', 'backup-backup') . $manifest->version, 'INFO');
2052 } else {
2053 $this->migration->log(__('Backup was made with unknown version of Backup Migration plugin.', 'backup-backup'), 'INFO');
2054 }
2055
2056 } catch (\Exception $e) {
2057
2058 $this->migration->log(__('Backup was made with unknown version of Backup Migration plugin.', 'backup-backup'), 'INFO');
2059
2060 } catch (\Throwable $e) {
2061
2062 $this->migration->log(__('Backup was made with unknown version of Backup Migration plugin.', 'backup-backup'), 'INFO');
2063
2064 }
2065 }
2066
2067
2068 // Even remove extracted WP-config if it's different site.
2069 if (untrailingslashit($manifest->dbdomain) != untrailingslashit($this->siteurl)) {
2070
2071 // Unlink wp-config inside extracted directory
2072 $extractedWpConfigPath = $this->tmp . DIRECTORY_SEPARATOR . 'wordpress' . DIRECTORY_SEPARATOR . 'wp-config.php';
2073 if (file_exists($extractedWpConfigPath)) @unlink($extractedWpConfigPath);
2074
2075 }
2076
2077 // Restore files
2078 $restoreResult = $this->restoreBackupFromFiles($manifest);
2079
2080
2081 if ($restoreResult !== 'repeat') {
2082 if (untrailingslashit($manifest->dbdomain) != untrailingslashit($this->siteurl)) {
2083
2084 // Restore WP Config if it's different domain
2085 $this->restoreOriginalWPConfig(false);
2086
2087 }
2088 }
2089
2090 if (!$this->isCLI) {
2091
2092 $shouldRepeatRestore = false;
2093 if ($restoreResult === 'repeat') {
2094
2095 $shouldRepeatRestore = true;
2096
2097 } else {
2098
2099 $shouldRepeatRestore = false;
2100
2101 }
2102
2103 BMP::res(['status' => 'restore_ongoing', 'tmp' => $this->tmptime, 'secret' => $secret, 'options' => [
2104 'code' => $this->code,
2105 'start' => $this->start,
2106 'amount' => $this->fileAmount,
2107 'storage' => $this->backupStorage,
2108 'fileRestoreSeek' => $this->fileRestoreSeek,
2109 'fileRestoreCategory' => $this->fileRestoreCategory,
2110 'repeat_restore' => $shouldRepeatRestore,
2111 'firstFileRestore' => $this->firstFileRestore,
2112 'password' => $this->password,
2113 'step' => 5
2114 ]]);
2115
2116 return;
2117
2118 }
2119
2120 }
2121
2122 // STEP: 6
2123 if ($this->isCLI || $this->batchStep == 6) {
2124
2125 // This literally does nothing.
2126
2127 if (!$this->isCLI) {
2128
2129 BMP::res(['status' => 'restore_ongoing', 'tmp' => $this->tmptime, 'secret' => $secret, 'options' => [
2130 'code' => $this->code,
2131 'start' => $this->start,
2132 'amount' => $this->fileAmount,
2133 'storage' => $this->backupStorage,
2134 'password' => $this->password,
2135 'step' => 6
2136 ]]);
2137
2138 return;
2139
2140 }
2141
2142 }
2143
2144 // STEP 7
2145 if ($this->isCLI || $this->batchStep == 7) {
2146
2147 // Get manifest
2148 if (!isset($manifest)) {
2149 $manifest = $this->getCurrentManifest();
2150 }
2151
2152 $this->migration->log(__('Validating table prefix...', 'backup-backup'), 'STEP');
2153 $dbPrefix = $this->findTablePrefixByFiles($manifest->config->table_prefix);
2154 $this->migration->log(__('Table prefix in manifest: ', 'backup-backup') . $manifest->config->table_prefix, 'INFO');
2155 $this->migration->log(__('Detected table prefix: ', 'backup-backup') . $dbPrefix, 'INFO');
2156
2157 $wasDisabled = 0;
2158 $dbFinishedConv = 'false';
2159 $newDataProcess = $this->processData;
2160
2161 $forcev3Engine = false;
2162 if (isset($manifest->db_backup_engine) && $manifest->db_backup_engine === 'v4') {
2163 if ($this->v3engine == false) {
2164 $forcev3Engine = true;
2165 }
2166 }
2167
2168 if ($this->v3engine || $forcev3Engine) {
2169
2170 if ($this->usingDbEngineV4) {
2171 $this->migration->log(__('Splitting process is disabled because v4 restore engine is enabled.', 'backup-backup'), 'INFO');
2172 } else {
2173 $this->migration->log(__('Splitting process is disabled because v3 restore engine is enabled.', 'backup-backup'), 'INFO');
2174 }
2175
2176 $wasDisabled = 1;
2177
2178 } else if (!$this->splitting) {
2179
2180 $this->migration->log(__('Splitting process is disabled in the settings, omitting.', 'backup-backup'), 'INFO');
2181 $wasDisabled = 1;
2182
2183 } else {
2184
2185 $db_tables = $this->tmp . DIRECTORY_SEPARATOR . 'db_tables';
2186
2187 if (is_dir($db_tables)) {
2188
2189 if (empty($this->processData)) {
2190 $this->migration->log(__('Converting database files into partial files.', 'backup-backup'), 'STEP');
2191 if (defined('BMI_DB_MAX_ROWS_PER_QUERY')) {
2192 $this->migration->log(__('Max rows per query (this site): ', 'backup-backup') . BMI_DB_MAX_ROWS_PER_QUERY, 'INFO');
2193 }
2194
2195 try {
2196
2197 if (isset($manifest->source_query_output)) {
2198 $this->migration->log(__('Max rows per query (source site): ', 'backup-backup') . $manifest->source_query_output, 'INFO');
2199 } else {
2200 $this->migration->log(__('Unknown query output value of backup file, maybe it was made before v1.1.7', 'backup-backup'), 'INFO');
2201 }
2202
2203 } catch (\Exception $e) {
2204
2205 $this->migration->log(__('Unknown query output value of backup file, maybe it was made before v1.1.7', 'backup-backup'), 'INFO');
2206
2207 } catch (\Throwable $e) {
2208
2209 $this->migration->log(__('Unknown query output value of backup file, maybe it was made before v1.1.7', 'backup-backup'), 'INFO');
2210
2211 }
2212
2213 }
2214
2215
2216 $dbsort = new SmartDatabaseSort($db_tables, $this->migration, $this->isCLI);
2217 $process = $dbsort->sortUnsorted($this->processData);
2218
2219 if (!is_null($process) && isset($process)) {
2220 $newDataProcess = $process;
2221 }
2222
2223 if ($this->isCLI || (isset($process['convertionFinished']) && $process['convertionFinished'] == 'yes')) {
2224 $this->migration->log(__('Database convertion finished successfully.', 'backup-backup'), 'SUCCESS');
2225
2226 $this->migration->log(__('Calculating new query size and counts.', 'backup-backup'), 'STEP');
2227 $stats = $dbsort->countAllFilesAndQueries();
2228 $this->migration->log(__('Calculaion completed, printing details.', 'backup-backup'), 'SUCCESS');
2229
2230 $this->migration->log(__('Total queries to insert after conversion: ', 'backup-backup') . $stats['total_queries'], 'INFO');
2231 $this->migration->log(__('Partial files count after conversion: ', 'backup-backup') . sizeof($stats['all_files']), 'INFO');
2232 $this->migration->log(__('Total size of the database: ', 'backup-backup') . BMP::humanSize($stats['total_size']), 'INFO');
2233 $this->migration->log(__('Table count to be imported: ', 'backup-backup') . sizeof($stats['all_tables']), 'INFO');
2234
2235 $total_qrs = $stats['total_queries'];
2236 $this->conversionStats = [];
2237 $this->conversionStats['total_queries'] = $total_qrs;
2238
2239 $dbFinishedConv = 'true';
2240 }
2241
2242 } else {
2243
2244 if (file_exists($this->tmp . DIRECTORY_SEPARATOR . 'bmi_database_backup.sql')) {
2245
2246 $this->migration->log(__('Ommiting database convert step as the database backup included was not made with V2 engine.', 'backup-backup'), 'WARN');
2247 $this->migration->log(__('The process may be less stable if the database is larger than usual.', 'backup-backup'), 'WARN');
2248 $dbFinishedConv = 'true';
2249
2250 } else {
2251
2252 $this->migration->log(__('Ommiting database convert step as there is no database backup included.', 'backup-backup'), 'INFO');
2253 $dbFinishedConv = 'true';
2254
2255 }
2256
2257 }
2258
2259 }
2260
2261 if (!$this->isCLI) {
2262
2263 BMP::res(['status' => 'restore_ongoing', 'tmp' => $this->tmptime, 'secret' => $secret, 'options' => [
2264 'code' => $this->code,
2265 'start' => $this->start,
2266 'amount' => $this->fileAmount,
2267 'dbConvertionFinished' => $dbFinishedConv,
2268 'processData' => $newDataProcess,
2269 'conversionStats' => $this->conversionStats,
2270 'storage' => $this->backupStorage,
2271 'dbFoundPrefix' => $dbPrefix,
2272 'password' => $this->password,
2273 'step' => 7 + $wasDisabled
2274 ]]);
2275
2276 return;
2277
2278 }
2279
2280 }
2281
2282 // STEP: 8
2283 if ($this->isCLI || $this->batchStep == 8) {
2284
2285 // Get manifest
2286 if (!isset($manifest)) {
2287 $manifest = $this->getCurrentManifest();
2288 }
2289
2290 if (isset($manifest->db_backup_engine) && $manifest->db_backup_engine === 'v4') {
2291 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'even-better-restore-v4.php';
2292 $this->usingDbEngineV4 = true;
2293 } else {
2294 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'even-better-restore-v3.php';
2295 $this->usingDbEngineV4 = false;
2296 }
2297
2298 // Try to restore database
2299 if (!$this->isCLI) {
2300
2301 $dbFinished = false;
2302 $database_exist = $this->restoreDatabaseDynamic($manifest);
2303
2304 if ($database_exist === false || $database_exist === true) {
2305
2306 $dbFinished = true;
2307
2308 } else {
2309
2310 if ($database_exist['status'] == 'new_file') {
2311
2312 $this->continueFile = false;
2313 $this->continueSeek = false;
2314
2315 } else {
2316
2317 $this->continueFile = $database_exist['file'];
2318 $this->continueSeek = $database_exist['seek'];
2319
2320 }
2321
2322 }
2323
2324 } else {
2325
2326 $database_exist = $this->restoreDatabaseDynamic($manifest);
2327
2328 }
2329
2330 $this->databaseExist = $database_exist;
2331
2332 if (!$this->isCLI) {
2333
2334 BMP::res(['status' => 'restore_ongoing', 'tmp' => $this->tmptime, 'secret' => $secret, 'options' => [
2335 'code' => $this->code,
2336 'start' => $this->start,
2337 'amount' => $this->fileAmount,
2338 'databaseExist' => $database_exist === true ? 'true' : 'false',
2339 'continueFile' => $this->continueFile,
2340 'continueSeek' => $this->continueSeek,
2341 'dbFinished' => $dbFinished,
2342 'firstDB' => $this->firstDB,
2343 'db_xi' => $this->db_xi,
2344 'ini_start' => $this->ini_start,
2345 'table_names_alter' => $this->table_names_alter,
2346 'conversionStats' => $this->conversionStats,
2347 'v3RestoreUsed' => $this->v3RestoreUsed,
2348 'dbFoundPrefix' => $this->dbFoundPrefix,
2349 'storage' => $this->backupStorage,
2350 'password' => $this->password,
2351 'step' => 8
2352 ]]);
2353
2354 return;
2355
2356 }
2357
2358 }
2359
2360 // STEP: 9
2361 if ($this->isCLI || $this->batchStep == 9) {
2362
2363 // Get manifest
2364 if (!isset($manifest)) {
2365 $manifest = $this->getCurrentManifest();
2366 }
2367
2368 if (isset($manifest->db_backup_engine) && $manifest->db_backup_engine === 'v4') {
2369 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'even-better-restore-v4.php';
2370 $this->usingDbEngineV4 = true;
2371 } else {
2372 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'even-better-restore-v3.php';
2373 $this->usingDbEngineV4 = false;
2374 }
2375
2376 $database_exist = $this->databaseExist;
2377 if ($database_exist === true || $database_exist === 'true') {
2378 $this->removePreviousSelectionsIfDatabaseIncluded();
2379 }
2380
2381 // Alter all tables
2382 $status = false;
2383 $tableIndex = $this->tableIndex;
2384 $replaceStep = $this->replaceStep;
2385 $replaceFinished = false;
2386 $currentReplacePage = 0;
2387 $totalReplacePage = 0;
2388 $fieldAdjustments = 0;
2389
2390 if ($this->isCLI) {
2391
2392 $srFinished = false;
2393 if ($database_exist && $this->v3RestoreUsed == true) {
2394 while (!$srFinished) {
2395
2396 $status = $this->search_replace_v3($manifest);
2397
2398 if ($status != false && is_array($status)) {
2399 $this->replaceStep = $status['step'];
2400 $this->tableIndex = $status['tableIndex'];
2401 $this->replaceFinished = $status['finished'];
2402 $this->currentReplacePage = $status['currentPage'];
2403 $this->totalReplacePage = $status['totalPages'];
2404 $this->fieldAdjustments = $status['fieldAdjustments'];
2405
2406 if ($this->replaceFinished == true) $srFinished = true;
2407 }
2408
2409 }
2410 } else {
2411 $this->replaceFinished = true;
2412 $this->migration->progress(98);
2413 }
2414
2415 } else {
2416
2417 if ($database_exist && $this->v3RestoreUsed == true) {
2418 $status = $this->search_replace_v3($manifest);
2419 } else {
2420 $this->replaceFinished = true;
2421 $this->migration->progress(98);
2422 }
2423
2424 if ($status != false && is_array($status)) {
2425 $this->replaceStep = $status['step'];
2426 $this->tableIndex = $status['tableIndex'];
2427 $this->replaceFinished = $status['finished'];
2428 $this->currentReplacePage = $status['currentPage'];
2429 $this->totalReplacePage = $status['totalPages'];
2430 $this->fieldAdjustments = $status['fieldAdjustments'];
2431 }
2432
2433 }
2434
2435 if (!$this->isCLI) {
2436
2437 BMP::res([
2438 'status' => 'restore_ongoing',
2439 'tmp' => $this->tmptime,
2440 'secret' => $secret,
2441 'options' => [
2442 'code' => $this->code,
2443 'start' => $this->start,
2444 'amount' => $this->fileAmount,
2445 'databaseExist' => $database_exist === true ? 'true' : 'false',
2446 'firstDB' => $this->firstDB,
2447 'db_xi' => $this->db_xi,
2448 'ini_start' => $this->ini_start,
2449 'table_names_alter' => $this->table_names_alter,
2450 'conversionStats' => $this->conversionStats,
2451 'v3RestoreUsed' => $this->v3RestoreUsed,
2452 'replaceStep' => $this->replaceStep,
2453 'tableIndex' => $this->tableIndex,
2454 'replaceFinished' => $this->replaceFinished,
2455 'currentReplacePage' => $this->currentReplacePage,
2456 'totalReplacePage' => $this->totalReplacePage,
2457 'fieldAdjustments' => $this->fieldAdjustments,
2458 'dbFoundPrefix' => $this->dbFoundPrefix,
2459 'storage' => $this->backupStorage,
2460 'password' => $this->password,
2461 'step' => 9
2462 ]
2463 ]);
2464
2465 return;
2466
2467 }
2468
2469 }
2470
2471 // STEP: 10
2472 if ($this->isCLI || $this->batchStep == 10) {
2473
2474 // Rename database from temporary to destination
2475 // And do the rest
2476 // Step 10 runs only at the end of database import
2477 // Get manifest
2478 if (!isset($manifest)) {
2479 $manifest = $this->getCurrentManifest();
2480 }
2481
2482 if (isset($manifest->db_backup_engine) && $manifest->db_backup_engine === 'v4') {
2483 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'even-better-restore-v4.php';
2484 $this->usingDbEngineV4 = true;
2485 } else {
2486 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'even-better-restore-v3.php';
2487 $this->usingDbEngineV4 = false;
2488 }
2489
2490 $database_exist = $this->databaseExist;
2491
2492 // Restore WP Config ** It allows to recover session after restore no matter what
2493 if ($database_exist == true || $database_exist == 'true') {
2494
2495 // Alter all tables
2496 if ($this->v3RestoreUsed == true) {
2497
2498 $this->alter_tables_v3($manifest);
2499
2500 } else {
2501
2502 $this->alter_tables($manifest);
2503
2504 // Modify the WP Config and replace
2505 $this->replaceDbPrefixInWPConfig($manifest);
2506
2507 }
2508
2509 // User is logged off at this point, try to log in
2510 $this->makeNewLoginSession($manifest);
2511
2512 } else {
2513
2514 // Restore WP Config without modifications
2515 $this->restoreOriginalWPConfig();
2516
2517 }
2518
2519 // Make sure the Xhria was not modified
2520 $this->setOrUpdateXhria();
2521
2522 // Fix elementor templates
2523 $this->clearElementorCache();
2524
2525 // Make final cleanup
2526 $this->finalCleanUP();
2527
2528 // Final flush of rewrite rules
2529 $this->flushImmediateRewriteRules();
2530
2531 // Remove backup migration temporary options
2532 $this->restoreLocalPluginConfiguration();
2533
2534 // Dedicated fix for block-wp-login plugin
2535 $this->fixWPLogin($manifest);
2536
2537 // Final verbose
2538 if ((intval(microtime(true)) - intval($this->start)) > 0) {
2539 $this->migration->log(__('Restore process took: ', 'backup-backup') . (intval(microtime(true)) - intval($this->start)) . ' seconds.', 'INFO');
2540 } else {
2541 $this->migration->log(__('Restore process fully finished.', 'INFO'));
2542 }
2543 Logger::log('Site restored...');
2544
2545 // Return success
2546 return true;
2547
2548 }
2549
2550 } catch (\Exception $e) {
2551
2552 // On this tragedy at least remove tmp files
2553 $this->handleError($e);
2554 return false;
2555
2556 } catch (\Throwable $e) {
2557
2558 // On this tragedy at least remove tmp files
2559 $this->handleError($e);
2560 return false;
2561
2562 }
2563
2564 }
2565
2566 }
2567