PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 1.3.4
Backup Migration v1.3.4
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
915 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_INCLUDES . DIRECTORY_SEPARATOR . 'htaccess' . 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 'url' => $url
374 ];
375
376 $fix = true;
377 $Xfiles = glob(BMI_INCLUDES . '/htaccess' . '/.BMI-*');
378 foreach ($Xfiles as $xfile) if (is_file($xfile)) unlink($xfile);
379 touch(BMI_INCLUDES . '/htaccess' . '/.' . $identy);
380
381 if ($fix === true) {
382 if (BMI_LEGACY_HARD_VERSION === false && $cron === false) {
383 $remote_settings['browser'] = true;
384 $this->zip_progress->log(__("Sending backup settings and identy to the browser", 'backup-backup'), 'INFO');
385 BMP::res(['status' => 'background_hard', 'filename' => $this->backupname, 'settings' => $remote_settings, 'url' => $url]);
386 exit;
387 } else {
388 $this->zip_progress->log(__('Starting background process on server-side...', 'backup-backup'), 'INFO');
389 require_once BMI_INCLUDES . '/bypasser.php';
390 $request = new Bypasser(false, BMI_CONFIG_DIR, trailingslashit(WP_CONTENT_DIR), BMI_BACKUPS, trailingslashit(ABSPATH), plugin_dir_path(BMI_ROOT_FILE), $url, $remote_settings);
391 $request->send_beat(true, $this->zip_progress);
392 }
393 }
394
395 sleep(2);
396 if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.running')) {
397 if (file_exists(BMI_INCLUDES . '/htaccess' . '/.' . $identy . '-running')) {
398 // $this->zip_progress->log(__('Request received correctly – backup is running.', 'backup-backup'), 'SUCCESS');
399 BMP::res(['status' => 'background', 'filename' => $this->backupname]);
400 exit;
401 } else {
402 $this->zip_progress->log(__('Could not find any response from the server, trying again in 3 seconds.', 'backup-backup'), 'WARN');
403 sleep(3);
404 if (file_exists(BMI_INCLUDES . '/htaccess' . '/.' . $identy . '-running')) {
405 // $this->zip_progress->log(__('Request received correctly – backup is running.', 'backup-backup'), 'SUCCESS');
406 BMP::res(['status' => 'background', 'filename' => $this->backupname]);
407 exit;
408 } else {
409 $this->zip_progress->log(__('Still nothing backup probably is not running.', 'backup-backup'), 'WARN');
410 if (file_exists(BMI_INCLUDES . '/htaccess' . '/.' . $identy . '-running')) @unlink(BMI_INCLUDES . '/htaccess' . '/.' . $identy . '-running');
411 if (file_exists(BMI_INCLUDES . '/htaccess' . '/.' . $identy)) @unlink(BMI_INCLUDES . '/htaccess' . '/.' . $identy);
412 throw new \Exception('Backup could not run on your server, please check global logs.');
413 }
414 }
415 } else {
416 BMP::res(['status' => 'background', 'filename' => $this->backupname]);
417 exit;
418 }
419
420 // ob_end_clean();
421 exit;
422 } else {
423 if (defined('BMI_USING_CLI_FUNCTIONALITY') && BMI_USING_CLI_FUNCTIONALITY === true) {
424 $this->zip_progress->log(__("Backup is running under PHP CLI environment.", 'backup-backup'), 'INFO');
425 if ($this->dbDumped === false) {
426 $this->createDatabaseDump($dbbackupname, $better_database_files_dir, $database_file, $database_file_dir);
427 }
428 } else {
429 $this->zip_progress->log(__("Backup will run as single-request, may be unstable...", 'backup-backup'), 'WARN');
430 }
431 }
432
433 // require the lib
434 if (!class_exists('PclZip')) {
435 if (!defined('PCLZIP_TEMPORARY_DIR')) {
436 $bmi_tmp_dir = BMI_ROOT_DIR . '/tmp';
437 if (!file_exists($bmi_tmp_dir)) {
438 @mkdir($bmi_tmp_dir, 0775, true);
439 }
440 define('PCLZIP_TEMPORARY_DIR', $bmi_tmp_dir . '/bmi-');
441 }
442 if (defined('BMI_PRO_PCLZIP') && file_exists(BMI_PRO_PCLZIP)) {
443 $this->zip_progress->log(__('Using dedicated PclZIP for Premium Users.', 'backup-backup'), 'INFO');
444 require_once BMI_PRO_PCLZIP;
445 } else {
446 require_once trailingslashit(ABSPATH) . 'wp-admin/includes/class-pclzip.php';
447 }
448 }
449 $common = $this->org_files;
450
451 if (!$lib = new \PclZip($this->new_file_path)) {
452 throw new \Exception('PHP-ZIP: Permission Denied or zlib can\'t be found');
453 }
454
455 if ($this->dbDumped === true) {
456 try {
457
458 $this->zip_progress->log(__('Adding database SQL file(s) to the backup file.', 'backup-backup'), 'STEP');
459
460 $files = [];
461
462 if ($database_file !== false && !($this->db_exporter_files && sizeof($this->db_exporter_files) > 0)) {
463 $files[] = $database_file;
464 }
465
466 $this->zip_progress->log('Database files in total found: ' . sizeof($this->db_exporter_files), 'VERBOSE');
467 if ($this->db_exporter_files && sizeof($this->db_exporter_files) > 0) {
468 for ($i = 0; $i < sizeof($this->db_exporter_files); ++$i) {
469 $files[] = $this->db_exporter_files[$i];
470 }
471 } else {
472 $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');
473 $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');
474 }
475
476 if (sizeof($files) > 0) {
477 $dbback = $lib->add($files, PCLZIP_OPT_REMOVE_PATH, $database_file_dir, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $safe_limit);
478 }
479
480 if ($dbback == 0) {
481 $this->zip_failed($lib->errorInfo(true));
482 return false;
483 }
484
485 } catch (\Exception $e) {
486 $this->zip_failed($e->getMessage());
487
488 return false;
489 } catch (\Throwable $e) {
490 $this->zip_failed($e->getMessage());
491
492 return false;
493 }
494
495 if (sizeof($files) > 0) {
496 $this->zip_progress->log(__('Database added to the backup successfully.', 'backup-backup'), 'SUCCESS');
497 } else {
498 $this->zip_progress->log(__('Database was not added to the backup.', 'backup-backup'), 'WARN');
499 }
500 }
501
502 $this->zip_progress->log(__('Performing site files backup...', 'backup-backup'), 'STEP');
503
504 try {
505 $splitby = 200; $milestoneby = 500;
506 $filestotal = sizeof($this->org_files);
507 if ($filestotal < 3000) { $splitby = 250; $milestoneby = 500; }
508 if ($filestotal > 5000) { $splitby = 500; $milestoneby = 500; }
509 if ($filestotal > 10000) { $splitby = 1000; $milestoneby = 1000; }
510 if ($filestotal > 15000) { $splitby = 2000; $milestoneby = 2000; }
511 if ($filestotal > 20000) { $splitby = 4000; $milestoneby = 4000; }
512 if ($filestotal > 25000) { $splitby = 6000; $milestoneby = 6000; }
513 if ($filestotal > 30000) { $splitby = 8000; $milestoneby = 8000; }
514 if ($filestotal > 32000) { $splitby = 10000; $milestoneby = 10000; }
515 if ($filestotal > 60500) { $splitby = 20000; $milestoneby = 20000; }
516 if ($filestotal > 90500) { $splitby = 40000; $milestoneby = 40000; }
517
518 $this->zip_progress->log(__("Chunks contain ", 'backup-backup') . $splitby . __(" files.", 'backup-backup'));
519
520 $chunks = array_chunk($this->org_files, $splitby);
521 $chunkslen = sizeof($chunks);
522 if ($chunkslen > 0) {
523 $sizeoflast = sizeof($chunks[$chunkslen - 1]);
524 if ($chunkslen > 1 && $sizeoflast == 1) {
525 $buffer = array_slice($chunks[$chunkslen - 2], -1);
526 $chunks[$chunkslen - 2] = array_slice($chunks[$chunkslen - 2], 0, -1);
527 $chunks[$chunkslen - 1][] = $buffer[0];
528 }
529 }
530
531 for ($i = 0; $i < $chunkslen; ++$i) {
532
533 // Abort if user wants it (check every 100 files)
534 if (file_exists(BMI_BACKUPS . '/.abort')) {
535 break;
536 }
537
538 $chunk = $chunks[$i];
539 $chunk = array_filter($chunk, function ($path) {
540 if (is_readable($path) && file_exists($path)) return true;
541 else {
542 $this->zip_progress->log(sprintf(__("Excluding file that cannot be read: %s", 'backup-backup'), $path), 'warn');
543 return false;
544 }
545 });
546
547 $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);
548 if ($back == 0) {
549 $this->zip_failed($lib->errorInfo(true));
550 return false;
551 }
552
553 $curfile = (($i * $splitby) + $splitby);
554 $this->zip_progress->progress($curfile . '/' . $max);
555 if ($curfile % $milestoneby === 0 && $curfile < $max) {
556 $this->zip_progress->log(__("Milestone: ", 'backup-backup') . ($curfile . '/' . $max), 'info');
557 }
558 }
559
560 } catch (\Exception $e) {
561 $this->zip_failed($e->getMessage());
562
563 return false;
564 } catch (\Throwable $e) {
565 $this->zip_failed($e->getMessage());
566
567 return false;
568 }
569
570 if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.abort')) {
571
572 if (file_exists($database_file_dir . 'bmi_backup_manifest.json')) {
573 @unlink($database_file_dir . 'bmi_backup_manifest.json');
574 }
575 if (file_exists($database_file_dir . 'bmi_logs_this_backup.log')) {
576 @unlink($database_file_dir . 'bmi_logs_this_backup.log');
577 }
578
579 } else {
580
581 // End
582 $this->zip_progress->log(__("Milestone: ", 'backup-backup') . ($max . '/' . $max), 'info');
583 $this->zip_progress->log(__("Compressed ", 'backup-backup') . $max . __(" files", 'backup-backup'), 'SUCCESS');
584
585 // Log time of ZIP Process
586 $this->zip_progress->log(__("Archiving of ", 'backup-backup') . $max . __(" files took: ", 'backup-backup') . number_format(microtime(true) - $this->start_zip, 2) . 's');
587
588 $this->zip_progress->log(__("Finalizing backup", 'backup-backup'), 'STEP');
589 $this->zip_progress->log(__("Generating manifest file and saving current live-log...", 'backup-backup'), 'INFO');
590
591 file_put_contents($database_file_dir . 'bmi_backup_manifest.json', $this->zip_progress->createManifest($dbBackupEngine));
592 file_put_contents($database_file_dir . 'bmi_logs_this_backup.log', file_get_contents(BMI_BACKUPS . DIRECTORY_SEPARATOR . 'latest.log'));
593
594 sleep(1);
595
596 $files = [];
597
598 if (file_exists($database_file_dir . 'bmi_logs_this_backup.log')) $files[] = $database_file_dir . 'bmi_logs_this_backup.log';
599 if (file_exists($database_file_dir . 'bmi_backup_manifest.json')) $files[] = $database_file_dir . 'bmi_backup_manifest.json';
600 else {
601
602 $this->zip_failed('Manifest file could not be added, manifest does not exist.');
603 return false;
604
605 }
606
607 $this->zip_progress->log(__("Adding manifest...", 'backup-backup'), 'INFO');
608 try {
609
610 $maback = $lib->add($files, PCLZIP_OPT_REMOVE_PATH, $database_file_dir, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $safe_limit);
611
612 if ($maback == 0) {
613 $this->zip_failed($lib->errorInfo(true));
614 return false;
615 }
616
617 } catch (\Exception $e) {
618 $this->zip_failed($e->getMessage());
619
620 return false;
621 } catch (\Throwable $e) {
622 $this->zip_failed($e->getMessage());
623
624 return false;
625 }
626
627 if (file_exists($database_file_dir . 'bmi_backup_manifest.json')) {
628 @unlink($database_file_dir . 'bmi_backup_manifest.json');
629 }
630 if (file_exists($database_file_dir . 'bmi_logs_this_backup.log')) {
631 @unlink($database_file_dir . 'bmi_logs_this_backup.log');
632 }
633
634 $this->zip_progress->progress($max . '/' . $max);
635
636 $this->zip_progress->log(__("Manifest file and logs added to the backup. Temporary files removed.", 'backup-backup'), 'SUCCESS');
637
638 }
639 }
640
641 $this->zip_progress->log(__("Closing files and archives", 'backup-backup'), 'STEP');
642
643 // Remove Better DB SQL Files
644 if ($this->db_exporter_files && sizeof($this->db_exporter_files) > 0) {
645 for ($i = 0; $i < sizeof($this->db_exporter_files); ++$i) {
646 if (file_exists($this->db_exporter_files[$i])) @unlink($this->db_exporter_files[$i]);
647 }
648 @rmdir($better_database_files_dir);
649 }
650
651 if ($database_file && file_exists($database_file)) @unlink($database_file);
652 if (!file_exists($this->new_file_path)) {
653 $this->zip_failed('PHP-ZIP: After doing the zipping file can not be found');
654 }
655 if (filesize($this->new_file_path) === 0) {
656 $this->zip_failed('PHP-ZIP: After doing the zipping file size is still 0 bytes');
657 }
658
659 // empty the array
660 $this->org_files = [];
661
662 $this->zip_progress->log(__("Cleanup finished, backup complete.", 'backup-backup'), 'SUCCESS');
663 // $this->zip_progress->log('#001', 'END-CODE');
664 return true;
665
666 }
667
668 public function zip_files($files, $to) {
669 $this->zip_start($to);
670 $this->zip_add($files);
671
672 return $this->zip_end();
673 }
674
675 public function unzip_file($file_path, $target_dir = null, &$zip_progress = null) {
676
677 // Progress
678 $this->zip_progress = $zip_progress;
679
680 // if it doesn't exist
681 if (!file_exists($file_path)) {
682 throw new \Exception("PHP-ZIP: File doesn't Exist");
683 }
684
685 $this->extr_file = $file_path;
686
687 // if (class_exists("ZipArchive")) $this->lib = 1;
688 // else $this->lib = 2;
689 $this->lib = 2;
690
691 if ($target_dir !== null) {
692 return $this->unzip_to($target_dir);
693 } else {
694 return true;
695 }
696 }
697
698 public function extract_files($zip_path, $files, $target_dir = null, &$zip_progress = null, $isFirstExtract = true) {
699
700 $this->zip_progress = $zip_progress;
701
702 // it exists, but it's not a directory
703 if (file_exists($target_dir) && (!is_dir($target_dir))) {
704 throw new \Exception("PHP-ZIPv2: Target directory exists as a file not a directory");
705 }
706 // it doesn't exist
707 if (!file_exists($target_dir)) {
708 if (!mkdir($target_dir)) {
709 throw new \Exception("PHP-ZIPv2: Directory not found, and unable to create it");
710 }
711 }
712 // validations -- end //
713 // TODO: NOTICE: Force disable PCLZIP
714 if (class_exists("ZipArchive") || class_exists("\ZipArchive")) {
715
716 $zip = new \ZipArchive;
717 $res = $zip->open($zip_path);
718
719 if ($res === true) {
720
721 if ($isFirstExtract) {
722 $this->zip_progress->log(__("Using ZipArchive, omiting memory limit calculations...", 'backup-backup'), 'INFO');
723 }
724
725 $zip->extractTo($target_dir, $files);
726 $zip->close();
727 return true;
728
729 } else {
730
731 $this->restore_failed('PHP-ZIPv2: Could not open Backup with ZipArchive.');
732 return false;
733
734 }
735
736 } else {
737
738 if ($isFirstExtract) {
739 $this->zip_progress->log(__("ZipArchive is not available, using PclZIP.", 'backup-backup'), 'INFO');
740 }
741
742 $safe_limit = $this->smartMemory($isFirstExtract);
743 $this->loadPclZip($isFirstExtract);
744 $lib = new \PclZip($zip_path);
745 $restor = $lib->extract(PCLZIP_OPT_BY_NAME, $files, PCLZIP_OPT_PATH, $target_dir, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $safe_limit);
746
747 if ($restor == 0) {
748
749 $this->restore_failed($lib->errorInfo(true));
750 return false;
751
752 }
753
754 return true;
755
756 }
757
758 }
759
760 public function loadPclZip($isFirstExtract = true) {
761
762 if (!class_exists('PclZip')) {
763 if (!defined('PCLZIP_TEMPORARY_DIR')) {
764 $bmi_tmp_dir = BMI_ROOT_DIR . '/tmp';
765 if (!file_exists($bmi_tmp_dir)) {
766 @mkdir($bmi_tmp_dir, 0775, true);
767 }
768 define('PCLZIP_TEMPORARY_DIR', $bmi_tmp_dir . '/bmi-');
769 }
770
771 if (defined('BMI_PRO_PCLZIP') && file_exists(BMI_PRO_PCLZIP)) {
772 require_once BMI_PRO_PCLZIP;
773 if ($isFirstExtract) {
774 $this->zip_progress->log(__('Using dedicated PclZIP for Premium Users.', 'backup-backup'), 'INFO');
775 }
776 } else {
777 require_once trailingslashit(ABSPATH) . 'wp-admin/includes/class-pclzip.php';
778 }
779 }
780
781 }
782
783 public function smartMemory($isFirstExtract = true) {
784
785 // Smart memory -- start //
786 if ($this->zip_progress != null && $isFirstExtract) {
787 $this->zip_progress->log(__("Smart memory calculation...", 'backup-backup'), 'STEP');
788 }
789
790 if ((intval($this->return_bytes(ini_get('memory_limit'))) / 1024 / 1024) < 384) {
791 @ini_set('memory_limit', '384M');
792 }
793
794 $memory_limit = (intval($this->return_bytes(ini_get('memory_limit'))) / 1024 / 1024);
795 if ($this->zip_progress != null && $isFirstExtract) {
796 $this->zip_progress->log(str_replace('%s', $memory_limit, __("There is %s MBs of memory to use", 'backup-backup')), 'INFO');
797 }
798
799 $safe_limit = intval($memory_limit / 4);
800 if ($safe_limit > 64) $safe_limit = 64;
801 if ($memory_limit === 384) $safe_limit = 78;
802 if ($memory_limit >= 512) $safe_limit = 104;
803 if ($memory_limit >= 1024) $safe_limit = 228;
804 if ($memory_limit >= 2048) $safe_limit = 428;
805
806 // $real_memory = intval(memory_get_usage() * 0.9 / 1024 / 1024);
807 // if ($real_memory < $safe_limit) $safe_limit = $real_memory;
808 $safe_limit = intval($safe_limit * 0.8);
809
810 if ($this->zip_progress != null && $isFirstExtract) {
811 $this->zip_progress->log(str_replace('%s', $safe_limit, __("Setting the safe limit to %s MB", 'backup-backup')), 'SUCCESS');
812 }
813 // Smart memory -- end //
814
815 return $safe_limit;
816
817 }
818
819 public function unzip_to($target_dir) {
820
821 // validations -- start //
822 if ($this->lib === 0 && $this->extr_file === 0) {
823 throw new \Exception("PHP-ZIP: unzip_file hasn't been called");
824 }
825 // it exists, but it's not a directory
826 if (file_exists($target_dir) && (!is_dir($target_dir))) {
827 throw new \Exception("PHP-ZIP: Target directory exists as a file not a directory");
828 }
829 // it doesn't exist
830 if (!file_exists($target_dir)) {
831 if (!mkdir($target_dir)) {
832 throw new \Exception("PHP-ZIP: Directory not found, and unable to create it");
833 }
834 }
835 // validations -- end //
836
837 // Target Directory
838 $this->extr_dirc = $target_dir;
839
840 // Smart Memory
841 $safe_limit = $this->smartMemory();
842
843 // Extract msg
844 $this->zip_progress->log(__('Extracting files into temporary directory (this process can take some time)...', 'backup_migration'), 'STEP');
845
846 // Force PCL Zip
847 $this->lib = 2;
848
849 // extract using ZipArchive
850 // if($this->lib === 1) {
851 // $lib = new \ZipArchive;
852 // if(!$lib->open($this->extr_file)) throw new \Exception("PHP-ZIP: Unable to open the zip file");
853 // if(!$lib->extractTo($this->extr_dirc)) throw new \Exception("PHP-ZIP: Unable to extract files");
854 // $lib->close();
855 // }
856
857 // extarct using PclZip
858 if ($this->lib === 2) {
859 $this->loadPclZip();
860 $lib = new \PclZip($this->extr_file);
861 $restor = $lib->extract(PCLZIP_OPT_PATH, $this->extr_dirc, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $safe_limit);
862 if ($restor == 0) {
863 $this->restore_failed($lib->errorInfo(true));
864
865 return false;
866 }
867 }
868
869 return true;
870 }
871
872 private function dir_to_assoc_arr(DirectoryIterator $dir) {
873 $data = [];
874 foreach ($dir as $node) {
875 if ($node->isDir() && !$node->isDot()) {
876 $data[$node->getFilename()] = $this->dir_to_assoc_arr(new DirectoryIterator($node->getPathname()));
877 } elseif ($node->isFile()) {
878 $data[] = $node->getFilename();
879 }
880 }
881
882 return $data;
883 }
884
885 private function path() {
886 return join(DIRECTORY_SEPARATOR, func_get_args());
887 }
888
889 private function commonPath($files, $remove = true) {
890 foreach ($files as $index => $filesStr) {
891 $files[$index] = explode(DIRECTORY_SEPARATOR, $filesStr);
892 }
893 $toDiff = $files;
894 foreach ($toDiff as $arr_i => $arr) {
895 foreach ($arr as $name_i => $name) {
896 $toDiff[$arr_i][$name_i] = $name . "___" . $name_i;
897 }
898 }
899 $diff = call_user_func_array("array_diff", $toDiff);
900 reset($diff);
901 $i = key($diff) - 1;
902 if ($remove) {
903 foreach ($files as $index => $arr) {
904 $files[$index] = implode(DIRECTORY_SEPARATOR, array_slice($files[$index], $i));
905 }
906 } else {
907 foreach ($files as $index => $arr) {
908 $files[$index] = implode(DIRECTORY_SEPARATOR, array_slice($files[$index], 0, $i));
909 }
910 }
911
912 return $files;
913 }
914 }
915