PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 2.1.2
Backup Migration v2.1.2
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 / database / even-better-restore-v4.php
backup-backup / includes / database Last commit date
export 4 months ago better-backup-v3.php 4 months ago better-backup.php 4 months ago better-restore.php 4 months ago even-better-restore-v3.php 4 months ago even-better-restore-v4.php 4 months ago interface-search-replace-repository.php 4 months ago manager.php 4 months ago search-replace-processor.php 4 months ago search-replace-repository.php 4 months ago search-replace-stack-based.php 4 months ago search-replace-v2.php 4 months ago search-replace.php 4 months ago smart-sort.php 4 months ago
even-better-restore-v4.php
973 lines
1 <?php
2
3 /**
4 * Author: Mikołaj `iClyde` Chodorowski
5 * Contact: kontakt@iclyde.pl
6 * Package: Backup Migration – WP Plugin
7 */
8
9 // Namespace
10 namespace BMI\Plugin\Database;
11
12 // Use
13 use BMI\Plugin\BMI_Logger AS Logger;
14 use BMI\Plugin\Backup_Migration_Plugin as BMP;
15 use BMI\Plugin\Progress\BMI_ZipProgress AS Progress;
16 use BMI\Plugin\Database\BMI_Search_Replace_Engine as BMISearchReplace;
17 use BMI\Plugin\Dashboard as Dashboard;
18
19 // Exit on direct access
20 if (!defined('ABSPATH')) exit;
21
22 /**
23 * Database Restore Enginge v4
24 */
25 class BMI_Even_Better_Database_Restore {
26
27 // Unwanted tables
28 private $unwantedTables = [
29 'wfblockediplog',
30 'wfblocks7',
31 'wfcrawlers',
32 'wffilechanges',
33 'wffilemods',
34 'wfhits',
35 'wfhoover',
36 'wfissues',
37 'wfknownfilelist',
38 'wflivetraffichuman',
39 'wflocs',
40 'wflogins',
41 'wfnotifications',
42 'wfpendingissues',
43 'wfreversecache',
44 'wfsnipcache',
45 'wfstatus',
46 'wftrafficrate',
47 'actionscheduler_logs',
48 'slim_stats',
49 'woocommerce_sessions',
50 'yoast_indexable',
51 'slim_events',
52 'cerber_files',
53 'cerber_traffic',
54 'cerber_log',
55 'cerber_countries',
56 'cerber_blocks',
57 'cerber_acl',
58 'statistics_views',
59 'pcachewpr',
60 'statistics_visitor_relationships',
61 'statistics_visitor',
62 'statistics_visit',
63 'statistics_search',
64 'statistics_pages',
65 'icl_languages_translations',
66 'icl_string_pages',
67 'icl_string_translations',
68 'itsec_log',
69 'actionscheduler_actions',
70 'aepc_logs',
71 'WP_SEO_404_links',
72 'wp_seo_404_links',
73 'WP_SEO_Redirection_LOG'
74 ];
75
76 private $excludeSearchReplaceTables = [
77 'itsec_log'
78 ];
79
80 /**
81 * __construct - Make connection
82 *
83 * @return @self
84 */
85 function __construct($storage, $firstDB, &$manifest, &$logger, $splitting, $isCLI) {
86
87 $this->isCLI = $isCLI;
88 $this->splitting = $splitting;
89 $this->storage = $storage;
90 $this->logger = &$logger;
91 $this->manifest = &$manifest;
92 $this->tablemap = BMI_TMP . DIRECTORY_SEPARATOR . '.table_map';
93
94 if ($firstDB) $this->initMessage();
95
96 $this->map = $this->getTableMap();
97 $this->seek = &$this->map['seek'];
98
99 }
100
101 public function start() {
102
103 if ($this->isCLI) {
104
105 while ($nextFile = $this->getNextFile()) {
106 $this->processFile($nextFile);
107 }
108
109 return true;
110
111 } else {
112
113 $nextFile = $this->getNextFile();
114 if ($nextFile === false) return true;
115 else {
116
117 $this->processFile($nextFile);
118
119 return false;
120
121 }
122
123 }
124
125 }
126
127 private function getTableMap() {
128
129 if (file_exists($this->tablemap)) {
130
131 $data = json_decode(file_get_contents($this->tablemap), true);
132 $this->map = $data;
133
134 } else {
135
136 $data = [
137 'tables' => [],
138 'seek' => [
139 'last_seek' => 0,
140 'last_file' => '...',
141 'last_start' => 0,
142 'total_tables' => sizeof(array_diff(scandir($this->storage), ['..', '.'])),
143 'active_plugins' => 'a:1:{i:0;s:31:"backup-backup/backup-backup.php";}'
144 ]
145 ];
146
147 file_put_contents($this->tablemap, json_encode($data));
148
149 }
150
151 return $data;
152
153 }
154
155 private function getTableProgress() {
156
157 $total_tables = $this->seek['total_tables'];
158 $tables_left = sizeof(array_diff(scandir($this->storage), ['..', '.']));
159
160 $finished_tables = ($total_tables - $tables_left) + 1;
161 $percentage = number_format(($finished_tables / $total_tables) * 100, 2);
162
163 $this->logger->progress(50 + ((number_format($percentage, 0) / 2) - 10));
164
165 return $finished_tables . '/' . $total_tables . ' (' . $percentage . '%)';
166
167 }
168
169 private function queryFile(&$objFile, $filePath, $tableName, $realTableName) {
170
171 global $wpdb;
172
173 $seek = &$this->seek['last_seek'];
174 if ($seek == 0) {
175 $seek = 5;
176 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Identifier is safely escaped via escapeSQLIDentifier()
177 $wpdb->query("DROP TABLE IF EXISTS " . BMP::escapeSQLIDentifier($tableName) . ";");
178
179 $str = __("Started restoration of %table_name% %total_tables% table", 'backup-backup');
180 $str = str_replace('%table_name%', $realTableName, $str);
181 $str = str_replace('%total_tables%', $this->getTableProgress(), $str);
182 $this->logger->log($str, 'STEP');
183
184 // Check if file can be cleaned
185 $this->filterFile($filePath, basename($filePath));
186 }
187
188 $wpdb->suppress_errors();
189
190 $wpdb->query('SET autocommit = 0;');
191 $wpdb->query('SET foreign_key_checks = 0;');
192 $wpdb->query("SET SQL_MODE = '';");
193 $wpdb->query('START TRANSACTION;');
194
195 $sql = '';
196 while (!$objFile->eof()) {
197 $objFile->seek($seek); $seek++;
198 $line = rtrim($objFile->current(), "\n");
199 if (strlen($line) !== 0) {
200 $sql = $line;
201 unset($line);
202 break;
203 }
204 }
205
206 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query is from trusted backup SQL file, not user input
207 $wpdb->query($sql);
208 unset($sql);
209 $wpdb->query('COMMIT;');
210 $wpdb->query('SET autocommit = 1;');
211 $wpdb->query('SET foreign_key_checks = 1;');
212
213 $str = __("Progress of %table_name%: %progress%", 'backup-backup');
214 $str = str_replace('%table_name%', $realTableName, $str);
215
216 $objFile->seek($objFile->getSize());
217 $total_size = $objFile->key() - 5;
218 $objFile->seek($seek);
219
220 if ($total_size <= 0) $total_size = 1;
221 if (($seek - 5) <= 0) $seek = 6;
222 if ($total_size > 0) $total_size += 1;
223
224 $progress = ($seek - 5) . '/' . $total_size . " (" . number_format(($seek - 5) / $total_size * 100, 2) . "%)";
225 $str = str_replace('%progress%', $progress, $str);
226 $this->logger->log($str, 'INFO');
227
228 $wpdb->show_errors();
229
230 if ($objFile->eof()) {
231 return true;
232 } else {
233 return false;
234 }
235
236 }
237
238 private function addNewTableToMap($from, $to, $file) {
239
240 if (!array_key_exists($from, $this->map['tables'])) {
241 $this->map['tables'][$from] = $to;
242 }
243
244 file_put_contents($this->tablemap, json_encode($this->map));
245
246 }
247
248 private function processFile($file) {
249
250 if ($this->seek['last_seek'] == 0) {
251 $this->seek['last_start'] = microtime(true);
252 }
253
254 $objFile = new \SplFileObject($file);
255
256 $objFile->seek(1);
257 $realTableName = explode('`', $objFile->current())[1];
258
259 $objFile->seek(2);
260 $tmpTableName = explode('`', $objFile->current())[1];
261
262 $finished = $this->queryFile($objFile, $file, $tmpTableName, $realTableName);
263
264 if ($finished && file_exists($file)) {
265 $this->seek['last_seek'] = 0;
266 $this->seek['last_file'] = '...';
267 @unlink($file);
268
269 $totalTime = microtime(true) - intval($this->seek['last_start']);
270 $totalTime = number_format($totalTime, 5);
271
272 $str = __("Table %table_name% restoration took %time% seconds", 'backup-backup');
273 $str = str_replace('%table_name%', $realTableName, $str);
274 $str = str_replace('%time%', $totalTime, $str);
275
276 $this->logger->log($str, 'SUCCESS');
277 $this->seek['last_start'] = 0;
278 }
279
280 $this->addNewTableToMap($tmpTableName, $realTableName, $file);
281
282 return true;
283
284 }
285
286 private function parseDomain($domain, $removeWWW = true) {
287
288 if (substr($domain, 0, 8) == 'https://') $domain = substr($domain, 8);
289 if (substr($domain, 0, 7) == 'http://') $domain = substr($domain, 7);
290 if ($removeWWW === true) {
291 if (substr($domain, 0, 4) == 'www.') $domain = substr($domain, 4);
292 }
293 $domain = untrailingslashit($domain);
294
295 return $domain;
296
297 }
298
299 private function replaceTableNames($tables) {
300
301 global $wpdb;
302
303 $this->logger->log(__('Performing table replacement', 'backup-backup'), 'STEP');
304
305 $wpdb->suppress_errors();
306 foreach ($tables as $oldTable => $newTable) {
307
308 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Identifier is safely escaped via escapeSQLIDentifier()
309 $wpdb->query("DROP TABLE IF EXISTS " . BMP::escapeSQLIDentifier($newTable) . ";");
310
311 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Identifier is safely escaped via escapeSQLIDentifier()
312 $wpdb->query("ALTER TABLE " . BMP::escapeSQLIDentifier($oldTable) . " RENAME TO " . BMP::escapeSQLIDentifier($newTable) . ";");
313
314 $str = __('Table %old% renamed to %new%', 'backup-backup');
315 $str = str_replace('%old%', $oldTable, $str);
316 $str = str_replace('%new%', $newTable, $str);
317 $this->logger->log($str, 'INFO');
318
319 }
320
321 $wpdb->show_errors();
322 $this->logger->log(__('All tables replaced', 'backup-backup'), 'SUCCESS');
323
324 }
325
326 private function performReplace($step = 0, $tableIndex = 0, $currentPage = 0, $totalPages = 0, $fieldAdjustments = 0, $newPrefix = "wp_") {
327
328 $status = [
329 'step' => $step,
330 'tableIndex' => $tableIndex,
331 'finished' => false,
332 'currentPage' => $currentPage,
333 'totalPages' => $totalPages,
334 'fieldAdjustments' => $fieldAdjustments
335 ];
336
337 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'search-replace.php';
338
339 $backupRootDir = $this->manifest->config->ABSPATH;
340 $currentRootDir = ABSPATH;
341
342 $homeURL = site_url();
343 if (strlen($homeURL) <= 8) $homeURL = home_url();
344 if (defined('WP_SITEURL') && strlen(WP_SITEURL) > 8) $homeURL = WP_SITEURL;
345
346 $backupDomain = $this->parseDomain($this->manifest->dbdomain);
347 $currentDomain = $this->parseDomain($homeURL, false);
348
349 $currentTable = false;
350 $allTables = array_keys($this->map['tables']);
351
352 if ($tableIndex < sizeof($allTables) && array_key_exists($tableIndex, $allTables)) {
353 $currentTable = $allTables[$tableIndex];
354 }
355
356 if ($currentTable == false && !$currentTable) {
357 if ($backupRootDir != $currentRootDir || $currentDomain != $this->parseDomain($backupDomain, false)) {
358 $this->logger->log(__('Search & Replace finished successfully.', 'backup-backup'), 'SUCCESS');
359 }
360
361 $status['finished'] = true;
362 return $status;
363 }
364
365 if (strpos($currentTable, $newPrefix) === false) {
366 $this->logger->log(__('Adjustments are not required for this table.', 'backup-backup') . "(" . sanitize_text_field(strval($currentTable)) . ")", 'INFO');
367
368 $status['step'] = 1;
369 $status['fieldAdjustments'] = 0;
370 $status['tableIndex'] = $tableIndex + 1;
371 return $status;
372 }
373
374 if ($this->searchForIncludesInList($this->excludeSearchReplaceTables, $currentTable)) {
375 $this->logger->log(__('Adjustments are not required for this table.', 'backup-backup') . "(" . sanitize_text_field(strval($currentTable)) . ")", 'INFO');
376
377 $status['step'] = 1;
378 $status['fieldAdjustments'] = 0;
379 $status['tableIndex'] = $tableIndex + 1;
380 return $status;
381 }
382
383 $replaceEngine = new BMISearchReplace([$currentTable], $currentPage, $totalPages);
384
385 if ($step == 0) {
386 $ssl = is_ssl() == true ? 'https://' : 'http://';
387 $this->logger->log('Previous detected domain: ' . $backupDomain, 'VERBOSE');
388 $this->logger->log('Previous detected domain parsed: ' . $this->parseDomain($backupDomain, false), 'VERBOSE');
389 $this->logger->log('New detected domain: ' . $currentDomain, 'VERBOSE');
390 $this->logger->log('SSL: ' . $ssl, 'VERBOSE');
391
392 $this->logger->log('Previous ABSPATH: ' . $backupRootDir, 'VERBOSE');
393 $this->logger->log('New detected ABSPATH: ' . $currentRootDir, 'VERBOSE');
394
395 if ($backupRootDir != $currentRootDir || $currentDomain != $this->parseDomain($backupDomain, false)) {
396 $this->logger->log(__('Performing Search & Replace', 'backup-backup'), 'STEP');
397 $pagesize = '?';
398 if (defined('BMI_MAX_SEARCH_REPLACE_PAGE')) $pagesize = BMI_MAX_SEARCH_REPLACE_PAGE;
399 $this->logger->log(__('Page size for that restoration: ', 'backup-backup') . $pagesize, 'INFO');
400 $status['step'] = $step + 1; $step++;
401 } else {
402 $this->logger->log(__('This backup was made on the same site, ommiting search & replace.', 'backup-backup'), 'INFO');
403 $status['finished'] = true;
404 return $status;
405 }
406 }
407
408 if ($step == 1) {
409 $replaceProgress = ($tableIndex + 1) . "/" . sizeof($allTables);
410 $replaceProgressPercentage = number_format((($tableIndex + 1) / sizeof($allTables) * 100), 2);
411 $progressLogT = __('Performing database adjustments for table %progress%: %table_name% (%progress_percentage%)', 'backup-backup');
412 $progressLogT = str_replace('%progress%', $replaceProgress, $progressLogT);
413 $progressLogT = str_replace('%table_name%', $currentTable, $progressLogT);
414 $progressLogT = str_replace('%progress_percentage%', $replaceProgressPercentage . '%', $progressLogT);
415 $this->logger->log($progressLogT, 'STEP');
416
417 $percentageProgress = number_format($replaceProgressPercentage, 0);
418 $this->logger->progress(number_format(90 + ($percentageProgress / 100) * 8, 0));
419
420 if ($backupRootDir != $currentRootDir) {
421
422 $dtables = 0; $drows = 0; $dchange = 0; $dupdates = 0;
423
424 $r = $replaceEngine->perform($backupRootDir, $currentRootDir);
425 $dtables += $r['tables']; $drows += $r['rows']; $dchange += $r['change']; $dupdates += $r['updates'];
426
427 $status['currentPage'] = $r['currentPage'];
428 $status['totalPages'] = $r['totalPages'];
429
430 if ($status['totalPages'] > 0) {
431
432 $info = __("Batch for path adjustment (%page%/%allPages%) updated: %updates% fields.", 'backup-backup');
433 $updates = $dupdates;
434 if ($updates == 0) $updates = 1;
435 $info = str_replace('%page%', $status['currentPage'], $info);
436 $info = str_replace('%allPages%', $status['totalPages'], $info);
437 $info = str_replace('%updates%', $updates, $info);
438 $this->logger->log($info, 'INFO');
439 $status['fieldAdjustments']++;
440
441 }
442
443 if ($status['currentPage'] >= $status['totalPages']) {
444 $status['currentPage'] = 0;
445 $status['totalPages'] = 0;
446 $status['step'] = $step + 1;
447 }
448 return $status;
449
450 } else {
451
452 $status['step'] = $step + 1;
453 $step++;
454
455 }
456 }
457
458 if ($step == 2 || $step == 3 || $step == 4 || $step == 5 || $step == 6 || $step == 7) {
459 if ($currentDomain != $this->parseDomain($backupDomain, false)) {
460 $ssl = is_ssl() == true ? 'https://' : 'http://';
461
462 $dtables = 0; $drows = 0; $dchange = 0; $dupdates = 0;
463
464 $possibleDomainsBackup = [
465 "https://www." . $backupDomain,
466 "http://www." . $backupDomain,
467 "https://" . $backupDomain,
468 "http://" . $backupDomain,
469 'www.' . $backupDomain,
470 $backupDomain
471 ];
472
473 $possibleDomainsCurrent = [
474 $ssl . $currentDomain,
475 $ssl . $currentDomain,
476 $ssl . $currentDomain,
477 $ssl . $currentDomain,
478 $currentDomain,
479 $currentDomain
480 ];
481
482 if ($step == 2) {
483 $r = $replaceEngine->perform($possibleDomainsBackup[0], $possibleDomainsCurrent[0]);
484 $dtables += $r['tables']; $drows += $r['rows']; $dchange += $r['change']; $dupdates += $r['updates'];
485 }
486
487 if ($step == 3) {
488 $r = $replaceEngine->perform($possibleDomainsBackup[1], $possibleDomainsCurrent[1]);
489 $dtables += $r['tables']; $drows += $r['rows']; $dchange += $r['change']; $dupdates += $r['updates'];
490 }
491
492 if ($step == 4) {
493 $r = $replaceEngine->perform($possibleDomainsBackup[2], $possibleDomainsCurrent[2]);
494 $dtables += $r['tables']; $drows += $r['rows']; $dchange += $r['change']; $dupdates += $r['updates'];
495 }
496
497 if ($step == 5) {
498 $r = $replaceEngine->perform($possibleDomainsBackup[3], $possibleDomainsCurrent[3]);
499 $dtables += $r['tables']; $drows += $r['rows']; $dchange += $r['change']; $dupdates += $r['updates'];
500 }
501
502 if ($step == 6) {
503 $r = $replaceEngine->perform($possibleDomainsBackup[4], $possibleDomainsCurrent[4]);
504 $dchange += $r['change']; $dupdates += $r['updates'];
505 }
506
507 if ($step == 7) {
508 if (!(substr($currentDomain, -strlen($backupDomain)) === $backupDomain)) {
509 $r = $replaceEngine->perform($possibleDomainsBackup[5], $possibleDomainsCurrent[5]);
510 $dchange += $r['change']; $dupdates += $r['updates'];
511 }
512 }
513
514 $status['currentPage'] = $r['currentPage'];
515 $status['totalPages'] = $r['totalPages'];
516
517 $variants = [
518 __('variant A', 'backup-backup'),
519 __('variant B', 'backup-backup'),
520 __('variant C', 'backup-backup'),
521 __('variant D', 'backup-backup'),
522 __('variant E', 'backup-backup'),
523 __('variant F', 'backup-backup'),
524 ];
525
526 if ($status['totalPages'] > 0) {
527
528 $info = __("Batch for domain (%variant%) adjustments (%page%/%allPages%) updated: %updates% fields.", 'backup-backup');
529 $updates = $dupdates;
530 if ($updates == 0) $updates = 1;
531 $info = str_replace('%variant%', $variants[$step - 2], $info);
532 $info = str_replace('%page%', $status['currentPage'], $info);
533 $info = str_replace('%allPages%', $status['totalPages'], $info);
534 $info = str_replace('%updates%', $updates, $info);
535 $this->logger->log($info, 'INFO');
536 $status['fieldAdjustments']++;
537
538 } else {
539
540 // $info = __('Domain (%variant%) adjustments are not required for this table.', 'backup-backup');
541 // $info = str_replace('%variant%', $variants[$step - 2], $info);
542 // $this->logger->log($info, 'INFO');
543
544 }
545
546 if ($status['currentPage'] >= $status['totalPages']) {
547 $status['currentPage'] = 0;
548 $status['totalPages'] = 0;
549 $status['step'] = $step + 1;
550 }
551
552 return $status;
553
554 } else {
555
556 $status['currentPage'] = 0;
557 $status['totalPages'] = 0;
558 $status['step'] = 8;
559 $step = 8;
560
561 }
562 }
563
564 if ($step == 8) {
565 if ($fieldAdjustments === 0) {
566 $this->logger->log(__('Adjustments are not required for this table.', 'backup-backup'), 'INFO');
567 }
568
569 $status['step'] = 1;
570 $status['fieldAdjustments'] = 0;
571 $status['tableIndex'] = $tableIndex + 1;
572 return $status;
573 }
574
575 return $status;
576
577 }
578
579 public function searchForIncludesInList($items, $search) {
580 $search = strtolower($search);
581 foreach ($items as $index => $item) {
582 if (strpos($search, strtolower($item)) !== false) return true;
583 }
584
585 return false;
586 }
587
588 public function is_valid_plugin($plugin) {
589
590 global $wp_version;
591
592 $default_headers = array( 'wp' => 'Requires at least', 'php' => 'Requires PHP' );
593
594 $wp = false;
595 $php = false;
596
597 $detectedwp = '0.0.0';
598 $detectedphp = '0.0.0';
599
600 try {
601
602 $plugin_file = BMP::fixSlashes(WP_PLUGIN_DIR . '/' . $plugin);
603 $plugin_readme = BMP::fixSlashes(WP_PLUGIN_DIR . '/' . dirname($plugin) . '/readme.txt');
604 if (!file_exists($plugin_readme)) $plugin_readme = BMP::fixSlashes(WP_PLUGIN_DIR . '/' . dirname($plugin) . '/README.txt');
605
606 if (file_exists($plugin_file)) {
607 $plugin_data = get_file_data($plugin_file, $default_headers, 'plugin');
608 if (!empty($plugin_data['wp']) && version_compare($plugin_data['wp'], $wp_version, '<=')) {
609 $detectedwp = $plugin_data['wp'];
610 $wp = true;
611 }
612 if (!empty($plugin_data['php']) && version_compare($plugin_data['php'], PHP_VERSION, '<=')) {
613 $detectedphp = $plugin_data['php'];
614 $php = true;
615 }
616 }
617
618 if (file_exists($plugin_readme)) {
619 $readme_data = get_file_data($plugin_readme, $default_headers, 'plugin');
620 if (!empty($readme_data['wp']) && version_compare($readme_data['wp'], $wp_version, '<=')) {
621 $detectedwp = $readme_data['wp'];
622 $wp = true;
623 }
624
625 if (!empty($readme_data['php']) && version_compare($readme_data['php'], PHP_VERSION, '<=')) {
626 $detectedphp = $readme_data['php'];
627 $php = true;
628 }
629 }
630
631 $this->logger->log(sprintf('Detected version WP/PHP for plugin: %s, is (%s/%s)', $plugin, $detectedwp, $detectedphp), 'VERBOSE');
632
633 if ($plugin == 'hello.php') return true;
634 if ($php && $wp) return true;
635 else return false;
636
637 } catch (Error $e) {
638 return false;
639 }
640
641 return false;
642
643 }
644
645 private function try_activate_plugins($plugins, $sucstr_source, $failstr_source, $failed_plugins = []) {
646
647 $plugins_copy = array_values($plugins);
648 $should_continue = false;
649 $activated_plugins = [];
650 $failed_plugins = $failed_plugins;
651 $disallowed_plugins = [
652 'bluehost-wordpress-plugin/bluehost-wordpress-plugin.php',
653 'sg-cachepress/sg-cachepress.php',
654 'wordpress-starter/siteground-wizard.php',
655 'revslider/revslider.php',
656 'easy-soundcloud-shortcode/easy-soundcloud-shortcode.php',
657 'easy-soundcloud-shortcode/EasySoundcloudShortcode.php',
658 'mainwp-child/mainwp-child.php',
659 'wp-google-maps/wp-google-maps.php',
660 'wp-google-maps/wpGoogleMaps.php'
661 ];
662
663 for ($i = 0; $i < sizeof($plugins_copy); ++$i) {
664
665 $plugin_name = $plugins_copy[$i];
666 $plugin_display = $plugin_name;
667
668 $shouldActivate = true;
669
670 if (empty($plugin_name)) {
671 $shouldActivate = false;
672 $plugin_display = '(---)';
673 } else if ($plugin_name == 'hello.php') {
674 $shouldActivate = true;
675 } else if (strpos($plugin_name, '/') === false) {
676 $shouldActivate = false;
677 }
678
679 $sucstr = str_replace('%plugin_name%', $plugin_display, $sucstr_source);
680 $failstr = str_replace('%plugin_name%', $plugin_display, $failstr_source);
681
682 if ($shouldActivate) {
683 try {
684
685 if ($this->is_valid_plugin($plugin_name) && !in_array($plugin_name, $disallowed_plugins)) {
686
687 $resultWP = activate_plugin($plugin_name, '', true, true);
688
689 $this->logger->log($sucstr, 'INFO');
690 $activated_plugins[] = $plugin_name;
691
692 $should_continue = true;
693 break;
694
695 } else {
696
697 if (!in_array($plugin_name, $failed_plugins)) {
698 $failed_plugins[] = $plugin_name;
699 }
700
701 }
702
703 } catch (\Exception $e) {
704
705 if (!in_array($plugin_name, $failed_plugins)) {
706
707 $failed_plugins[] = $plugin_name;
708 error_log(strval($e));
709
710 }
711
712 } catch (\Throwable $e) {
713
714 if (!in_array($plugin_name, $failed_plugins)) {
715
716 $msg = $e->getMessage();
717 if (strpos($msg, 'add_rule()') != false || strpos($msg, 'rewrite.php:143') != false) {
718
719 $activated_plugins[] = $plugin_name;
720 error_log(strval($e));
721
722 } else {
723
724 $failed_plugins[] = $plugin_name;
725 error_log(strval($e));
726
727 }
728
729 }
730
731 }
732
733 } else {
734
735 $this->logger->log($failstr, 'WARN');
736
737 }
738
739 }
740
741 return [ 'failed' => $failed_plugins, 'active' => $activated_plugins, 'should_continue' => $should_continue ];
742
743 }
744
745 public function enablePlugins() {
746
747 global $wpdb;
748
749 $this->logger->log(__('Enabling plugins included in the backup', 'backup-backup'), 'STEP');
750
751 if (is_serialized($this->seek['active_plugins'])) {
752
753 $plugins = unserialize($this->seek['active_plugins']);
754 usort($plugins, function ($a, $b) { return strlen($a) - strlen($b); });
755 $plugins = array_values($plugins);
756
757 $sucstr_source = __('Plugin %plugin_name% enabled successfully.', 'backup-backup');
758 $failstr_source = __('Failed to enable plugin %plugin_name%, trying to not end at fatal error...', 'backup-backup');
759
760 $fullyActive = [];
761 $failed_plugins = [];
762
763 if (!function_exists('activate_plugin')) {
764 require_once(ABSPATH .'/wp-admin/includes/plugin.php');
765 }
766
767 $one_more_time = true;
768 $try_again = true;
769 while ($try_again) {
770
771 $res = $this->try_activate_plugins($plugins, $sucstr_source, $failstr_source, $failed_plugins);
772
773 $fullyActive = array_unique(array_merge($fullyActive, $res['active']));
774 $plugins = array_diff($plugins, $res['active']);
775 $failed_plugins = array_unique(array_diff(array_merge($failed_plugins, $res['failed']), $fullyActive));
776
777 $try_again = $res['should_continue'];
778 if ($try_again == false && $one_more_time == true) {
779 $one_more_time = false;
780 $try_again = true;
781 }
782
783 }
784
785 for ($i = 0; $i < sizeof($failed_plugins); ++$i) {
786
787 $failstr = str_replace('%plugin_name%', $failed_plugins[$i], $failstr_source);
788 $this->logger->log($failstr, 'WARN');
789
790 }
791
792 if (!in_array('backup-backup/backup-backup.php', $fullyActive)) {
793 $fullyActive[] = 'backup-backup/backup-backup.php';
794 }
795
796 update_option('active_plugins', $fullyActive);
797
798 }
799
800 $this->logger->progress(100);
801 $this->logger->log(__('All plugins enabled, you are ready to go :)', 'backup-backup'), 'SUCCESS');
802
803 }
804
805 public function searchReplace($step = 0, $tableIndex = 0, $currentPage = 0, $totalPages = 0, $fieldAdjustments = 0, $newPrefix = 'wp_') {
806
807 $this->logger->progress(90);
808 $searchReplaceEngine = Dashboard\bmi_get_config('OTHER::NEW_SEARCH_REPLACE_ENGINE');
809 if ($searchReplaceEngine == false) {
810 return $this->performReplace($step, $tableIndex, $currentPage, $totalPages, $fieldAdjustments, $newPrefix);
811 }
812 return $this->performReplaceV2($step, $tableIndex, $currentPage, $totalPages, $fieldAdjustments, $newPrefix);
813
814 }
815
816 public function alter_tables() {
817
818 $this->logger->progress(98);
819 $this->prepareFinalDatabase();
820 $this->replaceTableNames($this->map['tables']);
821
822 }
823
824 private function prepareFinalDatabase() {
825
826 global $wpdb;
827
828 $tables = array_keys($this->map['tables']);
829 $unique_prefix = explode('_', $tables[0])[0];
830 $backupPrefix = $this->manifest->config->table_prefix;
831
832 $options_table = $unique_prefix . '_' . $backupPrefix . 'options';
833 if (!in_array($options_table, $tables)) {
834 $tablename = false;
835 for ($i = 0; $i < sizeof($tables); ++$i) {
836 $table = $tables[$i];
837 if (substr($table, -7) == 'options') {
838 $tablename = $table;
839 break;
840 }
841 }
842
843 $options_table = $tablename;
844 }
845
846 if ($options_table != false && in_array($options_table, $tables)) {
847
848 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Identifier is safely escaped via escapeSQLIDentifier()
849 $wpdb->query($wpdb->prepare("DELETE FROM " . BMP::escapeSQLIDentifier($options_table) . " WHERE option_name LIKE %s", '%\_transient\_%'));
850
851 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Identifier is safely escaped via escapeSQLIDentifier()
852 $active_plugins = $wpdb->get_results($wpdb->prepare("SELECT option_value FROM " . BMP::escapeSQLIDentifier($options_table) . " WHERE option_name = %s", 'active_plugins'));
853 $active_plugins = $active_plugins[0]->option_value;
854
855 $this->seek['active_plugins'] = $active_plugins;
856
857 update_option('active_plugins', ['backup-backup/backup-backup.php']);
858
859 $homeURL = site_url();
860 if (strlen($homeURL) <= 8) $homeURL = home_url();
861 if (defined('WP_SITEURL') && strlen(WP_SITEURL) > 8) $homeURL = WP_SITEURL;
862
863 $ssl = is_ssl() == true ? 'https://' : 'http://';
864 $currentDomain = $ssl . $this->parseDomain($homeURL, false);
865
866 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Identifier is safely escaped via escapeSQLIDentifier()
867 $wpdb->query($wpdb->prepare("UPDATE " . BMP::escapeSQLIDentifier($options_table) . " SET option_value = %s WHERE option_name = %s", $currentDomain, 'siteurl'));
868
869 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Identifier is safely escaped via escapeSQLIDentifier()
870 $wpdb->query($wpdb->prepare("UPDATE " . BMP::escapeSQLIDentifier($options_table) . " SET option_value = %s WHERE option_name = %s", $currentDomain, 'home'));
871
872 }
873
874 }
875
876 private function filterFile($path, $name) {
877
878 $blacklist = $this->unwantedTables;
879 $tablename = strtolower(substr($name, 0, -4));
880
881 $shouldBeExcluded = false;
882 for ($i = 0; $i < sizeof($blacklist); ++$i) {
883
884 $rule = strtolower($blacklist[$i]);
885 if (substr($tablename, -(strlen($rule))) == $rule) {
886 $shouldBeExcluded = true;
887 break;
888 }
889
890 }
891
892 if ($shouldBeExcluded == true) {
893
894 $file = new \SplFileObject($path);
895 $file->seek($file->getSize());
896 $total_lines = $file->key() + 1;
897
898 if ($total_lines >= 6) {
899
900 $str = "\n\n\n\n\n";
901
902 $file->seek(5);
903 $str .= trim($file->current());
904
905 $this->logger->log(str_replace('%s', substr($name, 0, -4), __('Cleaning up contents of %s table.', 'backup-backup')), 'INFO');
906 file_put_contents($path, $str);
907
908 $file = null;
909
910 }
911
912 }
913
914 }
915
916 private function getNextFile() {
917
918 if ($this->seek['last_file'] == '...') {
919
920 $nextFile = false;
921
922 $sqlFiles = array_diff(scandir($this->storage), ['..', '.']);
923 $sqlFiles = array_values($sqlFiles);
924
925 if (sizeof($sqlFiles) > 0) {
926 $nextFilePath = $this->storage . DIRECTORY_SEPARATOR . $sqlFiles[0];
927 return $nextFilePath;
928 }
929
930 $this->seek['last_file'] = $nextFile;
931 return $nextFile;
932
933 } else {
934
935 return $this->seek['last_file'];
936
937 }
938
939 }
940
941 private function initMessage() {
942
943 $this->logger->log(__('Successfully detected backup created with v3 engine, importing...', 'backup-backup'), 'INFO');
944 $this->logger->log(__('Restoring database (using v4 engine)...', 'backup-backup'), 'STEP');
945
946 if (file_exists($this->tablemap)) {
947 @unlink($this->tablemap);
948 }
949
950 }
951
952 private function performReplaceV2($step = 0, $tableIndex = 0, $currentPage = 0, $totalPages = 0, $fieldAdjustments = 0, $newPrefix = "wp_")
953 {
954
955 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'search-replace-processor.php';
956 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'search-replace-v2.php';
957
958 // Initialize Processor
959 $processor = new \BMI\Plugin\Database\BMISearchReplaceProcessor(
960 $this->map['tables'],
961 $this->logger,
962 $this->manifest,
963 $this->excludeSearchReplaceTables
964 );
965
966 // Process Current Step
967 return $processor->process($step, $tableIndex, $newPrefix);
968
969 }
970
971
972 }
973