PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 1.3.6
Backup Migration v1.3.6
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 / zipper / src / zip.php
backup-backup / includes / zipper / src Last commit date
zip.php 2 years ago
zip.php
922 lines
1 <?php
2
3 namespace BMI\Plugin\Zipper;
4
5 use BMI\Plugin\Backup_Migration_Plugin as BMP;
6 use BMI\Plugin\BMI_Logger as Logger;
7 use BMI\Plugin\Dashboard as Dashboard;
8 use BMI\Plugin\Database\BMI_Database as Database;
9 use BMI\Plugin\Database\BMI_Database_Exporter as BetterDatabaseExport;
10 use BMI\Plugin\Progress\BMI_ZipProgress as Progress;
11 use BMI\Plugin\Heart\BMI_Backup_Heart as Bypasser;
12
13 class Zip {
14 protected $lib;
15 protected $org_files;
16 protected $new_file_path;
17 protected $new_file_name;
18 protected $backupname;
19 protected $zip_progress;
20
21 protected $extr_file;
22 protected $extr_dirc;
23 protected $start_zip;
24
25 public function __construct() {
26 $this->lib = 0;
27 $this->extr_file = 0;
28 $this->new_file_path = 0;
29 $this->org_files = [];
30 }
31
32 public function zip_start($file_path, $files = [], $name = '', &$zip_progress = null, $start = null) {
33
34 // save the new file path
35 $this->new_file_path = $file_path;
36 $this->backupname = $name;
37 $this->zip_progress = $zip_progress;
38 $this->start_zip = $start;
39
40 if (sizeof($files) > 0) {
41 $this->org_files = $files;
42 }
43
44 // Some php installations doesn't have the ZipArchive
45 // So in this case we'll use another lib called PclZip
46 if (class_exists("ZipArchive") || class_exists("\ZipArchive")) {
47 $this->lib = 1;
48 } else {
49 $this->lib = 2;
50 }
51
52 return true;
53
54 }
55
56 public function return_bytes($val) {
57 $val = trim($val);
58 $last = strtolower($val[strlen($val) - 1]);
59 $val = substr($val, 0, -1);
60
61 switch ($last) {
62 // The 'G' modifier is available since PHP 5.1.0
63 case 'g':
64 $val *= 1024;
65 // no break
66 case 'm':
67 $val *= 1024;
68 // no break
69 case 'k':
70 $val *= 1024;
71 }
72
73 return $val;
74 }
75
76 public function zip_failed($error) {
77 error_log(print_r($error, true));
78 Logger::error(__("There was an error during backup (packing)...", 'backup-backup'));
79 Logger::error($error);
80
81 if ($this->zip_progress != null) {
82 $this->zip_progress->log(__("Issues during backup (packing)...", 'backup-backup'), 'ERROR');
83 $this->zip_progress->log(print_r($error, true), 'ERROR');
84 }
85 }
86
87 public function restore_failed($error) {
88 Logger::error(__("There was an error during restore process (extracting)...", 'backup-backup'));
89 Logger::error($error);
90
91 if ($this->zip_progress != null) {
92 $this->zip_progress->log(__("Issues during restore process (extracting)...", 'backup-backup'), 'ERROR');
93 $this->zip_progress->log($error, 'ERROR');
94 }
95 }
96
97 public function zip_add($in) {
98
99 // Just to make sure.. if the user haven't called the earlier method
100 if ($this->lib === 0 || $this->new_file_path === 0) {
101 throw new \Exception("PHP-ZIP: must call zip_start before zip_add");
102 }
103
104 // Push file
105 array_push($this->org_files, $in);
106
107 // Return
108 return true;
109 }
110
111 public function createDatabaseDump($dbbackupname, $better_database_files_dir, &$database_file, $database_file_dir, $dbBackupEngine = 'v4') {
112
113 if (Dashboard\bmi_get_config('BACKUP:DATABASE') == 'true') {
114
115 if (Dashboard\bmi_get_config('OTHER:BACKUP:DB:SINGLE:FILE') == 'true') {
116
117 // Require Database Manager
118 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'manager.php';
119
120 // Logs
121 $this->zip_progress->log(__("Making single-file database backup (using deprecated engine, due to used settings)", 'backup-backup'), 'STEP');
122
123 // Get database dump
124 $databaser = new Database(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
125 $databaser->exportDatabase($dbbackupname);
126
127 // Fix for newer version
128 $this->db_exporter_queries = 0;
129 $this->zip_progress->total_queries = 0;
130 $this->db_exporter_files = [];
131
132 $this->dbDumped = true;
133 $this->zip_progress->log(__("Database size: ", 'backup-backup') . BMP::humanSize(filesize($database_file)), 'INFO');
134
135 } else {
136
137 if ($dbBackupEngine == 'v4') {
138
139 // Require Database Manager
140 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'better-backup-v3.php';
141
142 // Get database dump
143 $this->zip_progress->log(__("Making database backup (using v3 engine, requires at least v1.2.2 to restore)", 'backup-backup'), 'STEP');
144
145 } else {
146
147 // Require Database Manager
148 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'better-backup.php';
149
150 // Get database dump
151 $this->zip_progress->log(__("Making database backup (using v2 engine, requires at least v1.1.0 to restore)", 'backup-backup'), 'STEP');
152
153 }
154
155 $this->zip_progress->log(__("Iterating database...", 'backup-backup'), 'INFO');
156
157 if (!is_dir($better_database_files_dir)) @mkdir($better_database_files_dir, 0755, true);
158 $db_exporter = new BetterDatabaseExport($better_database_files_dir, $this->zip_progress);
159
160 $this->zip_progress->log('Exporting database via zip.php', 'VERBOSE');
161 $db_exporter->export();
162 $this->db_exporter_files = $db_exporter->files;
163 $this->db_exporter_queries = $db_exporter->total_queries;
164
165 $this->zip_progress->total_queries = $this->db_exporter_queries;
166
167 $this->dbDumped = true;
168 $this->zip_progress->log(__("Database backup finished", 'backup-backup'), 'SUCCESS');
169
170 }
171
172 } else {
173
174 $this->dbDumped = false;
175 $this->zip_progress->log(__("Omitting database backup (due to settings)...", 'backup-backup'), 'WARN');
176 $database_file = false;
177 $this->db_exporter_files = [];
178 $this->db_exporter_queries = 0;
179 $this->zip_progress->total_queries = 0;
180
181 }
182
183 }
184
185 public function zip_end($force_lib = false, $cron = false) {
186
187 // v4 for new one, v3 for old one
188 $dbBackupEngine = 'v4';
189
190 // Try to set limit
191 $this->zip_progress->log(__("Smart memory calculation...", 'backup-backup'), 'STEP');
192 if ((intval($this->return_bytes(ini_get('memory_limit'))) / 1024 / 1024) < 384) @ini_set('memory_limit', '384M');
193 if (defined('WP_MAX_MEMORY_LIMIT')) $maxwp = WP_MAX_MEMORY_LIMIT;
194 else $maxwp = '1M';
195
196 $memory_limit = (intval($this->return_bytes(ini_get('memory_limit'))) / 1024 / 1024);
197 $maxwp = (intval($this->return_bytes($maxwp)) / 1024 / 1024);
198
199 if ($maxwp > $memory_limit) $memory_limit = $maxwp;
200 $this->zip_progress->log(str_replace('%s', $memory_limit, __("There is %s MBs of memory to use", 'backup-backup')), 'INFO');
201 $this->zip_progress->log(str_replace('%s', $maxwp, __("WordPress memory limit: %s MBs", 'backup-backup')), 'INFO');
202 $safe_limit = intval($memory_limit / 4);
203 if ($safe_limit > 64) $safe_limit = 64;
204 if ($memory_limit === 384) $safe_limit = 96;
205 if ($memory_limit >= 512) $safe_limit = 128;
206 if ($memory_limit >= 1024) $safe_limit = 256;
207
208 // $real_memory = intval(memory_get_usage() * 0.9 / 1024 / 1024);
209 // if ($real_memory < $safe_limit) $safe_limit = $real_memory;
210 $safe_limit = intval($safe_limit * 0.9);
211
212 $this->zip_progress->log(str_replace('%s', $safe_limit, __("Setting the safe limit to %s MB", 'backup-backup')), 'SUCCESS');
213
214 $abs = BMP::fixSlashes(ABSPATH) . DIRECTORY_SEPARATOR;
215
216 $dbbackupname = 'bmi_database_backup.sql';
217 $database_file = BMP::fixSlashes(BMI_TMP . DIRECTORY_SEPARATOR . $dbbackupname);
218 $database_file_dir = BMP::fixSlashes((dirname($database_file))) . DIRECTORY_SEPARATOR;
219 $better_database_files_dir = $database_file_dir . 'db_tables';
220
221 // force usage of specific lib (for testing purposes)
222 if ($force_lib === 2) {
223
224 $this->lib = 2;
225
226 } elseif ($force_lib === 1) {
227
228 $this->lib = 1;
229
230 }
231
232 $this->dbDumped = false;
233 $this->db_exporter_queries = 0;
234 $this->zip_progress->total_queries = 0;
235 $this->db_exporter_files = [];
236
237 // just to make sure.. if the user haven't called the earlier method
238 if ($this->lib === 0 || $this->new_file_path === 0) {
239 throw new \Exception('PHP-ZIP: zip_start and zip_add haven\'t been called yet');
240 }
241
242 // using zipArchive class
243 // if ($this->lib === 1) {
244 //
245 // // Create DB Dump
246 // $this->createDatabaseDump($dbbackupname, $better_database_files_dir, $database_file, $database_file_dir);
247 //
248 // // All files
249 // $max = sizeof($this->org_files);
250 // $this->zip_progress->log(__("Making archive", 'backup-backup'), 'STEP');
251 // $this->zip_progress->log(__("Compressing...", 'backup-backup'), 'INFO');
252 //
253 // // Verbose
254 // $this->zip_progress->log(__("Using Zlib to create Backup", 'backup-backup'));
255 //
256 // $lib = new \ZipArchive();
257 // if (!$lib->open($this->new_file_path, \ZipArchive::CREATE)) {
258 // throw new \Exception('PHP-ZIP: Permission Denied or zlib can\'t be found');
259 // }
260 //
261 // // Add each file
262 // for ($i = 0; $i < $max; $i++) {
263 // $file = $this->org_files[$i];
264 // $zippath = substr($file, strlen($abs));
265 // $lib->addFile($file, 'wordpress' . DIRECTORY_SEPARATOR . $zippath);
266 //
267 // if ($i % 100 === 0) {
268 // if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.abort')) {
269 // break;
270 // }
271 // $this->zip_progress->progress($i + 1 . '/' . $max);
272 // }
273 //
274 // if (($i + 1) % 500 === 0 || $i == 0) {
275 // if (($i + 1) < $max) {
276 // $this->zip_progress->log((__("Milestone: ", 'backup-backup') . ($i + 1) . '/' . $max), 'info');
277 // }
278 // }
279 // }
280 //
281 // if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.abort')) {
282 //
283 // // close the archive
284 // $lib->close();
285 // } else {
286 // $this->zip_progress->log((__("Milestone: ", 'backup-backup') . $max . '/' . $max), 'info');
287 // $this->zip_progress->log(__("Compressed ", 'backup-backup') . $max . __(" files", 'backup-backup'), 'SUCCESS');
288 //
289 // // Log time of ZIP Process
290 // $this->zip_progress->log(__("Archiving of ", 'backup-backup') . $max . __(" files took: ", 'backup-backup') . number_format(microtime(true) - $this->start_zip, 2) . 's');
291 //
292 // $this->zip_progress->log(__("Finalizing backup", 'backup-backup'), 'STEP');
293 // $this->zip_progress->log(__("Adding manifest...", 'backup-backup'), 'INFO');
294 // $this->zip_progress->log(__("Closing files and archives", 'backup-backup'), 'STEP');
295 // $this->zip_progress->log('#001', 'END-CODE');
296 //
297 // $this->zip_progress->end();
298 // $logs = file_get_contents(BMI_BACKUPS . DIRECTORY_SEPARATOR . 'latest.log');
299 // $this->zip_progress->start(true);
300 //
301 // if ($database_file !== false) {
302 // if (Dashboard\bmi_get_config('OTHER:BACKUP:DB:SINGLE:FILE') == 'true') {
303 // if (file_exists($database_file)) {
304 // $lib->addFile($database_file, 'bmi_database_backup.sql');
305 // }
306 // } else {
307 // if ($db_exporter_files && sizeof($db_exporter_files) > 0) {
308 // for ($i = 0; $i < sizeof($db_exporter_files); ++$i) {
309 // $lib->addFile($db_exporter_files[$i], 'db_tables' . DIRECTORY_SEPARATOR . basename($db_exporter_files[$i]));
310 // }
311 // }
312 // }
313 // }
314 //
315 // $lib->addFromString('bmi_backup_manifest.json', $this->zip_progress->createManifest($dbBackupEngine));
316 // $lib->addFromString('bmi_logs_this_backup.log', $logs);
317 // $this->zip_progress->progress($max . '/' . $max);
318 //
319 // // close the archive
320 // $lib->close();
321 // }
322 // }
323
324 // using PclZip
325 if ($this->lib === 2) {
326
327 // All files
328 $max = sizeof($this->org_files);
329
330 // Verbose
331 $legacy = BMI_LEGACY_VERSION;
332 if ($legacy) $legacy = BMI_LEGACY_HARD_VERSION;
333 $this->zip_progress->log(__("Using PclZip module to create the backup", 'backup-backup'), 'INFO');
334 if (!BMI_LEGACY_VERSION) {
335 $this->zip_progress->log(__("Legacy setting: Using server-sided script and cURL based loop for better capabilities", 'backup-backup'), 'INFO');
336 } elseif (!BMI_LEGACY_HARD_VERSION) {
337 $this->zip_progress->log(__("Legacy setting: Using user browser as middleware for full capabilities", 'backup-backup'), 'INFO');
338 } else {
339
340 $this->zip_progress->log(__("Legacy setting: Using default modules depending on user server", 'backup-backup'), 'INFO');
341
342 // Create DB Dump
343 $this->createDatabaseDump($dbbackupname, $better_database_files_dir, $database_file, $database_file_dir, $dbBackupEngine);
344
345 $this->zip_progress->log(__("Making archive", 'backup-backup'), 'STEP');
346 $this->zip_progress->log(__("Compressing...", 'backup-backup'), 'INFO');
347
348 }
349
350 // Run the backup in background
351 if ((!defined('BMI_USING_CLI_FUNCTIONALITY') || BMI_USING_CLI_FUNCTIONALITY === false) && ($legacy === false || BMI_CLI_ENABLED === true) && sizeof($this->org_files) > 10 && !defined('BMI_CLI_FAILED')) {
352 file_put_contents($database_file_dir . 'bmi_backup_manifest.json', $this->zip_progress->createManifest($dbBackupEngine));
353 $url = plugins_url('') . '/backup-backup/includes/backup-heart.php';
354 $identy = 'BMI-' . rand(10000000, 999999999);
355 $remote_settings = [
356 'identy' => $identy,
357 'manifest' => $database_file_dir . 'bmi_backup_manifest.json',
358 'backupname' => $this->backupname,
359 'safelimit' => $safe_limit,
360 'rev' => BMI_REV,
361 'total_files' => sizeof($this->org_files),
362 'filessofar' => 0,
363 'start' => microtime(true),
364 'config_dir' => BMI_CONFIG_DIR,
365 'content_dir' => trailingslashit(WP_CONTENT_DIR),
366 'backup_dir' => BMI_BACKUPS,
367 'abs_dir' => trailingslashit(ABSPATH),
368 'root_dir' => plugin_dir_path(BMI_ROOT_FILE),
369 'browser' => false,
370 'shareallowed' => BMP::canShareLogsOrShouldAsk(),
371 'dbiteratio' => 0,
372 'dblast' => 0,
373 'bmitmp' => BMI_TMP,
374 'url' => $url
375 ];
376
377 $fix = true;
378 $Xfiles = glob(BMI_TMP . DIRECTORY_SEPARATOR . '.BMI-*');
379 foreach ($Xfiles as $xfile) if (is_file($xfile)) unlink($xfile);
380 touch(BMI_TMP . DIRECTORY_SEPARATOR . '.' . $identy);
381
382 if ($fix === true) {
383 if (BMI_LEGACY_HARD_VERSION === false && $cron === false) {
384 $remote_settings['browser'] = true;
385 $this->zip_progress->log(__("Sending backup settings and identy to the browser", 'backup-backup'), 'INFO');
386 BMP::res(['status' => 'background_hard', 'filename' => $this->backupname, 'settings' => $remote_settings, 'url' => $url]);
387 exit;
388 } else {
389 $this->zip_progress->log(__('Starting background process on server-side...', 'backup-backup'), 'INFO');
390 require_once BMI_INCLUDES . '/bypasser.php';
391 $request = new Bypasser(false, BMI_CONFIG_DIR, trailingslashit(WP_CONTENT_DIR), BMI_BACKUPS, trailingslashit(ABSPATH), plugin_dir_path(BMI_ROOT_FILE), $url, $remote_settings);
392 $request->send_beat(true, $this->zip_progress);
393 }
394 }
395
396 sleep(2);
397 if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.running')) {
398 if (file_exists(BMI_TMP . DIRECTORY_SEPARATOR . '.' . $identy . '-running')) {
399 // $this->zip_progress->log(__('Request received correctly – backup is running.', 'backup-backup'), 'SUCCESS');
400 if ($cron === true) return ['status' => 'background', 'filename' => $this->backupname];
401 else BMP::res(['status' => 'background', 'filename' => $this->backupname]);
402 exit;
403 } else {
404 $this->zip_progress->log(__('Could not find any response from the server, trying again in 3 seconds.', 'backup-backup'), 'WARN');
405 sleep(3);
406 if (file_exists(BMI_TMP . DIRECTORY_SEPARATOR . '.' . $identy . '-running')) {
407 // $this->zip_progress->log(__('Request received correctly – backup is running.', 'backup-backup'), 'SUCCESS');
408 if ($cron === true) return ['status' => 'background', 'filename' => $this->backupname];
409 else BMP::res(['status' => 'background', 'filename' => $this->backupname]);
410 exit;
411 } else {
412 $this->zip_progress->log(__('Still nothing backup probably is not running.', 'backup-backup'), 'WARN');
413 if (file_exists(BMI_TMP . DIRECTORY_SEPARATOR . '.' . $identy . '-running')) @unlink(BMI_TMP . DIRECTORY_SEPARATOR . '.' . $identy . '-running');
414 if (file_exists(BMI_TMP . DIRECTORY_SEPARATOR . '.' . $identy)) @unlink(BMI_TMP . DIRECTORY_SEPARATOR . '.' . $identy);
415 throw new \Exception('Backup could not run on your server, please check global logs.');
416 }
417 }
418 } else {
419 if ($cron === true) return ['status' => 'background', 'filename' => $this->backupname];
420 else BMP::res(['status' => 'background', 'filename' => $this->backupname]);
421 exit;
422 }
423
424 // ob_end_clean();
425 exit;
426 } else {
427 if (defined('BMI_USING_CLI_FUNCTIONALITY') && BMI_USING_CLI_FUNCTIONALITY === true) {
428 $this->zip_progress->log(__("Backup is running under PHP CLI environment.", 'backup-backup'), 'INFO');
429 if ($this->dbDumped === false) {
430 $this->createDatabaseDump($dbbackupname, $better_database_files_dir, $database_file, $database_file_dir);
431 }
432 } else {
433 $this->zip_progress->log(__("Backup will run as single-request, may be unstable...", 'backup-backup'), 'WARN');
434 }
435 }
436
437 // require the lib
438 if (!class_exists('PclZip')) {
439 if (!defined('PCLZIP_TEMPORARY_DIR')) {
440 $bmi_tmp_dir = BMI_TMP;
441 if (!file_exists($bmi_tmp_dir)) {
442 @mkdir($bmi_tmp_dir, 0775, true);
443 }
444 define('PCLZIP_TEMPORARY_DIR', $bmi_tmp_dir . DIRECTORY_SEPARATOR . 'bmi-');
445 }
446 if (defined('BMI_PRO_PCLZIP') && file_exists(BMI_PRO_PCLZIP)) {
447 $this->zip_progress->log(__('Using dedicated PclZIP for Premium Users.', 'backup-backup'), 'INFO');
448 require_once BMI_PRO_PCLZIP;
449 } else {
450 require_once trailingslashit(ABSPATH) . 'wp-admin/includes/class-pclzip.php';
451 }
452 }
453 $common = $this->org_files;
454
455 if (!$lib = new \PclZip($this->new_file_path)) {
456 throw new \Exception('PHP-ZIP: Permission Denied or zlib can\'t be found');
457 }
458
459 if ($this->dbDumped === true) {
460 try {
461
462 $this->zip_progress->log(__('Adding database SQL file(s) to the backup file.', 'backup-backup'), 'STEP');
463
464 $files = [];
465
466 if ($database_file !== false && !($this->db_exporter_files && sizeof($this->db_exporter_files) > 0)) {
467 $files[] = $database_file;
468 }
469
470 $this->zip_progress->log('Database files in total found: ' . sizeof($this->db_exporter_files), 'VERBOSE');
471 if ($this->db_exporter_files && sizeof($this->db_exporter_files) > 0) {
472 for ($i = 0; $i < sizeof($this->db_exporter_files); ++$i) {
473 $files[] = $this->db_exporter_files[$i];
474 }
475 } else {
476 $this->zip_progress->log(__('No database files found to be added into backup, ignore this message if database was not meant to be included.', 'backup-backup'), 'WARN');
477 $this->zip_progress->log('No database files found to be added into backup, ignore this message if database was not meant to be included.', 'VERBOSE');
478 }
479
480 if (sizeof($files) > 0) {
481 $dbback = $lib->add($files, PCLZIP_OPT_REMOVE_PATH, $database_file_dir, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $safe_limit);
482 }
483
484 if ($dbback == 0) {
485 $this->zip_failed($lib->errorInfo(true));
486 return false;
487 }
488
489 } catch (\Exception $e) {
490 $this->zip_failed($e->getMessage());
491
492 return false;
493 } catch (\Throwable $e) {
494 $this->zip_failed($e->getMessage());
495
496 return false;
497 }
498
499 if (sizeof($files) > 0) {
500 $this->zip_progress->log(__('Database added to the backup successfully.', 'backup-backup'), 'SUCCESS');
501 } else {
502 $this->zip_progress->log(__('Database was not added to the backup.', 'backup-backup'), 'WARN');
503 }
504 }
505
506 $this->zip_progress->log(__('Performing site files backup...', 'backup-backup'), 'STEP');
507
508 try {
509 $splitby = 200; $milestoneby = 500;
510 $filestotal = sizeof($this->org_files);
511 if ($filestotal < 3000) { $splitby = 250; $milestoneby = 500; }
512 if ($filestotal > 5000) { $splitby = 500; $milestoneby = 500; }
513 if ($filestotal > 10000) { $splitby = 1000; $milestoneby = 1000; }
514 if ($filestotal > 15000) { $splitby = 2000; $milestoneby = 2000; }
515 if ($filestotal > 20000) { $splitby = 4000; $milestoneby = 4000; }
516 if ($filestotal > 25000) { $splitby = 6000; $milestoneby = 6000; }
517 if ($filestotal > 30000) { $splitby = 8000; $milestoneby = 8000; }
518 if ($filestotal > 32000) { $splitby = 10000; $milestoneby = 10000; }
519 if ($filestotal > 60500) { $splitby = 20000; $milestoneby = 20000; }
520 if ($filestotal > 90500) { $splitby = 40000; $milestoneby = 40000; }
521
522 $this->zip_progress->log(__("Chunks contain ", 'backup-backup') . $splitby . __(" files.", 'backup-backup'));
523
524 $chunks = array_chunk($this->org_files, $splitby);
525 $chunkslen = sizeof($chunks);
526 if ($chunkslen > 0) {
527 $sizeoflast = sizeof($chunks[$chunkslen - 1]);
528 if ($chunkslen > 1 && $sizeoflast == 1) {
529 $buffer = array_slice($chunks[$chunkslen - 2], -1);
530 $chunks[$chunkslen - 2] = array_slice($chunks[$chunkslen - 2], 0, -1);
531 $chunks[$chunkslen - 1][] = $buffer[0];
532 }
533 }
534
535 for ($i = 0; $i < $chunkslen; ++$i) {
536
537 // Abort if user wants it (check every 100 files)
538 if (file_exists(BMI_BACKUPS . '/.abort')) {
539 break;
540 }
541
542 $back = 0;
543 $chunk = $chunks[$i];
544 $chunk = array_filter($chunk, function ($path) {
545 if (is_readable($path) && file_exists($path)) return true;
546 else {
547 $this->zip_progress->log(sprintf(__("Excluding file that cannot be read: %s", 'backup-backup'), $path), 'warn');
548 return false;
549 }
550 });
551
552 if (sizeof($chunk) > 0) {
553 $back = $lib->add($chunk, PCLZIP_OPT_REMOVE_PATH, $abs, PCLZIP_OPT_ADD_PATH, 'wordpress' . DIRECTORY_SEPARATOR/*, PCLZIP_OPT_ADD_TEMP_FILE_ON*/, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $safe_limit);
554 }
555 if ($back == 0) {
556 $this->zip_failed($lib->errorInfo(true));
557 return false;
558 }
559
560 $curfile = (($i * $splitby) + $splitby);
561 $this->zip_progress->progress($curfile . '/' . $max);
562 if ($curfile % $milestoneby === 0 && $curfile < $max) {
563 $this->zip_progress->log(__("Milestone: ", 'backup-backup') . ($curfile . '/' . $max), 'info');
564 }
565 }
566
567 } catch (\Exception $e) {
568 $this->zip_failed($e->getMessage());
569
570 return false;
571 } catch (\Throwable $e) {
572 $this->zip_failed($e->getMessage());
573
574 return false;
575 }
576
577 if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.abort')) {
578
579 if (file_exists($database_file_dir . 'bmi_backup_manifest.json')) {
580 @unlink($database_file_dir . 'bmi_backup_manifest.json');
581 }
582 if (file_exists($database_file_dir . 'bmi_logs_this_backup.log')) {
583 @unlink($database_file_dir . 'bmi_logs_this_backup.log');
584 }
585
586 } else {
587
588 // End
589 $this->zip_progress->log(__("Milestone: ", 'backup-backup') . ($max . '/' . $max), 'info');
590 $this->zip_progress->log(__("Compressed ", 'backup-backup') . $max . __(" files", 'backup-backup'), 'SUCCESS');
591
592 // Log time of ZIP Process
593 $this->zip_progress->log(__("Archiving of ", 'backup-backup') . $max . __(" files took: ", 'backup-backup') . number_format(microtime(true) - $this->start_zip, 2) . 's');
594
595 $this->zip_progress->log(__("Finalizing backup", 'backup-backup'), 'STEP');
596 $this->zip_progress->log(__("Generating manifest file and saving current live-log...", 'backup-backup'), 'INFO');
597
598 file_put_contents($database_file_dir . 'bmi_backup_manifest.json', $this->zip_progress->createManifest($dbBackupEngine));
599 file_put_contents($database_file_dir . 'bmi_logs_this_backup.log', file_get_contents(BMI_BACKUPS . DIRECTORY_SEPARATOR . 'latest.log'));
600
601 sleep(1);
602
603 $files = [];
604
605 if (file_exists($database_file_dir . 'bmi_logs_this_backup.log')) $files[] = $database_file_dir . 'bmi_logs_this_backup.log';
606 if (file_exists($database_file_dir . 'bmi_backup_manifest.json')) $files[] = $database_file_dir . 'bmi_backup_manifest.json';
607 else {
608
609 $this->zip_failed('Manifest file could not be added, manifest does not exist.');
610 return false;
611
612 }
613
614 $this->zip_progress->log(__("Adding manifest...", 'backup-backup'), 'INFO');
615 try {
616
617 $maback = $lib->add($files, PCLZIP_OPT_REMOVE_PATH, $database_file_dir, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $safe_limit);
618
619 if ($maback == 0) {
620 $this->zip_failed($lib->errorInfo(true));
621 return false;
622 }
623
624 } catch (\Exception $e) {
625 $this->zip_failed($e->getMessage());
626
627 return false;
628 } catch (\Throwable $e) {
629 $this->zip_failed($e->getMessage());
630
631 return false;
632 }
633
634 if (file_exists($database_file_dir . 'bmi_backup_manifest.json')) {
635 @unlink($database_file_dir . 'bmi_backup_manifest.json');
636 }
637 if (file_exists($database_file_dir . 'bmi_logs_this_backup.log')) {
638 @unlink($database_file_dir . 'bmi_logs_this_backup.log');
639 }
640
641 $this->zip_progress->progress($max . '/' . $max);
642
643 $this->zip_progress->log(__("Manifest file and logs added to the backup. Temporary files removed.", 'backup-backup'), 'SUCCESS');
644
645 }
646 }
647
648 $this->zip_progress->log(__("Closing files and archives", 'backup-backup'), 'STEP');
649
650 // Remove Better DB SQL Files
651 if ($this->db_exporter_files && sizeof($this->db_exporter_files) > 0) {
652 for ($i = 0; $i < sizeof($this->db_exporter_files); ++$i) {
653 if (file_exists($this->db_exporter_files[$i])) @unlink($this->db_exporter_files[$i]);
654 }
655 @rmdir($better_database_files_dir);
656 }
657
658 if ($database_file && file_exists($database_file)) @unlink($database_file);
659 if (!file_exists($this->new_file_path)) {
660 $this->zip_failed('PHP-ZIP: After doing the zipping file can not be found');
661 }
662 if (filesize($this->new_file_path) === 0) {
663 $this->zip_failed('PHP-ZIP: After doing the zipping file size is still 0 bytes');
664 }
665
666 // empty the array
667 $this->org_files = [];
668
669 $this->zip_progress->log(__("Cleanup finished, backup complete.", 'backup-backup'), 'SUCCESS');
670 // $this->zip_progress->log('#001', 'END-CODE');
671 return true;
672
673 }
674
675 public function zip_files($files, $to) {
676 $this->zip_start($to);
677 $this->zip_add($files);
678
679 return $this->zip_end();
680 }
681
682 public function unzip_file($file_path, $target_dir = null, &$zip_progress = null) {
683
684 // Progress
685 $this->zip_progress = $zip_progress;
686
687 // if it doesn't exist
688 if (!file_exists($file_path)) {
689 throw new \Exception("PHP-ZIP: File doesn't Exist");
690 }
691
692 $this->extr_file = $file_path;
693
694 // if (class_exists("ZipArchive")) $this->lib = 1;
695 // else $this->lib = 2;
696 $this->lib = 2;
697
698 if ($target_dir !== null) {
699 return $this->unzip_to($target_dir);
700 } else {
701 return true;
702 }
703 }
704
705 public function extract_files($zip_path, $files, $target_dir = null, &$zip_progress = null, $isFirstExtract = true) {
706
707 $this->zip_progress = $zip_progress;
708
709 // it exists, but it's not a directory
710 if (file_exists($target_dir) && (!is_dir($target_dir))) {
711 throw new \Exception("PHP-ZIPv2: Target directory exists as a file not a directory");
712 }
713 // it doesn't exist
714 if (!file_exists($target_dir)) {
715 if (!mkdir($target_dir)) {
716 throw new \Exception("PHP-ZIPv2: Directory not found, and unable to create it");
717 }
718 }
719 // validations -- end //
720 // TODO: NOTICE: Force disable PCLZIP
721 if (class_exists("ZipArchive") || class_exists("\ZipArchive")) {
722
723 $zip = new \ZipArchive;
724 $res = $zip->open($zip_path);
725
726 if ($res === true) {
727
728 if ($isFirstExtract) {
729 $this->zip_progress->log(__("Using ZipArchive, omiting memory limit calculations...", 'backup-backup'), 'INFO');
730 }
731
732 $zip->extractTo($target_dir, $files);
733 $zip->close();
734 return true;
735
736 } else {
737
738 $this->restore_failed('PHP-ZIPv2: Could not open Backup with ZipArchive.');
739 return false;
740
741 }
742
743 } else {
744
745 if ($isFirstExtract) {
746 $this->zip_progress->log(__("ZipArchive is not available, using PclZIP.", 'backup-backup'), 'INFO');
747 }
748
749 $safe_limit = $this->smartMemory($isFirstExtract);
750 $this->loadPclZip($isFirstExtract);
751 $lib = new \PclZip($zip_path);
752 $restor = $lib->extract(PCLZIP_OPT_BY_NAME, $files, PCLZIP_OPT_PATH, $target_dir, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $safe_limit);
753
754 if ($restor == 0) {
755
756 $this->restore_failed($lib->errorInfo(true));
757 return false;
758
759 }
760
761 return true;
762
763 }
764
765 }
766
767 public function loadPclZip($isFirstExtract = true) {
768
769 if (!class_exists('PclZip')) {
770 if (!defined('PCLZIP_TEMPORARY_DIR')) {
771 $bmi_tmp_dir = BMI_TMP;
772 if (!file_exists($bmi_tmp_dir)) {
773 @mkdir($bmi_tmp_dir, 0775, true);
774 }
775 define('PCLZIP_TEMPORARY_DIR', $bmi_tmp_dir . DIRECTORY_SEPARATOR . 'bmi-');
776 }
777
778 if (defined('BMI_PRO_PCLZIP') && file_exists(BMI_PRO_PCLZIP)) {
779 require_once BMI_PRO_PCLZIP;
780 if ($isFirstExtract) {
781 $this->zip_progress->log(__('Using dedicated PclZIP for Premium Users.', 'backup-backup'), 'INFO');
782 }
783 } else {
784 require_once trailingslashit(ABSPATH) . 'wp-admin/includes/class-pclzip.php';
785 }
786 }
787
788 }
789
790 public function smartMemory($isFirstExtract = true) {
791
792 // Smart memory -- start //
793 if ($this->zip_progress != null && $isFirstExtract) {
794 $this->zip_progress->log(__("Smart memory calculation...", 'backup-backup'), 'STEP');
795 }
796
797 if ((intval($this->return_bytes(ini_get('memory_limit'))) / 1024 / 1024) < 384) {
798 @ini_set('memory_limit', '384M');
799 }
800
801 $memory_limit = (intval($this->return_bytes(ini_get('memory_limit'))) / 1024 / 1024);
802 if ($this->zip_progress != null && $isFirstExtract) {
803 $this->zip_progress->log(str_replace('%s', $memory_limit, __("There is %s MBs of memory to use", 'backup-backup')), 'INFO');
804 }
805
806 $safe_limit = intval($memory_limit / 4);
807 if ($safe_limit > 64) $safe_limit = 64;
808 if ($memory_limit === 384) $safe_limit = 78;
809 if ($memory_limit >= 512) $safe_limit = 104;
810 if ($memory_limit >= 1024) $safe_limit = 228;
811 if ($memory_limit >= 2048) $safe_limit = 428;
812
813 // $real_memory = intval(memory_get_usage() * 0.9 / 1024 / 1024);
814 // if ($real_memory < $safe_limit) $safe_limit = $real_memory;
815 $safe_limit = intval($safe_limit * 0.8);
816
817 if ($this->zip_progress != null && $isFirstExtract) {
818 $this->zip_progress->log(str_replace('%s', $safe_limit, __("Setting the safe limit to %s MB", 'backup-backup')), 'SUCCESS');
819 }
820 // Smart memory -- end //
821
822 return $safe_limit;
823
824 }
825
826 public function unzip_to($target_dir) {
827
828 // validations -- start //
829 if ($this->lib === 0 && $this->extr_file === 0) {
830 throw new \Exception("PHP-ZIP: unzip_file hasn't been called");
831 }
832 // it exists, but it's not a directory
833 if (file_exists($target_dir) && (!is_dir($target_dir))) {
834 throw new \Exception("PHP-ZIP: Target directory exists as a file not a directory");
835 }
836 // it doesn't exist
837 if (!file_exists($target_dir)) {
838 if (!mkdir($target_dir)) {
839 throw new \Exception("PHP-ZIP: Directory not found, and unable to create it");
840 }
841 }
842 // validations -- end //
843
844 // Target Directory
845 $this->extr_dirc = $target_dir;
846
847 // Smart Memory
848 $safe_limit = $this->smartMemory();
849
850 // Extract msg
851 $this->zip_progress->log(__('Extracting files into temporary directory (this process can take some time)...', 'backup_migration'), 'STEP');
852
853 // Force PCL Zip
854 $this->lib = 2;
855
856 // extract using ZipArchive
857 // if($this->lib === 1) {
858 // $lib = new \ZipArchive;
859 // if(!$lib->open($this->extr_file)) throw new \Exception("PHP-ZIP: Unable to open the zip file");
860 // if(!$lib->extractTo($this->extr_dirc)) throw new \Exception("PHP-ZIP: Unable to extract files");
861 // $lib->close();
862 // }
863
864 // extarct using PclZip
865 if ($this->lib === 2) {
866 $this->loadPclZip();
867 $lib = new \PclZip($this->extr_file);
868 $restor = $lib->extract(PCLZIP_OPT_PATH, $this->extr_dirc, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $safe_limit);
869 if ($restor == 0) {
870 $this->restore_failed($lib->errorInfo(true));
871
872 return false;
873 }
874 }
875
876 return true;
877 }
878
879 private function dir_to_assoc_arr(DirectoryIterator $dir) {
880 $data = [];
881 foreach ($dir as $node) {
882 if ($node->isDir() && !$node->isDot()) {
883 $data[$node->getFilename()] = $this->dir_to_assoc_arr(new DirectoryIterator($node->getPathname()));
884 } elseif ($node->isFile()) {
885 $data[] = $node->getFilename();
886 }
887 }
888
889 return $data;
890 }
891
892 private function path() {
893 return join(DIRECTORY_SEPARATOR, func_get_args());
894 }
895
896 private function commonPath($files, $remove = true) {
897 foreach ($files as $index => $filesStr) {
898 $files[$index] = explode(DIRECTORY_SEPARATOR, $filesStr);
899 }
900 $toDiff = $files;
901 foreach ($toDiff as $arr_i => $arr) {
902 foreach ($arr as $name_i => $name) {
903 $toDiff[$arr_i][$name_i] = $name . "___" . $name_i;
904 }
905 }
906 $diff = call_user_func_array("array_diff", $toDiff);
907 reset($diff);
908 $i = key($diff) - 1;
909 if ($remove) {
910 foreach ($files as $index => $arr) {
911 $files[$index] = implode(DIRECTORY_SEPARATOR, array_slice($files[$index], $i));
912 }
913 } else {
914 foreach ($files as $index => $arr) {
915 $files[$index] = implode(DIRECTORY_SEPARATOR, array_slice($files[$index], 0, $i));
916 }
917 }
918
919 return $files;
920 }
921 }
922