PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 1.3.2
Backup Migration v1.3.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 / better-restore.php
backup-backup / includes / database Last commit date
better-backup-v3.php 2 years ago better-backup.php 2 years ago better-restore.php 2 years ago even-better-restore-v3.php 2 years ago even-better-restore-v4.php 2 years ago manager.php 2 years ago search-replace.php 2 years ago smart-sort.php 2 years ago
better-restore.php
988 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
17 // Exit on direct access
18 if (!defined('ABSPATH')) exit;
19
20 global $bmi_last_seek, $bmi_last_file;
21 // register_shutdown_function(function () {
22 //
23 // echo $GLOBALS['bmi_last_file'] . ' - ' . $GLOBALS['bmi_last_seek'];
24 //
25 // });
26
27 /**
28 * Database importing
29 * Main Class, requires $wpdb
30 */
31 class BMI_Database_Importer {
32
33 /**
34 * Private local variables
35 */
36 private $files = [];
37
38 /**
39 * __construct - Initialization and logger resolver
40 *
41 * @return self
42 */
43 function __construct($storage, $total_queries, $old_abspath, $old_domain, $new_domain, &$logger, $isCLI, $conversionStats) {
44
45 /**
46 * WP Global Database variable
47 */
48 global $wpdb;
49 $this->wpdb = &$wpdb;
50
51 /**
52 * Set isCLI variable
53 */
54 $this->isCLI = $isCLI;
55
56 /**
57 * Logger of BMI core
58 */
59 $this->logger = &$logger;
60
61 /**
62 * Storage directory
63 */
64 // $this->storage = trailingslashit(__DIR__) . 'data';
65 $this->storage = $storage;
66 $this->total_queries = intval($total_queries);
67 // $this->total_queries = $this->conversionStats['total_queries'];
68
69 // Conversion stats
70 $this->conversionStats = $conversionStats;
71
72 /**
73 * Domain(s) configuration
74 * ? - All cases that domain can be represented in the database
75 */
76 $this->source_domain = untrailingslashit($old_domain);
77 $this->new_domain = untrailingslashit($new_domain); // untrailingslashit(home_url());
78 $this->json_source_domain = trim(json_encode($this->source_domain), '"');
79 $this->json_new_domain = trim(json_encode($this->new_domain), '"');
80 $this->no_protocol_source_domain = ltrim(ltrim($this->source_domain, "https://"), "http://");
81 $this->no_protocol_new_domain = ltrim(ltrim($this->new_domain, "https://"), "http://");
82 $this->json_no_protocol_source_domain = ltrim(ltrim($this->json_source_domain, "https:\/\/"), "http:\/\/");
83 $this->json_no_protocol_new_domain = ltrim(ltrim($this->json_new_domain, "https:\/\/"), "http:\/\/");
84
85 $this->source_abs = untrailingslashit($old_abspath);
86 $this->new_abs = untrailingslashit(ABSPATH);
87 $this->json_source_abs = trim(json_encode($this->source_abs), '"');
88 $this->json_new_abs = trim(json_encode($this->new_abs), '"');
89
90 $this->xi = 0;
91 $this->current_file = '';
92 $this->init_start = microtime(true);
93 $this->last_finish = $this->init_start;
94 $this->table_names_alter = [];
95
96 @ini_set('memory_limit', '-1');
97 if ((class_exists('mysqli') || class_exists('\mysqli')) && false) {
98 $this->owndb = true;
99 $this->db = new \mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
100 if ($this->db->connect_error) $this->owndb = false;
101 else $this->db->query('SET foreign_key_checks = 0;');
102 } else {
103 $this->owndb = false;
104 }
105
106 set_error_handler(function($errno, $errstr, $errfile, $errline) {
107
108 if (BMI_DEBUG) {
109 error_log('BMI DEBUG ENABLED, HERE IS THE COMPLETE REPORT (ERROR HANDLER #4):');
110 error_log(print_r($errno, true));
111 error_log(print_r($errstr, true));
112 error_log(print_r($errfile, true));
113 error_log(print_r($errline, true));
114 }
115
116 // The only notice we could get here is Instance does not exist, ignore it.
117 if ($errno === '8' || $errno === 8 || $errno === E_NOTICE) {
118 if ($errfile == __FILE__) {
119 throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
120 return;
121 }
122 }
123
124 // error_log($errno .' '. $errstr .' '. $errfile .':'. $errline);
125 // throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
126
127 });
128
129 }
130
131 /**
132 * showFirstLogs - Shows logs for initialization
133 *
134 * @return @void
135 */
136 public function showFirstLogs() {
137
138 $this->logger->log("Initializing database import V2 engine...", 'INFO');
139 $this->logger->log("Initialized successfully, memory usage: " . number_format(memory_get_usage() / 1024 / 1024, 2) . " MB", 'SUCCESS');
140
141 }
142
143 /**
144 * showFirstLogs - Shows logs after finish
145 *
146 * @return @void
147 */
148 public function showEndLogs() {
149
150 $from_start = number_format(microtime(true) - $this->init_start, 4);
151 $this->logger->log("Total time to insert " . $this->xi . " queries took " . $from_start . " seconds.", 'INFO');
152
153 }
154
155 /**
156 * import - Import initializer
157 *
158 * @return {bool} true on success false on fail
159 */
160 public function import() {
161
162 $this->logger->log("Loading file list...", 'INFO');
163 $this->load_file_list();
164 $this->logger->log("File list loaded, memory usage: " . number_format(memory_get_usage() / 1024 / 1024, 2) . " MB", 'SUCCESS');
165
166 $this->logger->log("Restoring tables using temporary name...", 'INFO');
167 $this->restore_tables();
168
169 // Mark those as finished
170 $GLOBALS['bmi_last_file'] = false;
171 $GLOBALS['bmi_last_seek'] = false;
172
173 $this->showEndLogs();
174
175 }
176
177 /**
178 * restore_by_file - Allows to restore by one file
179 *
180 * @param {string} path to sql file
181 * @param {int} last seek position of this file
182 * @return {int/bool} last seek number or bool if completed
183 */
184 public function restore_by_file($path, $last_seek = 0) {
185
186 if (!file_exists($path)) {
187 if (file_exists($path)) @unlink($path);
188 $this->logger->log("Database file does not exist anymore (" . basename($path) . ")? – Omitting.", 'INFO');
189 return true;
190 }
191
192 $file = new \SplFileObject($path);
193 $file->seek($file->getSize());
194 $total_lines = $file->key() + 1;
195 $this->current_file = $path;
196
197 $last_real_table_name = null;
198 $custom_var_started = false;
199 $values_started = false;
200 $query_ended = false;
201 $query = '';
202
203 $query_exist = false;
204
205 $last_seek_start = $last_seek;
206
207 for ($i = $last_seek; $i < $total_lines; ++$i) {
208
209 // Set the pointer
210 $GLOBALS['bmi_last_seek'] = $i;
211 $file->seek($i);
212
213 // Get the line
214 $line = trim($file->current());
215
216 // Find our declaractions
217 if ($line == ltrim("\/* QUERY START */", '\\')) $query_ended = false;
218 elseif ($line == ltrim("\/* CUSTOM VARS START */", '\\')) $custom_var_started = true;
219 elseif ($line == ltrim("\/* CUSTOM VARS END */", '\\')) $custom_var_started = false;
220 elseif ($line == ltrim("\/* VALUES START */", '\\')) $values_started = true;
221 elseif ($line == ltrim("\/* VALUES END */", '\\')) $values_started = false;
222 elseif ($line == ltrim("\/* QUERY END */", '\\')) $query_ended = true;
223 else {
224
225 if ($custom_var_started === true) {
226
227 $line = rtrim($line, "\n");
228 $the_variable = null;
229 preg_match("/`(.*)`/i", $line, $the_variable);
230 $the_variable = $the_variable[1];
231
232 if (strpos($line, 'REAL_TABLE_NAME') !== false) {
233 $this->table_names_alter[$the_variable] = '';
234 $last_real_table_name = $the_variable;
235 } else if (strpos($line, 'PRE_TABLE_NAME') !== false) {
236 $this->table_names_alter[$last_real_table_name] = $the_variable;
237 }
238
239 }
240
241 if (strlen($line) > 0 || $values_started === true) {
242
243 $line = rtrim($line, "\n");
244
245 if ($values_started === true && $query_ended != true) {
246
247 if ($this->source_domain != $this->new_domain) {
248
249 $this->line_replace($line);
250
251 }
252
253 }
254
255 $query .= $line;
256
257 }
258
259 if ($query_ended === true) {
260
261 $this->run_query($query);
262 $values_started = false;
263 $query_ended = false;
264 $query_exist = true;
265 $query = '';
266 $last_seek = $i;
267
268 if (($last_seek_start + 100) < $i) {
269 break;
270 }
271
272 }
273
274 }
275
276 }
277
278 if ($query_exist === false) {
279 if (file_exists($path)) {
280 gc_collect_cycles();
281 $file = null;
282 @unlink($path);
283 }
284 return true;
285 } else {
286 $file = null;
287 return $last_seek;
288 }
289
290 }
291
292 /**
293 * get_sql_files - Returns all SQL files included in the backup
294 *
295 * @return {array} List of SQL files
296 */
297 public function get_sql_files($first_db = true) {
298
299 if ($first_db == true) {
300 $this->logger->log("Loading file list...", 'INFO');
301 }
302
303 $this->load_file_list();
304
305 if ($first_db == true) {
306 $this->logger->log("File list loaded, memory usage: " . number_format(memory_get_usage() / 1024 / 1024, 2) . " MB", 'SUCCESS');
307 }
308
309 return $this->files;
310
311 }
312
313 /**
314 * restore_tables - Line by line reader and query builder
315 * It uses path provided in __construct to get the files
316 * I.e. it will try to load all .sql files located in the folder
317 * Important, those failes have to be generated with better-backup module
318 *
319 * @return {void}
320 */
321 private function restore_tables() {
322
323 foreach ($this->files as $file_index => $path) {
324
325 $GLOBALS['bmi_last_file'] = $path;
326 $this->current_file = $path;
327
328 $import = 0;
329
330 do {
331
332 $import = $this->restore_by_file($path, $import);
333
334 } while ($import !== true);
335
336 }
337
338 $this->alter_names();
339 $this->queries_ended();
340
341 }
342
343 /**
344 * queries_ended - Runs when all queries imported
345 *
346 * @return @void
347 */
348 public function queries_ended() {
349
350 if ($this->owndb == true) {
351
352 $this->db->close();
353
354 } else {
355
356 $foreign_keys = "SET foreign_key_checks = 1;";
357 $this->run_query($foreign_keys);
358
359 }
360
361 }
362
363 /**
364 * alter_names - Replaces temporary tables with final name
365 *
366 * @return {void}
367 */
368 public function alter_names() {
369
370 $this->logger->log(__('Switching tables to new ones and removing old ones.', 'backup-backup'), 'STEP');
371
372 foreach ($this->table_names_alter as $final_name => $tmp_name) {
373
374 $sql = "DROP TABLE IF EXISTS `$final_name`;";
375 $this->run_query($sql, true);
376
377 $sql = "ALTER TABLE `$tmp_name` RENAME TO `$final_name`;";
378 $this->run_query($sql, true);
379
380 }
381
382 $this->logger->log(__('Tables replaced successfully.', 'backup-backup'), 'SUCCESS');
383
384 }
385
386 /**
387 * replace_domain_string - Replaces the string recursively
388 * It will find all possibilites to replace even the same string multiple times
389 *
390 * @param {string} &$string = '' String pointer declarated somewhere else to save memory
391 * @return {void} - It modifies the root variable
392 */
393 private function replace_domain_string(&$string = '', &$replaced = false) {
394
395 $replacement = $this->is_replacable($string);
396 if ($replacement !== false) {
397
398 $string = str_replace($replacement[0], $replacement[1], $string);
399 $replaced = true;
400
401 if ($this->is_replacable($string) !== false) {
402 $replaced = false;
403 $this->replace_domain_string($string, $replaced);
404 }
405
406 }
407
408 }
409
410 /**
411 * iterate_maybe_serial_string - It checks if the serialized thing was nested
412 * And makes the serialization for each once again to get the right object
413 *
414 * @param {string} &$string String that may be serializable
415 * @return {void} It modifies pointer
416 */
417 private function iterate_maybe_serial_string(&$string) {
418
419 // $serial = $this->is_valid_serialized($string, false);
420 if (is_serialized($string)) {
421
422 // $this->iterate_maybe_serial_string($string);
423 $string = @unserialize($string, ['allowed_classes' => false]);
424 $this->iterate_object($string);
425 $string = @serialize($string);
426
427 } else {
428
429 $this->iterate_object($string);
430
431 }
432
433 }
434
435 /**
436 * is_replacable - Checks if the string is repecable, if it contains the old domain
437 *
438 * @param {string} &$string String that can include old domain name
439 * @return {void} It modifies pointer
440 */
441 private function is_replacable(&$string) {
442
443 if (strpos($string, $this->source_domain) !== false) {
444 return [$this->source_domain, $this->new_domain];
445 }
446
447 if (strpos($string, $this->json_source_domain) !== false) {
448 return [$this->json_source_domain, $this->json_new_domain];
449 }
450
451 if (strpos($string, $this->no_protocol_source_domain) !== false) {
452 return [$this->no_protocol_source_domain, $this->no_protocol_new_domain];
453 }
454
455 if (strpos($string, $this->json_no_protocol_source_domain) !== false) {
456 return [$this->json_no_protocol_source_domain, $this->json_no_protocol_new_domain];
457 }
458
459 if (strpos($string, $this->source_abs) !== false) {
460 return [$this->source_abs, $this->new_abs];
461 }
462
463 if (strpos($string, $this->json_source_abs) !== false) {
464 return [$this->json_source_abs, $this->json_new_abs];
465 }
466
467 return false;
468
469 }
470
471 /**
472 * check_key - Checks object key
473 * It allows to modify object keys in case the domain was used there
474 * E.g. some redirect plugins may store it in the database this way
475 *
476 * @param {string} $key Object key or normal string
477 * @return {string/bool} String if it's replacable bool false if nothing to replace
478 */
479 private function check_key($key) {
480
481 if ($this->is_replacable($key) !== false) {
482
483 $replaced = false;
484 $this->replace_domain_string($key, $replaced);
485 return $key;
486
487 } else {
488
489 return false;
490
491 }
492
493 }
494
495 /**
496 * logIterationError - Exception logged
497 *
498 * @param {string} $e Exception or Throwable
499 * @return @void
500 */
501 public function logIterationError($e) {
502
503 $this->logger->log('Notice while importing: ' . $e->getMessage() . ' @ ' . $e->getLine() . ' (Code: ' . $e->getCode() . ')', 'WARN');
504 // error_log(print_r($e, true));
505
506 }
507
508 /**
509 * throwThisLine - Throws an exception for catch
510 *
511 * @return Throwable
512 */
513 public function throwThisLine($e) {
514
515 // $this->logIterationError($e);
516 throw new \ErrorException($e->getMessage(), 0, $e->getCode(), $e->getFile(), $e->getLine());
517
518 }
519
520 /**
521 * @RECURSIVE MEMORY EATER
522 * iterate_object - Recursive iterator of object/array
523 * It checks every value and key and replaces it where needed
524 *
525 * @param {?object/?mixed} &$object Mixed type, it will detect correct object and return no changed if wrong
526 * @return {void} It modifies object or does nothing on wrong source
527 */
528 private function iterate_object(&$object) {
529
530 if (is_string($object) && is_serialized($object)) {
531
532 $this->iterate_maybe_serial_string($object);
533 return;
534
535 }
536
537 if (!(is_object($object) || is_array($object))) {
538
539 return;
540
541 }
542
543 try {
544
545 foreach ($object as $key => $value) {
546
547 $key_match = $this->check_key($key);
548 if ($key_match !== false) {
549
550 if (is_object($object)) {
551
552 $object->$key_match = $object->$key;
553 unset($object->$key);
554
555 } elseif (is_array($object)) {
556
557 $object[$key_match] = $object[$key];
558 unset($object[$key]);
559
560 }
561
562 $key = $key_match;
563
564 }
565
566 if (($value && !is_string($value)) && (is_object($value) || is_array($value))) {
567
568 try {
569
570 $success = $this->iterate_object($value);
571 if ($success === false) return false;
572
573 if (is_object($object)) $object->$key = $value;
574 elseif (is_array($object)) $object[$key] = $value;
575
576 } catch (\Exception $e) { /* Errors? Probably here! */ $this->throwThisLine($e); return false; }
577 catch (\Throwable $e) { /* Errors? Probably here! */ $this->throwThisLine($e); return false; }
578
579 } else {
580
581 // $subserial = $this->is_valid_serialized($value, false);
582 // $subserial['valid'] === true
583 if (is_serialized($value)) {
584
585 try {
586
587 $success = $this->iterate_object($value);
588 if ($success === false) return false;
589
590 if (is_object($object)) @$object->$key = $value;
591 elseif (is_array($object)) $object[$key] = $value;
592
593 } catch (\Exception $e) { /* Errors? Probably here! */ $this->throwThisLine($e); return false; }
594 catch (\Throwable $e) { /* Errors? Probably here! */ $this->throwThisLine($e); return false; }
595
596 } else {
597
598 if (is_array($value) || is_object($value)) {
599
600 try {
601
602 $success = $this->iterate_object($value);
603 if ($success === false) return false;
604
605 if (is_object($object)) @($object->$key = $value);
606 elseif (is_array($object)) $object[$key] = $value;
607
608 } catch (\Exception $e) { /* Errors? Probably here! */ $this->throwThisLine($e); return false; }
609 catch (\Throwable $e) { /* Errors? Probably here! */ $this->throwThisLine($e); return false; }
610
611
612 } else {
613
614 // Final string to replace (if string / else ignore)
615 if (gettype($value) === 'string') {
616
617 $modified = false;
618 $this->replace_domain_string($value, $modified);
619
620 if ($modified) {
621
622 try {
623
624 if (is_object($object)) @($object->$key = $value);
625 elseif (is_array($object)) $object[$key] = $value;
626
627 } catch (\Exception $e) { /* Errors? Probably here! */ $this->throwThisLine($e); return false; }
628 catch (\Throwable $e) { /* Errors? Probably here! */ $this->throwThisLine($e); return false; }
629
630 }
631
632 }
633
634 }
635
636 }
637
638 }
639
640 }
641
642 } catch (\Exception $e) {
643
644 $this->logIterationError($e);
645 return false;
646
647 } catch (\Throwable $e) {
648
649 $this->logIterationError($e);
650 return false;
651
652 } catch (\ErrorException $e) {
653
654 $this->logIterationError($e);
655 return false;
656
657 }
658
659 }
660
661 /**
662 * line_replace - Value replacer before inserting to database
663 *
664 * @param {string} &$line = '' Pointer to file line which should be replaced if needed
665 * @return {void} It modifies pointer
666 */
667 private function line_replace(&$line = '') {
668
669 // Remove wpdb prepare slashes to make sure the serialized data is correct
670 $copy = $line;
671 $line = stripcslashes($line);
672 $line = str_replace('\0', '', $line);
673
674 $serial = $this->is_valid_serialized($line);
675
676 if ($serial['valid'] === true) {
677
678 // Copy no needed here slashes will be added by wpdb
679 unset($copy);
680
681 // Iterate entire object replace keys and values
682 $this->iterate_object($line);
683
684 // Serialize it again as it was serialized before
685 $line = serialize($line);
686
687 // Make the string SQL friendly, as it was before
688 if ($serial['quotes']) $line = $this->wpdb->prepare('%s', $line);
689 if ($serial['comma']) $line .= ',';
690
691 } else {
692
693 // Make sure the line is exactly the same as in file
694 $line = $copy;
695
696 // Unset copy to free space
697 unset($copy);
698
699 // Find and replace domain
700 $replaced = false;
701 $this->replace_domain_string($line, $replaced);
702
703 }
704
705 }
706
707 /**
708 * run_query - Query runner, it runs the final query after built and replacements
709 *
710 * @param {string} &$query Pointer to small or huge query
711 * @return {void} Returns nothing, and the &$query is not removed here
712 *
713 * Notice: Please unset($query) after this method otherwise we will lose memory
714 */
715 private function run_query(&$query, $isAlter = false) {
716
717 $this->xi++;
718 if ($this->owndb === true) $this->db->query($query);
719 else $this->wpdb->query($query);
720
721 if ($this->xi % 5 == 0) {
722
723 $from_start = number_format(microtime(true) - $this->init_start, 4);
724 $filename = basename($this->current_file);
725
726 $from_last = number_format(microtime(true) - $this->last_finish, 4);
727 $this->last_finish = microtime(true);
728
729 $queries_so_far = $this->xi;
730 $queries_total = $this->total_queries;
731 $queries_progress = number_format(($queries_so_far / $queries_total) * 100, 2);
732
733 $halfProgress = true;
734 if (isset($this->conversionStats['total_queries'])) {
735 $halfProgress = false;
736 }
737
738 if (!$isAlter) {
739 if ($halfProgress) {
740 $this->logger->progress(50 + ($queries_progress / 2));
741 } else {
742 $this->logger->progress(75 + ($queries_progress / 4));
743 }
744 }
745
746 $match_output = [];
747 preg_match('/(.*)_(\d+)\_of\_(\d+)/', $filename, $match_output); // 1 (table name), 2 (part), 3 (total parts)
748
749 $size = "";
750
751 if (file_exists($this->current_file)) {
752 $size = ", size: " . BMP::humanSize(filesize($this->current_file));
753 }
754
755 if (!$isAlter) {
756 if (sizeof($match_output) >= 3) {
757
758 $this->logger->log("Finished " . $queries_so_far . "/" . $queries_total . " (" . $queries_progress . "%)" . " for " . $match_output[1] . " table (part: " . $match_output[2] . "/" . $match_output[3] . $size . ").", 'INFO');
759
760 } else {
761
762 $this->logger->log("Finished " . $queries_so_far . "/" . $queries_total . " (" . $queries_progress . "%)" . " for " . $filename . " table (part: dynamic)", 'INFO');
763
764 }
765 }
766
767 }
768
769 }
770
771 /**
772 * load_file_list - Loads files to restore
773 *
774 * @return {array} list list of restorable files
775 */
776 private function load_file_list() {
777
778 $files = scandir($this->storage);
779 foreach ($files as $index => $filename) {
780
781 if (in_array($filename, ['.', '..'])) continue;
782
783 $filePath = $this->storage . DIRECTORY_SEPARATOR . $filename;
784 if (is_dir($filePath)) {
785
786 $splitedFiles = scandir($filePath);
787 foreach ($splitedFiles as $splitedIndex => $splitedFilename) {
788
789 if (in_array($splitedFilename, ['.', '..', 'bmi_converted_completed_tables'])) continue;
790 if (substr($splitedFilename, -4) != '.sql') continue;
791
792 $this->files[] = $this->storage . DIRECTORY_SEPARATOR . $filename . DIRECTORY_SEPARATOR . $splitedFilename;
793
794 }
795
796 continue;
797
798 }
799
800 if (substr($filename, -4) != '.sql') continue;
801 $this->files[] = $this->storage . DIRECTORY_SEPARATOR . $filename;
802
803 }
804
805 $this->sortFiles();
806
807 return $this->files;
808
809 }
810
811 private function sortFiles() {
812
813 $files = $this->files;
814
815 $tmp_files = [];
816 for ($i = 0; $i < sizeof($files); ++$i) {
817
818 $file = $files[$i];
819
820 $name = [];
821 preg_match('/^(.*)\.sql$/', basename($file), $name); // 1
822
823 $order = [];
824 $name = $name[1];
825 preg_match('/^(.*)\_((\d+)\_of\_(\d+))$/', $name, $order); // 1, 3, 4
826
827 $tablename = $name;
828
829 if (sizeof($order) >= 3) {
830 $tablename = $order[1];
831 }
832
833 if (!isset($tmp_files[$tablename])) {
834 $tmp_files[$tablename] = [];
835 }
836
837 $tmp_files[$tablename][] = $file;
838
839 }
840
841 ksort($tmp_files);
842 $files = [];
843
844 foreach ($tmp_files as $table => $sqlfiles) {
845
846 usort($sqlfiles, function ($a, $b) {
847
848 $nameA = [];
849 preg_match('/^(.*)\.sql$/', basename($a), $nameA); // 1
850
851 $nameB = [];
852 preg_match('/^(.*)\.sql$/', basename($b), $nameB); // 1
853
854 $nameA = $nameA[1];
855 $nameB = $nameB[1];
856
857 $orderA = [];
858 preg_match('/^(.*)\_((\d+)\_of\_(\d+))$/', $nameA, $orderA); // 1, 3, 4
859
860 $orderB = [];
861 preg_match('/^(.*)\_((\d+)\_of\_(\d+))$/', $nameB, $orderB); // 1, 3, 4
862
863 if (sizeof($orderA) >= 3 && sizeof($orderB) >= 3) {
864
865 if (intval($orderA[3]) > intval($orderB[3])) {
866
867 return 1;
868
869 } else if (intval($orderA[3]) < intval($orderB[3])) {
870
871 return -1;
872
873 } else {
874
875 return 0;
876
877 }
878
879 } else {
880
881 return 1;
882
883 }
884
885 });
886
887 for ($i = 0; $i < sizeof($sqlfiles); ++$i) {
888
889 $files[] = $sqlfiles[$i];
890
891 }
892
893 }
894
895 unset($tmp_files);
896
897 $this->files = $files;
898 return $this->files;
899
900 }
901
902 /**
903 * is_valid_serialized - Checks if string is serialized and if it's valid serialization
904 *
905 * @param {mixed} $data = '' Maybe serialized string
906 * @return {array} If the results is valid, if comma was removed, if quotes were removed
907 */
908 private function is_valid_serialized(&$data = '', $sql = true) {
909
910 $commaRemoved = false;
911 $quotesRemoved = false;
912
913 if ($sql) {
914
915 $length = strlen($data);
916 $is_last = $data[$length - 1] == "," ? false : true;
917
918 if (!$is_last) {
919
920 $commaRemoved = true;
921 $data = substr($data, 0, -1);
922
923 }
924
925 }
926
927 if (is_numeric($data)) {
928
929 if ($commaRemoved) $data .= ',';
930 return ['valid' => false, 'comma' => false, 'quotes' => false];
931
932 }
933
934 if (!is_string($data)) {
935
936 if ($commaRemoved) $data .= ',';
937 return ['valid' => false, 'comma' => false, 'quotes' => false];
938
939 }
940
941 if (!$data) {
942
943 if ($commaRemoved) $data .= ',';
944 return ['valid' => false, 'comma' => false, 'quotes' => false];
945
946 }
947
948 if ($data[0] == "'") {
949
950 $quotesRemoved = true;
951 $data = trim($data, "'");
952
953 }
954
955 if (is_serialized($data)) {
956
957 $data = preg_replace('!s:(\d+):"(.*?)";!e', "'s:'.strlen('$2').':\"$2\";'", $data);
958 $unserialized = @unserialize($data, ['allowed_classes' => false]);
959
960 } else {
961
962 $unserialized = false;
963
964 }
965
966 $is_valid = ($data == 'b:0;' || ($unserialized !== false)) ? true : false;
967
968 if (!$is_valid) {
969
970 if ($quotesRemoved) {
971 $data = $this->wpdb->prepare('%s', $data);
972 }
973
974 if ($commaRemoved) $data .= ',';
975
976 } else {
977
978 $data = $unserialized;
979 unset($unserialized);
980
981 }
982
983 return ['valid' => $is_valid, 'comma' => $commaRemoved, 'quotes' => $quotesRemoved];
984
985 }
986
987 }
988