PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 1.4.6
Backup Migration v1.4.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
1108 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 Logger::error(__("There was an error during backup (packing)...", 'backup-backup'));
78 Logger::error(print_r($error, true));
79
80 if ($this->zip_progress != null) {
81 $this->zip_progress->log(__("Issues during backup (packing)...", 'backup-backup'), 'ERROR');
82 $this->zip_progress->log(print_r($error, true), 'ERROR');
83 $this->zip_progress->log('#004', 'END-CODE');
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 $this->zip_progress->log('#004', 'END-CODE');
95 }
96 }
97
98 public function zip_add($in) {
99
100 // Just to make sure.. if the user haven't called the earlier method
101 if ($this->lib === 0 || $this->new_file_path === 0) {
102 throw new \Exception("PHP-ZIP: must call zip_start before zip_add");
103 }
104
105 // Push file
106 array_push($this->org_files, $in);
107
108 // Return
109 return true;
110 }
111
112 public function createDatabaseDump($dbbackupname, $better_database_files_dir, &$database_file, $database_file_dir, $dbBackupEngine = 'v4') {
113
114 if (Dashboard\bmi_get_config('BACKUP:DATABASE') == 'true') {
115
116 if (Dashboard\bmi_get_config('OTHER:BACKUP:DB:SINGLE:FILE') == 'true') {
117
118 // Require Database Manager
119 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'manager.php';
120
121 // Logs
122 $this->zip_progress->log(__("Making single-file database backup (using deprecated engine, due to used settings)", 'backup-backup'), 'STEP');
123
124 // Get database dump
125 $databaser = new Database(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
126 $databaser->exportDatabase($dbbackupname);
127
128 // Fix for newer version
129 $this->db_exporter_queries = 0;
130 $this->zip_progress->total_queries = 0;
131 $this->db_exporter_files = [];
132
133 $this->dbDumped = true;
134 $this->zip_progress->log(__("Database size: ", 'backup-backup') . BMP::humanSize(filesize($database_file)), 'INFO');
135
136 } else {
137
138 if ($dbBackupEngine == 'v4') {
139
140 // Require Database Manager
141 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'better-backup-v3.php';
142
143 // Get database dump
144 $this->zip_progress->log(__("Making database backup (using v3 engine, requires at least v1.2.2 to restore)", 'backup-backup'), 'STEP');
145
146 } else {
147
148 // Require Database Manager
149 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'better-backup.php';
150
151 // Get database dump
152 $this->zip_progress->log(__("Making database backup (using v2 engine, requires at least v1.1.0 to restore)", 'backup-backup'), 'STEP');
153
154 }
155
156 $this->zip_progress->log(__("Iterating database...", 'backup-backup'), 'INFO');
157
158 if (!is_dir($better_database_files_dir)) @mkdir($better_database_files_dir, 0755, true);
159 $db_exporter = new BetterDatabaseExport($better_database_files_dir, $this->zip_progress);
160
161 $this->zip_progress->log('Exporting database via zip.php', 'VERBOSE');
162 $db_exporter->export();
163 $this->db_exporter_files = $db_exporter->files;
164 $this->db_exporter_queries = $db_exporter->total_queries;
165
166 $this->zip_progress->total_queries = $this->db_exporter_queries;
167
168 $this->dbDumped = true;
169 $this->zip_progress->log(__("Database backup finished", 'backup-backup'), 'SUCCESS');
170
171 }
172
173 } else {
174
175 $this->dbDumped = false;
176 $this->zip_progress->log(__("Omitting database backup (due to settings)...", 'backup-backup'), 'WARN');
177 $database_file = false;
178 $this->db_exporter_files = [];
179 $this->db_exporter_queries = 0;
180 $this->zip_progress->total_queries = 0;
181
182 }
183
184 }
185
186 public function saveRemoteSettings($settings) {
187 $settings_name = 'currentBackupConfig.' . 'php';
188 $settings_path = BMP::fixSlashes(BMI_TMP . DIRECTORY_SEPARATOR . $settings_name);
189
190 if (file_exists($settings_path)) @unlink($settings_path);
191 file_put_contents($settings_path, '<?php //' . json_encode($settings));
192 }
193
194 // Cut Path for ZIP structure
195 public function cutDir($file) {
196
197 if (substr($file, -4) === '.sql') {
198
199 $path = 'db_tables' . DIRECTORY_SEPARATOR . basename($file);
200
201 } else {
202
203 $path = basename($file);
204
205 }
206
207 $path = str_replace('\\', '/', $path);
208 return $path;
209
210 }
211
212 public function zip_end($force_lib = false, $cron = false) {
213
214 // v4 for new one, v3 for old one
215 $dbBackupEngine = 'v4';
216
217 // Try to set limit
218 $this->zip_progress->log(__("Smart memory calculation...", 'backup-backup'), 'STEP');
219 if ((intval($this->return_bytes(ini_get('memory_limit'))) / 1024 / 1024) < 384) @ini_set('memory_limit', '384M');
220 if (defined('WP_MAX_MEMORY_LIMIT')) $maxwp = WP_MAX_MEMORY_LIMIT;
221 else $maxwp = '1M';
222
223 $memory_limit = (intval($this->return_bytes(ini_get('memory_limit'))) / 1024 / 1024);
224 $maxwp = (intval($this->return_bytes($maxwp)) / 1024 / 1024);
225
226 if ($maxwp < $memory_limit) $memory_limit = $maxwp;
227 $this->zip_progress->log(str_replace('%s', $memory_limit, __("There is %s MBs of memory to use", 'backup-backup')), 'INFO');
228 $this->zip_progress->log(str_replace('%s', $maxwp, __("WordPress memory limit: %s MBs", 'backup-backup')), 'INFO');
229 $safe_limit = intval($memory_limit / 4);
230 if ($safe_limit >= 1024) $safe_limit = 256;
231 else if ($safe_limit >= 512) $safe_limit = 128;
232 else if ($safe_limit === 384) $safe_limit = 96;
233 else if ($safe_limit > 64) $safe_limit = 64;
234
235 // $real_memory = intval(memory_get_usage() * 0.9 / 1024 / 1024);
236 // if ($real_memory < $safe_limit) $safe_limit = $real_memory;
237 $safe_limit = intval($safe_limit * 0.9);
238
239 $this->zip_progress->log(str_replace('%s', $safe_limit, __("Setting the safe limit to %s MB", 'backup-backup')), 'SUCCESS');
240
241 $abs = BMP::fixSlashes(ABSPATH) . DIRECTORY_SEPARATOR;
242
243 $dbbackupname = 'bmi_database_backup.sql';
244 $database_file = BMP::fixSlashes(BMI_TMP . DIRECTORY_SEPARATOR . $dbbackupname);
245 $database_file_dir = BMP::fixSlashes((dirname($database_file))) . DIRECTORY_SEPARATOR;
246 $better_database_files_dir = $database_file_dir . 'db_tables';
247
248 // force usage of specific lib (for testing purposes)
249 if ($force_lib === 2) {
250
251 $this->lib = 2;
252
253 } elseif ($force_lib === 1) {
254
255 $this->lib = 1;
256
257 }
258
259 $this->dbDumped = false;
260 $this->db_exporter_queries = 0;
261 $this->zip_progress->total_queries = 0;
262 $this->db_exporter_files = [];
263
264 // just to make sure.. if the user haven't called the earlier method
265 if ($this->lib === 0 || $this->new_file_path === 0) {
266 throw new \Exception('PHP-ZIP: zip_start and zip_add haven\'t been called yet');
267 }
268
269 // using zipArchive class
270 // if ($this->lib === 1) {
271 //
272 // // Create DB Dump
273 // $this->createDatabaseDump($dbbackupname, $better_database_files_dir, $database_file, $database_file_dir);
274 //
275 // // All files
276 // $max = sizeof($this->org_files);
277 // $this->zip_progress->log(__("Making archive", 'backup-backup'), 'STEP');
278 // $this->zip_progress->log(__("Compressing...", 'backup-backup'), 'INFO');
279 //
280 // // Verbose
281 // $this->zip_progress->log(__("Using Zlib to create Backup", 'backup-backup'));
282 //
283 // $lib = new \ZipArchive();
284 // if (!$lib->open($this->new_file_path, \ZipArchive::CREATE)) {
285 // throw new \Exception('PHP-ZIP: Permission Denied or zlib can\'t be found');
286 // }
287 //
288 // // Add each file
289 // for ($i = 0; $i < $max; $i++) {
290 // $file = $this->org_files[$i];
291 // $zippath = substr($file, strlen($abs));
292 // $lib->addFile($file, 'wordpress' . DIRECTORY_SEPARATOR . $zippath);
293 //
294 // if ($i % 100 === 0) {
295 // if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.abort')) {
296 // break;
297 // }
298 // $this->zip_progress->progress($i + 1 . '/' . $max);
299 // }
300 //
301 // if (($i + 1) % 500 === 0 || $i == 0) {
302 // if (($i + 1) < $max) {
303 // $this->zip_progress->log((__("Milestone: ", 'backup-backup') . ($i + 1) . '/' . $max), 'info');
304 // }
305 // }
306 // }
307 //
308 // if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.abort')) {
309 //
310 // // close the archive
311 // $lib->close();
312 // } else {
313 // $this->zip_progress->log((__("Milestone: ", 'backup-backup') . $max . '/' . $max), 'info');
314 // $this->zip_progress->log(__("Compressed ", 'backup-backup') . $max . __(" files", 'backup-backup'), 'SUCCESS');
315 //
316 // // Log time of ZIP Process
317 // $this->zip_progress->log(__("Archiving of ", 'backup-backup') . $max . __(" files took: ", 'backup-backup') . number_format(microtime(true) - $this->start_zip, 2) . 's');
318 //
319 // $this->zip_progress->log(__("Finalizing backup", 'backup-backup'), 'STEP');
320 // $this->zip_progress->log(__("Adding manifest...", 'backup-backup'), 'INFO');
321 // $this->zip_progress->log(__("Closing files and archives", 'backup-backup'), 'STEP');
322 // $this->zip_progress->log('#001', 'END-CODE');
323 //
324 // $this->zip_progress->end();
325 // $logs = file_get_contents(BMI_BACKUPS . DIRECTORY_SEPARATOR . 'latest.log');
326 // $this->zip_progress->start(true);
327 //
328 // if ($database_file !== false) {
329 // if (Dashboard\bmi_get_config('OTHER:BACKUP:DB:SINGLE:FILE') == 'true') {
330 // if (file_exists($database_file)) {
331 // $lib->addFile($database_file, 'bmi_database_backup.sql');
332 // }
333 // } else {
334 // if ($db_exporter_files && sizeof($db_exporter_files) > 0) {
335 // for ($i = 0; $i < sizeof($db_exporter_files); ++$i) {
336 // $lib->addFile($db_exporter_files[$i], 'db_tables' . DIRECTORY_SEPARATOR . basename($db_exporter_files[$i]));
337 // }
338 // }
339 // }
340 // }
341 //
342 // $lib->addFromString('bmi_backup_manifest.json', $this->zip_progress->createManifest($dbBackupEngine));
343 // $lib->addFromString('bmi_logs_this_backup.log', $logs);
344 // $this->zip_progress->progress($max . '/' . $max);
345 //
346 // // close the archive
347 // $lib->close();
348 // }
349 // }
350
351 // using PclZip
352 if ($this->lib === 2) {
353
354 // All files
355 $max = sizeof($this->org_files);
356
357 // Verbose
358 $legacy = BMI_LEGACY_VERSION;
359 if ($legacy) $legacy = BMI_LEGACY_HARD_VERSION;
360 if (class_exists('\ZipArchive') || class_exists('ZipArchive')) {
361 $this->zip_progress->log(__("ZipArchive is available this process should use ZipArchive", 'backup-backup'), 'INFO');
362 } else {
363 $this->zip_progress->log(__("Using PclZip module to create the backup", 'backup-backup'), 'INFO');
364 }
365 if (!BMI_LEGACY_VERSION) {
366 $this->zip_progress->log(__("Legacy setting: Using server-sided script and cURL based loop for better capabilities", 'backup-backup'), 'INFO');
367 } elseif (!BMI_LEGACY_HARD_VERSION) {
368 $this->zip_progress->log(__("Legacy setting: Using user browser as middleware for full capabilities", 'backup-backup'), 'INFO');
369 } else {
370
371 $this->zip_progress->log(__("Legacy setting: Using default modules depending on user server", 'backup-backup'), 'INFO');
372
373 // Create DB Dump
374 $this->createDatabaseDump($dbbackupname, $better_database_files_dir, $database_file, $database_file_dir, $dbBackupEngine);
375
376 $this->zip_progress->log(__("Making archive", 'backup-backup'), 'STEP');
377 $this->zip_progress->log(__("Compressing...", 'backup-backup'), 'INFO');
378
379 }
380
381 // Run the backup in background
382 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')) {
383 file_put_contents($database_file_dir . 'bmi_backup_manifest.json', $this->zip_progress->createManifest($dbBackupEngine));
384 // $url = plugins_url('') . '/backup-backup/includes/middleware-backup-proxy.php';
385 $url = admin_url('admin-ajax.php');
386 $identy = 'BMI-' . rand(10000000, 99999999);
387 $remote_settings = [
388 'identy' => $identy,
389 'manifest' => $database_file_dir . 'bmi_backup_manifest.json',
390 'backupname' => $this->backupname,
391 'safelimit' => $safe_limit,
392 'rev' => BMI_REV,
393 'total_files' => sizeof($this->org_files),
394 'filessofar' => 0,
395 'start' => microtime(true),
396 'config_dir' => BMI_CONFIG_DIR,
397 'content_dir' => trailingslashit(WP_CONTENT_DIR),
398 'backup_dir' => BMI_BACKUPS,
399 'abs_dir' => trailingslashit(ABSPATH),
400 'root_dir' => plugin_dir_path(BMI_ROOT_FILE),
401 'browser' => false,
402 'shareallowed' => BMP::canShareLogsOrShouldAsk(),
403 'dbiteratio' => 0,
404 'it' => 0,
405 'dbit' => 0,
406 'dblast' => 0,
407 'bmitmp' => BMI_TMP,
408 'url' => $url,
409 'db_v2_engine' => false,
410 'final_made' => false,
411 'final_batch' => false,
412 'dbitJustFinished' => false,
413 'useragent' => isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'WordPress.org Site Self Request'
414 ];
415 $fix = true;
416 $Xfiles = glob(BMI_TMP . DIRECTORY_SEPARATOR . '.BMI-*');
417 foreach ($Xfiles as $xfile) if (is_file($xfile)) unlink($xfile);
418 touch(BMI_TMP . DIRECTORY_SEPARATOR . '.' . $identy);
419
420 if ($fix === true) {
421 if (BMI_LEGACY_HARD_VERSION === false && $cron === false) {
422 $remote_settings['browser'] = true;
423 $this->zip_progress->log(__("Saving backup configuration for current session...", 'backup-backup'), 'INFO');
424 $this->saveRemoteSettings($remote_settings);
425 $this->zip_progress->log(__("Sending confirmation to user browser to keep pinging the process.", 'backup-backup'), 'INFO');
426 BMP::res(['status' => 'background_hard', 'filename' => $this->backupname, 'url' => $url]);
427 exit;
428 } else {
429 $this->zip_progress->log(__("Saving backup configuration for current session...", 'backup-backup'), 'INFO');
430 $this->saveRemoteSettings($remote_settings);
431 $this->zip_progress->log(__('Starting background process on server-side...', 'backup-backup'), 'INFO');
432 require_once BMI_INCLUDES . '/backup-process.php';
433 $request = new Bypasser($identy, BMI_CONFIG_DIR, trailingslashit(WP_CONTENT_DIR), BMI_BACKUPS, trailingslashit(ABSPATH), plugin_dir_path(BMI_ROOT_FILE));
434 $request->send_beat(true, $this->zip_progress);
435 }
436 }
437
438 sleep(2);
439 if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.running')) {
440 if (file_exists(BMI_TMP . DIRECTORY_SEPARATOR . '.' . $identy . '-running')) {
441 // $this->zip_progress->log(__('Request received correctly – backup is running.', 'backup-backup'), 'SUCCESS');
442 if ($cron === true) return ['status' => 'background', 'filename' => $this->backupname];
443 else BMP::res(['status' => 'background', 'filename' => $this->backupname]);
444 exit;
445 } else {
446 $this->zip_progress->log(__('Could not find any response from the server, trying again in 3 seconds.', 'backup-backup'), 'WARN');
447 sleep(3);
448 if (file_exists(BMI_TMP . DIRECTORY_SEPARATOR . '.' . $identy . '-running')) {
449 // $this->zip_progress->log(__('Request received correctly – backup is running.', 'backup-backup'), 'SUCCESS');
450 if ($cron === true) return ['status' => 'background', 'filename' => $this->backupname];
451 else BMP::res(['status' => 'background', 'filename' => $this->backupname]);
452 exit;
453 } else {
454 $this->zip_progress->log(__('Still nothing backup probably is not running.', 'backup-backup'), 'WARN');
455 if (file_exists(BMI_TMP . DIRECTORY_SEPARATOR . '.' . $identy . '-running')) @unlink(BMI_TMP . DIRECTORY_SEPARATOR . '.' . $identy . '-running');
456 if (file_exists(BMI_TMP . DIRECTORY_SEPARATOR . '.' . $identy)) @unlink(BMI_TMP . DIRECTORY_SEPARATOR . '.' . $identy);
457 throw new \Exception('Backup could not run on your server, please check global logs.');
458 }
459 }
460 } else {
461 if ($cron === true) return ['status' => 'background', 'filename' => $this->backupname];
462 else BMP::res(['status' => 'background', 'filename' => $this->backupname]);
463 exit;
464 }
465
466 // ob_end_clean();
467 exit;
468 } else {
469 if (defined('BMI_USING_CLI_FUNCTIONALITY') && BMI_USING_CLI_FUNCTIONALITY === true) {
470 $this->zip_progress->log(__("Backup is running under PHP CLI environment.", 'backup-backup'), 'INFO');
471 } else {
472 $this->zip_progress->log(__("Backup will run as single-request, may be unstable...", 'backup-backup'), 'WARN');
473 }
474 }
475
476 if ($this->dbDumped === false) {
477 $this->createDatabaseDump($dbbackupname, $better_database_files_dir, $database_file, $database_file_dir);
478 }
479
480 $zipArchive = false;
481 if (class_exists('\ZipArchive') || class_exists('ZipArchive')) {
482 if (!isset($zip)) {
483 $zip = new \ZipArchive();
484 }
485
486 if ($zip) {
487 $zipArchive = true;
488 } else {
489 $zipArchive = false;
490 }
491 }
492
493 // Check if PclZip exists
494 if ($zipArchive === false) {
495 if (!class_exists('PclZip')) {
496 if (!defined('PCLZIP_TEMPORARY_DIR')) {
497 $bmi_tmp_dir = BMI_TMP;
498 if (!file_exists($bmi_tmp_dir)) {
499 @mkdir($bmi_tmp_dir, 0775, true);
500 }
501
502 define('PCLZIP_TEMPORARY_DIR', $bmi_tmp_dir . DIRECTORY_SEPARATOR . 'bmi-');
503 }
504
505 if (!defined('PCLZIP_READ_BLOCK_SIZE')) {
506 define('PCLZIP_READ_BLOCK_SIZE', 32768);
507 }
508 }
509
510 // Require the LIB and check if it's compatible
511 if (defined('BMI_PRO_PCLZIP') && file_exists(BMI_PRO_PCLZIP)) {
512 $this->zip_progress->log(__('Using dedicated PclZIP for Premium Users.', 'backup-backup'), 'INFO');
513 require_once BMI_PRO_PCLZIP;
514 } else {
515 require_once trailingslashit(ABSPATH) . 'wp-admin/includes/class-pclzip.php';
516 }
517
518 // Get/Create the Archive
519 if (!$lib = new \PclZip($this->new_file_path)) {
520 throw new \Exception('PHP-ZIP: Permission Denied or zlib can\'t be found');
521 }
522
523 } else {
524
525 if (file_exists($this->new_file_path)) $zip->open($this->new_file_path);
526 else $zip->open($this->new_file_path, \ZipArchive::CREATE);
527
528 $this->zip_progress->log('Using ZipArchive extension for this backup process.', 'INFO');
529
530 }
531
532 // require the lib
533 // if (!class_exists('PclZip')) {
534 // if (!defined('PCLZIP_TEMPORARY_DIR')) {
535 // $bmi_tmp_dir = BMI_TMP;
536 // if (!file_exists($bmi_tmp_dir)) {
537 // @mkdir($bmi_tmp_dir, 0775, true);
538 // }
539 // define('PCLZIP_TEMPORARY_DIR', $bmi_tmp_dir . DIRECTORY_SEPARATOR . 'bmi-');
540 // }
541 // if (defined('BMI_PRO_PCLZIP') && file_exists(BMI_PRO_PCLZIP)) {
542 // $this->zip_progress->log(__('Using dedicated PclZIP for Premium Users.', 'backup-backup'), 'INFO');
543 // require_once BMI_PRO_PCLZIP;
544 // } else {
545 // require_once trailingslashit(ABSPATH) . 'wp-admin/includes/class-pclzip.php';
546 // }
547 // }
548 $common = $this->org_files;
549
550 if ($this->dbDumped === true) {
551 try {
552
553 $this->zip_progress->log(__('Adding database SQL file(s) to the backup file.', 'backup-backup'), 'STEP');
554
555 $files = [];
556
557 if ($database_file !== false && !($this->db_exporter_files && sizeof($this->db_exporter_files) > 0)) {
558 $files[] = $database_file;
559 }
560
561 $this->zip_progress->log('Database files in total found: ' . sizeof($this->db_exporter_files), 'VERBOSE');
562 if ($this->db_exporter_files && sizeof($this->db_exporter_files) > 0) {
563 for ($i = 0; $i < sizeof($this->db_exporter_files); ++$i) {
564 $files[] = $this->db_exporter_files[$i];
565 }
566 } else {
567 $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');
568 $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');
569 }
570
571 $files = array_filter($files, function ($path) {
572 if (is_readable($path) && is_writable($path) && !is_link($path)) return true;
573 else {
574 $this->zip_progress->log(sprintf(__("Excluding file that cannot be read: %s", 'backup-backup'), $path), 'warn');
575 return false;
576 }
577 });
578
579 if (sizeof($files) > 0) {
580 if ($zipArchive) {
581 for ($i = 0; $i < sizeof($files); ++$i) {
582
583 // Add the file
584 if (is_dir($files[$i])) {
585 $zip->addEmptyDir($this->cutDir($files[$i]));
586 } else {
587 $zip->addFile($files[$i], $this->cutDir($files[$i]));
588 }
589
590 }
591
592 $zAresult = $zip->close();
593 if ($zAresult !== true) {
594
595 $this->zip_failed('Error, there is most likely not enough space for the backup.');
596 return false;
597
598 }
599 $zip->open($this->new_file_path);
600 } else {
601 $dbback = $lib->add($files, PCLZIP_OPT_REMOVE_PATH, $database_file_dir, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $safe_limit);
602
603 if ($dbback == 0) {
604 $this->zip_failed($lib->errorInfo(true));
605 return false;
606 }
607 }
608 }
609
610 } catch (\Exception $e) {
611 $this->zip_failed($e->getMessage());
612
613 return false;
614 } catch (\Throwable $e) {
615 $this->zip_failed($e->getMessage());
616
617 return false;
618 }
619
620 if (sizeof($files) > 0) {
621 $this->zip_progress->log(__('Database added to the backup successfully.', 'backup-backup'), 'SUCCESS');
622 } else {
623 $this->zip_progress->log(__('Database was not added to the backup.', 'backup-backup'), 'WARN');
624 }
625 }
626
627 $this->zip_progress->log(__('Performing site files backup...', 'backup-backup'), 'STEP');
628
629 try {
630 $splitby = 200; $milestoneby = 500;
631 $filestotal = sizeof($this->org_files);
632 if ($filestotal < 3000) { $splitby = 250; $milestoneby = 500; }
633 if ($filestotal > 5000) { $splitby = 500; $milestoneby = 500; }
634 if ($filestotal > 10000) { $splitby = 1000; $milestoneby = 1000; }
635 if ($filestotal > 15000) { $splitby = 2000; $milestoneby = 2000; }
636 if ($filestotal > 20000) { $splitby = 4000; $milestoneby = 4000; }
637 if ($filestotal > 25000) { $splitby = 6000; $milestoneby = 6000; }
638 if ($filestotal > 30000) { $splitby = 8000; $milestoneby = 8000; }
639 if ($filestotal > 32000) { $splitby = 10000; $milestoneby = 10000; }
640 if ($filestotal > 60500) { $splitby = 20000; $milestoneby = 20000; }
641 if ($filestotal > 90500) { $splitby = 40000; $milestoneby = 40000; }
642
643 $this->zip_progress->log(__("Chunks contain ", 'backup-backup') . $splitby . __(" files.", 'backup-backup'));
644
645 $chunks = array_chunk($this->org_files, $splitby);
646 $chunkslen = sizeof($chunks);
647 if ($chunkslen > 0) {
648 $sizeoflast = sizeof($chunks[$chunkslen - 1]);
649 if ($chunkslen > 1 && $sizeoflast == 1) {
650 $buffer = array_slice($chunks[$chunkslen - 2], -1);
651 $chunks[$chunkslen - 2] = array_slice($chunks[$chunkslen - 2], 0, -1);
652 $chunks[$chunkslen - 1][] = $buffer[0];
653 }
654 }
655
656 $backupSize = 0;
657 for ($i = 0; $i < $chunkslen; ++$i) {
658
659 // Abort if user wants it (check every 100 files)
660 if (file_exists(BMI_BACKUPS . '/.abort')) {
661 break;
662 }
663
664 $back = 0;
665 $chunk = $chunks[$i];
666 $chunk = array_filter($chunk, function ($path) {
667 if (is_readable($path) && file_exists($path) && !is_link($path)) return true;
668 else {
669 $this->zip_progress->log(sprintf(__("Excluding file that cannot be read: %s", 'backup-backup'), $path), 'warn');
670 return false;
671 }
672 });
673
674 if (sizeof($chunk) > 0) {
675 if ($zipArchive) {
676 for ($j = 0; $j < sizeof($chunk); ++$j) {
677
678 $path = 'wordpress' . DIRECTORY_SEPARATOR . substr($chunk[$j], strlen(ABSPATH));
679
680 // Add the file
681 $path = str_replace('\\', '/', $path);
682 if (is_dir($chunk[$j])) {
683 $zip->addEmptyDir($path);
684 } else {
685 $zip->addFile($chunk[$j], $path);
686 }
687
688 }
689
690 $zAresult = $zip->close();
691 if ($zAresult !== true) {
692
693 $this->zip_failed('Error, there is most likely not enough space for the backup.');
694 return false;
695
696 }
697 $zip->open($this->new_file_path);
698 } else {
699
700 $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);
701
702 if ($back == 0) {
703 $this->zip_failed($lib->errorInfo(true));
704 return false;
705 }
706
707 }
708 }
709
710 $curfile = (($i * $splitby) + $splitby);
711 $this->zip_progress->progress($curfile . '/' . $max);
712 if ($curfile % $milestoneby === 0 && $curfile < $max) {
713 if (!file_exists($this->new_file_path))
714 return $this->zip_failed('Expected backup file does not exist, there could be some issue or the backup was removed by third parties.');
715
716 $currentBackupSize = filesize($this->new_file_path);
717 if ($backupSize > $currentBackupSize)
718 return $this->zip_failed('Backup size is smaller than it should be, it has been damaged, it may not be restorable, abort recommended.');
719
720 $backupSize = $currentBackupSize;
721
722 $this->zip_progress->log(__("Milestone: ", 'backup-backup') . ($curfile . '/' . $max), 'info');
723 }
724 }
725
726 } catch (\Exception $e) {
727 $this->zip_failed($e->getMessage());
728
729 return false;
730 } catch (\Throwable $e) {
731 $this->zip_failed($e->getMessage());
732
733 return false;
734 }
735
736 if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.abort')) {
737
738 $this->zip_progress->log('#002', 'END-CODE');
739
740 if (file_exists($database_file_dir . 'bmi_backup_manifest.json')) {
741 @unlink($database_file_dir . 'bmi_backup_manifest.json');
742 }
743 if (file_exists($database_file_dir . 'bmi_logs_this_backup.log')) {
744 @unlink($database_file_dir . 'bmi_logs_this_backup.log');
745 }
746
747 } else {
748
749 // End
750 $this->zip_progress->log(__("Milestone: ", 'backup-backup') . ($max . '/' . $max), 'info');
751 $this->zip_progress->log(__("Compressed ", 'backup-backup') . $max . __(" files", 'backup-backup'), 'SUCCESS');
752
753 // Log time of ZIP Process
754 $this->zip_progress->log(__("Archiving of ", 'backup-backup') . $max . __(" files took: ", 'backup-backup') . number_format(microtime(true) - $this->start_zip, 2) . 's');
755
756 $this->zip_progress->log(__("Finalizing backup", 'backup-backup'), 'STEP');
757 $this->zip_progress->log(__("Generating manifest file and saving current live-log...", 'backup-backup'), 'INFO');
758
759 file_put_contents($database_file_dir . 'bmi_backup_manifest.json', $this->zip_progress->createManifest($dbBackupEngine));
760 file_put_contents($database_file_dir . 'bmi_logs_this_backup.log', file_get_contents(BMI_BACKUPS . DIRECTORY_SEPARATOR . 'latest.log'));
761
762 sleep(1);
763
764 $files = [];
765
766 if (file_exists($database_file_dir . 'bmi_logs_this_backup.log')) $files[] = $database_file_dir . 'bmi_logs_this_backup.log';
767 if (file_exists($database_file_dir . 'bmi_backup_manifest.json')) $files[] = $database_file_dir . 'bmi_backup_manifest.json';
768 else {
769
770 $this->zip_failed('Manifest file could not be added, manifest does not exist.');
771 return false;
772
773 }
774
775 $this->zip_progress->log(__("Adding manifest...", 'backup-backup'), 'INFO');
776 try {
777
778 if ($zipArchive) {
779 for ($i = 0; $i < sizeof($files); ++$i) {
780
781 // Add the file
782 if (is_dir($files[$i])) {
783 $zip->addEmptyDir($this->cutDir($files[$i]));
784 } else {
785 $zip->addFile($files[$i], $this->cutDir($files[$i]));
786 }
787
788 }
789
790 $zAresult = $zip->close();
791 if ($zAresult !== true) {
792
793 $this->zip_failed('Error, there is most likely not enough space for the backup.');
794 return false;
795
796 }
797 } else {
798
799 $maback = $lib->add($files, PCLZIP_OPT_REMOVE_PATH, $database_file_dir, PCLZIP_OPT_ADD_TEMP_FILE_ON, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $safe_limit);
800
801 if ($maback == 0) {
802 $this->zip_failed($lib->errorInfo(true));
803 return false;
804 }
805
806 }
807
808 } catch (\Exception $e) {
809 $this->zip_failed($e->getMessage());
810
811 return false;
812 } catch (\Throwable $e) {
813 $this->zip_failed($e->getMessage());
814
815 return false;
816 }
817
818 if (file_exists($database_file_dir . 'bmi_backup_manifest.json')) {
819 @unlink($database_file_dir . 'bmi_backup_manifest.json');
820 }
821 if (file_exists($database_file_dir . 'bmi_logs_this_backup.log')) {
822 @unlink($database_file_dir . 'bmi_logs_this_backup.log');
823 }
824
825 $this->zip_progress->progress($max . '/' . $max);
826
827 $this->zip_progress->log(__("Manifest file and logs added to the backup. Temporary files removed.", 'backup-backup'), 'SUCCESS');
828
829 }
830 }
831
832 $this->zip_progress->log(__("Closing files and archives", 'backup-backup'), 'STEP');
833
834 // Remove Better DB SQL Files
835 if ($this->db_exporter_files && sizeof($this->db_exporter_files) > 0) {
836 for ($i = 0; $i < sizeof($this->db_exporter_files); ++$i) {
837 if (file_exists($this->db_exporter_files[$i])) @unlink($this->db_exporter_files[$i]);
838 }
839 if (file_exists($better_database_files_dir) && is_dir($better_database_files_dir)) {
840 @rmdir($better_database_files_dir);
841 }
842 }
843
844 if ($database_file && file_exists($database_file)) @unlink($database_file);
845 if (!file_exists($this->new_file_path)) {
846 $this->zip_failed('PHP-ZIP: After doing the zipping file can not be found');
847 }
848 if (filesize($this->new_file_path) === 0) {
849 $this->zip_failed('PHP-ZIP: After doing the zipping file size is still 0 bytes');
850 }
851
852 // empty the array
853 $this->org_files = [];
854
855 $this->zip_progress->log(__("Cleanup finished, backup complete.", 'backup-backup'), 'SUCCESS');
856 // $this->zip_progress->log('#001', 'END-CODE');
857 return true;
858
859 }
860
861 public function zip_files($files, $to) {
862 $this->zip_start($to);
863 $this->zip_add($files);
864
865 return $this->zip_end();
866 }
867
868 public function unzip_file($file_path, $target_dir = null, &$zip_progress = null) {
869
870 // Progress
871 $this->zip_progress = $zip_progress;
872
873 // if it doesn't exist
874 if (!file_exists($file_path)) {
875 throw new \Exception("PHP-ZIP: File doesn't Exist");
876 }
877
878 $this->extr_file = $file_path;
879
880 // if (class_exists("ZipArchive")) $this->lib = 1;
881 // else $this->lib = 2;
882 $this->lib = 2;
883
884 if ($target_dir !== null) {
885 return $this->unzip_to($target_dir);
886 } else {
887 return true;
888 }
889 }
890
891 public function extract_files($zip_path, $files, $target_dir = null, &$zip_progress = null, $isFirstExtract = true) {
892
893 $this->zip_progress = $zip_progress;
894
895 // it exists, but it's not a directory
896 if (file_exists($target_dir) && (!is_dir($target_dir))) {
897 throw new \Exception("PHP-ZIPv2: Target directory exists as a file not a directory");
898 }
899 // it doesn't exist
900 if (!file_exists($target_dir)) {
901 if (!mkdir($target_dir)) {
902 throw new \Exception("PHP-ZIPv2: Directory not found, and unable to create it");
903 }
904 }
905 // validations -- end //
906 // TODO: NOTICE: Force disable PCLZIP
907 if (class_exists("ZipArchive") || class_exists("\ZipArchive")) {
908
909 $zip = new \ZipArchive;
910 $res = $zip->open($zip_path);
911
912 if ($res === true) {
913
914 if ($isFirstExtract) {
915 $this->zip_progress->log(__("Using ZipArchive, omiting memory limit calculations...", 'backup-backup'), 'INFO');
916 }
917
918 $zip->extractTo($target_dir, $files);
919 $zip->close();
920 return true;
921
922 } else {
923
924 $this->restore_failed('PHP-ZIPv2: Could not open Backup with ZipArchive.');
925 return false;
926
927 }
928
929 } else {
930
931 if ($isFirstExtract) {
932 $this->zip_progress->log(__("ZipArchive is not available, using PclZIP.", 'backup-backup'), 'INFO');
933 }
934
935 $safe_limit = $this->smartMemory($isFirstExtract);
936 $this->loadPclZip($isFirstExtract);
937 $lib = new \PclZip($zip_path);
938 $restor = $lib->extract(PCLZIP_OPT_BY_NAME, $files, PCLZIP_OPT_PATH, $target_dir, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $safe_limit);
939
940 if ($restor == 0) {
941
942 $this->restore_failed($lib->errorInfo(true));
943 return false;
944
945 }
946
947 return true;
948
949 }
950
951 }
952
953 public function loadPclZip($isFirstExtract = true) {
954
955 if (!class_exists('PclZip')) {
956 if (!defined('PCLZIP_TEMPORARY_DIR')) {
957 $bmi_tmp_dir = BMI_TMP;
958 if (!file_exists($bmi_tmp_dir)) {
959 @mkdir($bmi_tmp_dir, 0775, true);
960 }
961 define('PCLZIP_TEMPORARY_DIR', $bmi_tmp_dir . DIRECTORY_SEPARATOR . 'bmi-');
962 }
963
964 if (defined('BMI_PRO_PCLZIP') && file_exists(BMI_PRO_PCLZIP)) {
965 require_once BMI_PRO_PCLZIP;
966 if ($isFirstExtract) {
967 $this->zip_progress->log(__('Using dedicated PclZIP for Premium Users.', 'backup-backup'), 'INFO');
968 }
969 } else {
970 require_once trailingslashit(ABSPATH) . 'wp-admin/includes/class-pclzip.php';
971 }
972 }
973
974 }
975
976 public function smartMemory($isFirstExtract = true) {
977
978 // Smart memory -- start //
979 if ($this->zip_progress != null && $isFirstExtract) {
980 $this->zip_progress->log(__("Smart memory calculation...", 'backup-backup'), 'STEP');
981 }
982
983 if ((intval($this->return_bytes(ini_get('memory_limit'))) / 1024 / 1024) < 384) {
984 @ini_set('memory_limit', '384M');
985 }
986
987 $memory_limit = (intval($this->return_bytes(ini_get('memory_limit'))) / 1024 / 1024);
988 if ($this->zip_progress != null && $isFirstExtract) {
989 $this->zip_progress->log(str_replace('%s', $memory_limit, __("There is %s MBs of memory to use", 'backup-backup')), 'INFO');
990 }
991
992 $safe_limit = intval($memory_limit / 4);
993 if ($safe_limit > 64) $safe_limit = 64;
994 if ($memory_limit === 384) $safe_limit = 78;
995 if ($memory_limit >= 512) $safe_limit = 104;
996 if ($memory_limit >= 1024) $safe_limit = 228;
997 if ($memory_limit >= 2048) $safe_limit = 428;
998
999 // $real_memory = intval(memory_get_usage() * 0.9 / 1024 / 1024);
1000 // if ($real_memory < $safe_limit) $safe_limit = $real_memory;
1001 $safe_limit = intval($safe_limit * 0.8);
1002
1003 if ($this->zip_progress != null && $isFirstExtract) {
1004 $this->zip_progress->log(str_replace('%s', $safe_limit, __("Setting the safe limit to %s MB", 'backup-backup')), 'SUCCESS');
1005 }
1006 // Smart memory -- end //
1007
1008 return $safe_limit;
1009
1010 }
1011
1012 public function unzip_to($target_dir) {
1013
1014 // validations -- start //
1015 if ($this->lib === 0 && $this->extr_file === 0) {
1016 throw new \Exception("PHP-ZIP: unzip_file hasn't been called");
1017 }
1018 // it exists, but it's not a directory
1019 if (file_exists($target_dir) && (!is_dir($target_dir))) {
1020 throw new \Exception("PHP-ZIP: Target directory exists as a file not a directory");
1021 }
1022 // it doesn't exist
1023 if (!file_exists($target_dir)) {
1024 if (!mkdir($target_dir)) {
1025 throw new \Exception("PHP-ZIP: Directory not found, and unable to create it");
1026 }
1027 }
1028 // validations -- end //
1029
1030 // Target Directory
1031 $this->extr_dirc = $target_dir;
1032
1033 // Smart Memory
1034 $safe_limit = $this->smartMemory();
1035
1036 // Extract msg
1037 $this->zip_progress->log(__('Extracting files into temporary directory (this process can take some time)...', 'backup_migration'), 'STEP');
1038
1039 // Force PCL Zip
1040 $this->lib = 2;
1041
1042 // extract using ZipArchive
1043 // if($this->lib === 1) {
1044 // $lib = new \ZipArchive;
1045 // if(!$lib->open($this->extr_file)) throw new \Exception("PHP-ZIP: Unable to open the zip file");
1046 // if(!$lib->extractTo($this->extr_dirc)) throw new \Exception("PHP-ZIP: Unable to extract files");
1047 // $lib->close();
1048 // }
1049
1050 // extarct using PclZip
1051 if ($this->lib === 2) {
1052 $this->loadPclZip();
1053 $lib = new \PclZip($this->extr_file);
1054 $restor = $lib->extract(PCLZIP_OPT_PATH, $this->extr_dirc, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $safe_limit);
1055 if ($restor == 0) {
1056 $this->restore_failed($lib->errorInfo(true));
1057
1058 return false;
1059 }
1060 }
1061
1062 return true;
1063 }
1064
1065 private function dir_to_assoc_arr(DirectoryIterator $dir) {
1066 $data = [];
1067 foreach ($dir as $node) {
1068 if ($node->isDir() && !$node->isDot()) {
1069 $data[$node->getFilename()] = $this->dir_to_assoc_arr(new DirectoryIterator($node->getPathname()));
1070 } elseif ($node->isFile()) {
1071 $data[] = $node->getFilename();
1072 }
1073 }
1074
1075 return $data;
1076 }
1077
1078 private function path() {
1079 return join(DIRECTORY_SEPARATOR, func_get_args());
1080 }
1081
1082 private function commonPath($files, $remove = true) {
1083 foreach ($files as $index => $filesStr) {
1084 $files[$index] = explode(DIRECTORY_SEPARATOR, $filesStr);
1085 }
1086 $toDiff = $files;
1087 foreach ($toDiff as $arr_i => $arr) {
1088 foreach ($arr as $name_i => $name) {
1089 $toDiff[$arr_i][$name_i] = $name . "___" . $name_i;
1090 }
1091 }
1092 $diff = call_user_func_array("array_diff", $toDiff);
1093 reset($diff);
1094 $i = key($diff) - 1;
1095 if ($remove) {
1096 foreach ($files as $index => $arr) {
1097 $files[$index] = implode(DIRECTORY_SEPARATOR, array_slice($files[$index], $i));
1098 }
1099 } else {
1100 foreach ($files as $index => $arr) {
1101 $files[$index] = implode(DIRECTORY_SEPARATOR, array_slice($files[$index], 0, $i));
1102 }
1103 }
1104
1105 return $files;
1106 }
1107 }
1108