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