export
3 weeks ago
better-backup-v3.php
3 weeks ago
better-backup.php
3 weeks ago
better-restore.php
3 weeks ago
even-better-restore-v3.php
3 weeks ago
even-better-restore-v4.php
3 weeks ago
interface-search-replace-repository.php
3 weeks ago
manager.php
3 weeks ago
search-replace-processor.php
3 weeks ago
search-replace-repository.php
3 weeks ago
search-replace-stack-based.php
3 weeks ago
search-replace-v2.php
3 weeks ago
search-replace.php
3 weeks ago
smart-sort.php
3 weeks ago
manager.php
290 lines
| 1 | <?php |
| 2 | |
| 3 | // Namespace |
| 4 | namespace BMI\Plugin\Database; |
| 5 | |
| 6 | // Use |
| 7 | use BMI\Plugin\BMI_Logger AS Logger; |
| 8 | use BMI\Plugin\Progress\BMI_ZipProgress AS Progress; |
| 9 | use BMI\Plugin\Backup_Migration_Plugin as BMP; |
| 10 | |
| 11 | // Exit on direct access |
| 12 | if (!defined('ABSPATH')) exit; |
| 13 | |
| 14 | /** |
| 15 | * BMI_Database |
| 16 | */ |
| 17 | class BMI_Database { |
| 18 | |
| 19 | function __construct($host, $user, $pass, $name) { |
| 20 | |
| 21 | $this->host = $host; |
| 22 | $this->user = $user; |
| 23 | $this->pass = $pass; |
| 24 | $this->name = $name; |
| 25 | |
| 26 | } |
| 27 | |
| 28 | public function exportDatabase($backup_name = false) { |
| 29 | |
| 30 | $mysqli = new \mysqli($this->host, $this->user, $this->pass, $this->name); |
| 31 | $mysqli->select_db($this->name); |
| 32 | $mysqli->query("SET NAMES 'utf8'"); |
| 33 | |
| 34 | $file = fopen(BMI_TMP . DIRECTORY_SEPARATOR . $backup_name, 'w'); |
| 35 | $queryTables = $mysqli->query('SHOW TABLES'); |
| 36 | while ($row = $queryTables->fetch_row()) $target_tables[] = $row[0]; |
| 37 | |
| 38 | fwrite($file, "SET SQL_MODE = \"\";\r\n"); |
| 39 | fwrite($file, "SET time_zone = \"+00:00\";\r\n\r\n\r\n"); |
| 40 | fwrite($file, "/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\r\n"); |
| 41 | fwrite($file, "/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;\r\n"); |
| 42 | fwrite($file, "/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;\r\n"); |
| 43 | fwrite($file, "SET SQL_MODE = \"\";\r\n"); |
| 44 | fwrite($file, "/*!40101 SET NAMES utf8 */;\r\n"); |
| 45 | fwrite($file, "--\r\n-- Database: `" . $this->name . "`\r\n"); |
| 46 | fwrite($file, "--\r\n\r\n\r\n"); |
| 47 | |
| 48 | foreach ($target_tables as $table) { |
| 49 | |
| 50 | if (empty($table)) { continue; } |
| 51 | |
| 52 | $chunks_req = $mysqli->query('SELECT count(1) FROM `'.$table.'`'); |
| 53 | $chunks_res = mysqli_fetch_array($chunks_req); |
| 54 | $chunks = intval($chunks_res[0]); |
| 55 | |
| 56 | $res = $mysqli->query('SHOW CREATE TABLE `' . $table . '`'); |
| 57 | $TableMLine = $res->fetch_row(); |
| 58 | fwrite($file, "\n\n".$TableMLine[1].";\n\n"); |
| 59 | $TableMLine[1] = str_ireplace('CREATE TABLE `','CREATE TABLE IF NOT EXISTS `', $TableMLine[1]); |
| 60 | |
| 61 | if ($chunks > 0) { |
| 62 | $g = 0; |
| 63 | while($g < $chunks) { |
| 64 | if (($g + 100) > $chunks) $limit_str = 'LIMIT '. $g . ',1000'; |
| 65 | else $limit_str = 'LIMIT '. $g . ',100'; |
| 66 | $this->saveDatabaseFields($mysqli, $table, $file, $limit_str); |
| 67 | $g += 100; |
| 68 | } |
| 69 | } else $this->saveDatabaseFields($mysqli, $table, $file); |
| 70 | |
| 71 | fwrite($file, "\n\n\n"); |
| 72 | |
| 73 | } |
| 74 | |
| 75 | fwrite($file, "\r\n\r\n/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\r\n"); |
| 76 | fwrite($file, "/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\r\n"); |
| 77 | fwrite($file, "/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;"); |
| 78 | |
| 79 | $mysqli->close(); |
| 80 | $backup_name = $backup_name ? $backup_name : $this->name.'___('.date('H-i-s').'_'.date('d-m-Y').').sql'; |
| 81 | fclose($file); |
| 82 | |
| 83 | } |
| 84 | |
| 85 | public function saveDatabaseFields(&$mysqli, $table, &$file, $limit = '') { |
| 86 | |
| 87 | $result = $mysqli->query('SELECT * FROM ' . BMP::escapeSQLIDentifier($table) . ' ' . $limit); |
| 88 | $fields_amount = $result->field_count; |
| 89 | $rows_num = $mysqli->affected_rows; |
| 90 | for ($i = 0, $st_counter = 0; $i < $fields_amount; $i++, $st_counter = 0) { |
| 91 | while ($row = $result->fetch_row()) { |
| 92 | |
| 93 | if ($st_counter % 100 == 0 || $st_counter == 0) { |
| 94 | fwrite($file, "\nINSERT INTO " . BMP::escapeSQLIDentifier($table) . " VALUES"); |
| 95 | } |
| 96 | |
| 97 | fwrite($file, "\n("); |
| 98 | for ($j = 0; $j < $fields_amount; $j++) { |
| 99 | $row[$j] = str_replace("\n","\\n", addslashes($row[$j])); |
| 100 | |
| 101 | if (isset($row[$j])) fwrite($file, '"'.$row[$j].'"'); |
| 102 | else fwrite($file, '""'); |
| 103 | |
| 104 | if ($j<($fields_amount-1)) fwrite($file, ','); |
| 105 | } |
| 106 | |
| 107 | fwrite($file, ")"); |
| 108 | if ((($st_counter+1)%100==0 && $st_counter!=0) || $st_counter+1==$rows_num) { |
| 109 | |
| 110 | fwrite($file, ";"); |
| 111 | |
| 112 | } else fwrite($file, ","); |
| 113 | |
| 114 | $st_counter = $st_counter + 1; |
| 115 | |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | } |
| 120 | |
| 121 | public function recursiveReplace(&$line, &$at, &$from, &$to) { |
| 122 | |
| 123 | $end = -1; |
| 124 | for ($i = $at; $i > 3; --$i) { |
| 125 | $str = $line[$i-2] . $line[$i-1] . $line[$i]; |
| 126 | if ($str === ':\"') { |
| 127 | $end = $i; |
| 128 | break; |
| 129 | } |
| 130 | unset($str); |
| 131 | } |
| 132 | |
| 133 | $start = -1; $semi = false; |
| 134 | for ($i = $end; $i > 0; --$i) { |
| 135 | $str = $line[$i] . $line[$i+1] . $line[$i+2]; |
| 136 | if ($str === ';s:') { |
| 137 | $semi = true; |
| 138 | $start = $i + 3; |
| 139 | break; |
| 140 | } |
| 141 | if ($str === '{s:') { |
| 142 | $start = $i + 3; |
| 143 | break; |
| 144 | } |
| 145 | unset($str); |
| 146 | } |
| 147 | |
| 148 | if ($start == -1 || $end == -1) return $line; |
| 149 | $diff = $end - $start; $eof = -1; |
| 150 | $len = intval(substr($line, $start, $diff)); |
| 151 | $maxend = strlen($line); |
| 152 | if ($maxend > ($end + 3 + $len + 6)) { |
| 153 | $maxend = ($end + 3 + $len + 6); |
| 154 | } |
| 155 | |
| 156 | for ($j = ($end + 3); $j < $maxend; ++$j) { |
| 157 | $str = $line[$j] . $line[$j+1] . $line[$j+2]; |
| 158 | if ($str === '\";') { |
| 159 | $eof = $j; |
| 160 | break; |
| 161 | } |
| 162 | unset($str); |
| 163 | } |
| 164 | |
| 165 | if ($eof == -1) { |
| 166 | for ($i = $maxend; $i > ($end + 3); --$i) { |
| 167 | $str = $line[$i] . $line[$i+1] . $line[$i+2];; |
| 168 | if ($str === '\";') { |
| 169 | $eof = $i; |
| 170 | break; |
| 171 | } |
| 172 | unset($str); |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | if ($eof == -1 || $eof < $end || $eof < $start) return $line; |
| 177 | $should = abs(strlen($from) - strlen($to)); |
| 178 | $toreplace = substr($line, $start - 3, (($eof + 6) - $start)); |
| 179 | $content = str_replace($from, $to, substr($toreplace, $diff + 6, -3)); |
| 180 | if ($semi) $newreplace = ';s:' . strlen($content) . ':\"' . $content . '\";'; |
| 181 | else $newreplace = '{s:' . strlen($content) . ':\"' . $content . '\";'; |
| 182 | $line = str_replace($toreplace, $newreplace, $line); |
| 183 | // if (strlen($len >= $content)) { |
| 184 | |
| 185 | // error_log($start . ' - ' . $end . ' - ' . $eof . ' - ' . $len . '['.strlen($toreplace).']' . ' - ' . '['.strlen($newreplace).']'); |
| 186 | // error_log(strlen($toreplace) . ' -> ' . strlen($newreplace)); |
| 187 | // error_log($toreplace); |
| 188 | // error_log($newreplace); |
| 189 | // error_log(''); |
| 190 | // } |
| 191 | |
| 192 | // Do the same for every occurence |
| 193 | $d = strpos($line, $from); |
| 194 | if ($d !== false) $line = $this->recursiveReplace($line, $d, $from, $to); |
| 195 | |
| 196 | return $line; |
| 197 | } |
| 198 | |
| 199 | public function replaceInLine(&$line) { |
| 200 | |
| 201 | if (strpos($line, ':\"') !== false) { |
| 202 | |
| 203 | $d = strpos($line, $this->old_domain); |
| 204 | if ($d !== false) $this->recursiveReplace($line, $d, $this->old_domain, $this->new_domain); |
| 205 | |
| 206 | $b = strpos($line, $this->abs); |
| 207 | if ($b !== false) $this->recursiveReplace($line, $b, $this->abs, $this->newabs); |
| 208 | |
| 209 | } else { |
| 210 | |
| 211 | // Replace in case user had this wrongly defined in wp-config. |
| 212 | // Of course, those two ifs will take a lot of memory... |
| 213 | if (substr($line, 0, 16) === '("1","siteurl","') $line = '("1","siteurl","' . $this->new_domain . '","yes"),'; |
| 214 | if (substr($line, 0, 13) === '("2","home","') $line = '("2","home","' . $this->new_domain . '","yes"),'; |
| 215 | |
| 216 | $line = str_replace($this->old_domain, $this->new_domain, $line); |
| 217 | $line = str_replace($this->abs, $this->newabs, $line); |
| 218 | |
| 219 | } |
| 220 | |
| 221 | return $line; |
| 222 | } |
| 223 | |
| 224 | public function importDatabase($file, $old_domain, $new_domain, $abs, $newabs, $newprefix, $siteurl, $home) { |
| 225 | |
| 226 | if (BMP::isFunctionEnabled('ignore_user_abort')) @ignore_user_abort(true); |
| 227 | if (BMP::isFunctionEnabled('set_time_limit')) @set_time_limit(16000); |
| 228 | if (BMP::isFunctionEnabled('ini_set')) { |
| 229 | @ini_set('max_execution_time', '259200'); |
| 230 | @ini_set('max_input_time', '259200'); |
| 231 | } |
| 232 | |
| 233 | $SQL_CONTENT = $file; |
| 234 | $mysqli = new \mysqli($this->host, $this->user, $this->pass, $this->name); |
| 235 | |
| 236 | if (mysqli_connect_errno()) { |
| 237 | Logger::error(__("Failed to connect to MySQL: ", 'backup-backup') . mysqli_connect_error()); |
| 238 | return __("Importing failed - cannot connect to the database.", 'backup-backup'); |
| 239 | } |
| 240 | |
| 241 | $this->old_domain = $old_domain; |
| 242 | $this->new_domain = $new_domain; |
| 243 | $this->escaped_old = str_replace('/', '\\\/', $old_domain); |
| 244 | $this->escaped_new = str_replace('/', '\\\/', $new_domain); |
| 245 | |
| 246 | $this->abs = $abs; |
| 247 | $this->newabs = $newabs; |
| 248 | $this->e_abs_old = str_replace('/', '\\\/', $abs); |
| 249 | $this->e_abs_new = str_replace('/', '\\\/', $newabs); |
| 250 | |
| 251 | $handle = fopen($file, 'r'); |
| 252 | if ($handle) { |
| 253 | $templine = ''; |
| 254 | $qyr = $mysqli->query('SET foreign_key_checks = 0'); |
| 255 | |
| 256 | while (($line = fgets($handle)) !== false) { |
| 257 | preg_match_all("/\nCREATE TABLE(.*?)\`(.*?)\`/si", "\n" . $line, $target_tables); |
| 258 | foreach ($target_tables[2] as $table) $mysqli->query('DROP TABLE IF EXISTS `' . $table . '`'); |
| 259 | if (substr($line, 0, 2) != '--' && $line != '') { |
| 260 | $this->replaceInLine($line); |
| 261 | $templine .= $line; |
| 262 | if (substr(trim($line), -1, 1) == ';') { |
| 263 | if (!$mysqli->query($templine)) { |
| 264 | Logger::error('Error performing query ' . $templine . ': ' . $mysqli->error); |
| 265 | } |
| 266 | $templine = ''; |
| 267 | $line = ''; |
| 268 | } |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | fclose($handle); |
| 273 | } else { |
| 274 | Logger::error(__("Cannot open SQL file...", 'backup-backup')); |
| 275 | return __("Importing failed - Cannot open SQL file...", 'backup-backup'); |
| 276 | } |
| 277 | |
| 278 | $qyr = $mysqli->query('SET foreign_key_checks = 1'); |
| 279 | $mysqli->query("SET NAMES 'utf8'"); |
| 280 | |
| 281 | // $mysqli->query("UPDATE `".$new_prefix."options` SET option_value = '" . $siteurl. "' WHERE option_name = 'siteurl'"); |
| 282 | // $mysqli->query("UPDATE `".$new_prefix."options` SET option_value = '" . $home. "' WHERE option_name = 'home'"); |
| 283 | |
| 284 | $mysqli->close(); |
| 285 | return __("Importing finished. Now, Delete the import file.", 'backup-backup'); |
| 286 | |
| 287 | } |
| 288 | |
| 289 | } |
| 290 |