PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 2.0.0
Backup Migration v2.0.0
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 10 months ago
zip.php
1144 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->handle_batch();
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 $chunk = array_values($chunk);
679
680 if (sizeof($chunk) > 0) {
681 $needManipulation = false;
682 if (strpos(WP_CONTENT_DIR, ABSPATH) === false) {
683 $needManipulation = true;
684 }
685 if ($zipArchive) {
686 for ($j = 0; $j < sizeof($chunk); ++$j) {
687
688 if ($needManipulation) {
689 if (strpos($chunk[$j], WP_CONTENT_DIR) !== false) {
690 $path = 'wordpress' . DIRECTORY_SEPARATOR . 'wp-content' . DIRECTORY_SEPARATOR . substr($chunk[$j], strlen(WP_CONTENT_DIR));
691 } else {
692 $path = 'wordpress' . DIRECTORY_SEPARATOR . substr($chunk[$j], strlen(ABSPATH));
693 }
694 } else {
695 $path = 'wordpress' . DIRECTORY_SEPARATOR . substr($chunk[$j], strlen(ABSPATH));
696 }
697
698 // Add the file
699 $path = BMP::fixSlashes($path);
700 if (is_dir($chunk[$j])) {
701 $zip->addEmptyDir($path);
702 } else {
703 $zip->addFile($chunk[$j], $path);
704 }
705
706 }
707
708 $zAresult = $zip->close();
709 if ($zAresult !== true) {
710 $this->zip_progress->log('not_enough_space', 'verbose');
711 $this->zip_failed('Error, there is most likely not enough space for the backup.');
712 return false;
713
714 }
715 $zip->open($this->new_file_path);
716 } else {
717
718 if ($needManipulation) {
719 $abs = BMP::fixSlashes(ABSPATH) . DIRECTORY_SEPARATOR;
720 $content = BMP::fixSlashes(WP_CONTENT_DIR) . DIRECTORY_SEPARATOR;
721 $coreFiles = [];
722 $contentFiles = [];
723 foreach ($chunk as $file) {
724 if (strpos($file, $content) !== false) {
725 $contentFiles[] = $file;
726 } else {
727 $coreFiles[] = $file;
728 }
729 }
730
731 $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);
732 $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);
733 $back = $back_1 && $back_2;
734
735 } else {
736 $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);
737 }
738 if ($back == 0) {
739 $this->zip_failed($lib->errorInfo(true));
740 return false;
741 }
742
743 }
744 }
745
746 $curfile = (($i * $splitby) + $splitby);
747 $this->zip_progress->progress($curfile . '/' . $max);
748 if ($curfile % $milestoneby === 0 && $curfile < $max) {
749 if (!file_exists($this->new_file_path))
750 return $this->zip_failed('Expected backup file does not exist, there could be some issue or the backup was removed by third parties.');
751
752 $currentBackupSize = filesize($this->new_file_path);
753 if ($backupSize > $currentBackupSize)
754 return $this->zip_failed('Backup size is smaller than it should be, it has been damaged, it may not be restorable, abort recommended.');
755
756 $backupSize = $currentBackupSize;
757
758 $this->zip_progress->log(__("Milestone: ", 'backup-backup') . ($curfile . '/' . $max), 'info');
759 }
760 }
761
762 } catch (\Exception $e) {
763 $this->zip_failed($e->getMessage());
764
765 return false;
766 } catch (\Throwable $e) {
767 $this->zip_failed($e->getMessage());
768
769 return false;
770 }
771
772 if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . '.abort')) {
773
774 $this->zip_progress->log('#002', 'END-CODE');
775
776 if (file_exists($database_file_dir . 'bmi_backup_manifest.json')) {
777 @unlink($database_file_dir . 'bmi_backup_manifest.json');
778 }
779 if (file_exists($database_file_dir . 'bmi_logs_this_backup.log')) {
780 @unlink($database_file_dir . 'bmi_logs_this_backup.log');
781 }
782
783 } else {
784
785 // End
786 $this->zip_progress->log(__("Milestone: ", 'backup-backup') . ($max . '/' . $max), 'info');
787 $this->zip_progress->log(__("Compressed ", 'backup-backup') . $max . __(" files", 'backup-backup'), 'SUCCESS');
788
789 // Log time of ZIP Process
790 $this->zip_progress->log(__("Archiving of ", 'backup-backup') . $max . __(" files took: ", 'backup-backup') . number_format(microtime(true) - $this->start_zip, 2) . 's');
791
792 $this->zip_progress->log(__("Finalizing backup", 'backup-backup'), 'STEP');
793 $this->zip_progress->log(__("Generating manifest file and saving current live-log...", 'backup-backup'), 'INFO');
794
795 file_put_contents($database_file_dir . 'bmi_backup_manifest.json', $this->zip_progress->createManifest($dbBackupEngine));
796 file_put_contents($database_file_dir . 'bmi_logs_this_backup.log', file_get_contents(BMI_BACKUPS . DIRECTORY_SEPARATOR . 'latest.log'));
797
798 sleep(1);
799
800 $files = [];
801
802 if (file_exists($database_file_dir . 'bmi_logs_this_backup.log')) $files[] = $database_file_dir . 'bmi_logs_this_backup.log';
803 if (file_exists($database_file_dir . 'bmi_backup_manifest.json')) $files[] = $database_file_dir . 'bmi_backup_manifest.json';
804 else {
805
806 $this->zip_failed('Manifest file could not be added, manifest does not exist.');
807 return false;
808
809 }
810
811 $this->zip_progress->log(__("Adding manifest...", 'backup-backup'), 'INFO');
812 try {
813
814 if ($zipArchive) {
815 for ($i = 0; $i < sizeof($files); ++$i) {
816
817 // Add the file
818 if (is_dir($files[$i])) {
819 $zip->addEmptyDir($this->cutDir($files[$i]));
820 } else {
821 $zip->addFile($files[$i], $this->cutDir($files[$i]));
822 }
823
824 }
825
826 $zAresult = $zip->close();
827 if ($zAresult !== true) {
828 $this->zip_progress->log('not_enough_space', 'verbose');
829 $this->zip_failed('Error, there is most likely not enough space for the backup.');
830 return false;
831
832 }
833 } else {
834
835 $maback = $lib->add($files, PCLZIP_OPT_REMOVE_PATH, $database_file_dir, PCLZIP_OPT_ADD_TEMP_FILE_ON, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $safe_limit);
836
837 if ($maback == 0) {
838 $this->zip_failed($lib->errorInfo(true));
839 return false;
840 }
841
842 }
843
844 } catch (\Exception $e) {
845 $this->zip_failed($e->getMessage());
846
847 return false;
848 } catch (\Throwable $e) {
849 $this->zip_failed($e->getMessage());
850
851 return false;
852 }
853
854 if (file_exists($database_file_dir . 'bmi_backup_manifest.json')) {
855 @unlink($database_file_dir . 'bmi_backup_manifest.json');
856 }
857 if (file_exists($database_file_dir . 'bmi_logs_this_backup.log')) {
858 @unlink($database_file_dir . 'bmi_logs_this_backup.log');
859 }
860
861 $this->zip_progress->progress($max . '/' . $max);
862
863 $this->zip_progress->log(__("Manifest file and logs added to the backup. Temporary files removed.", 'backup-backup'), 'SUCCESS');
864
865 }
866 }
867
868 $this->zip_progress->log(__("Closing files and archives", 'backup-backup'), 'STEP');
869
870 // Remove Better DB SQL Files
871 if ($this->db_exporter_files && sizeof($this->db_exporter_files) > 0) {
872 for ($i = 0; $i < sizeof($this->db_exporter_files); ++$i) {
873 if (file_exists($this->db_exporter_files[$i])) @unlink($this->db_exporter_files[$i]);
874 }
875 if (file_exists($better_database_files_dir) && is_dir($better_database_files_dir)) {
876 @rmdir($better_database_files_dir);
877 }
878 }
879
880 if ($database_file && file_exists($database_file)) @unlink($database_file);
881 if (!file_exists($this->new_file_path)) {
882 $this->zip_failed('PHP-ZIP: After doing the zipping file can not be found');
883 }
884 if (filesize($this->new_file_path) === 0) {
885 $this->zip_failed('PHP-ZIP: After doing the zipping file size is still 0 bytes');
886 }
887
888 // empty the array
889 $this->org_files = [];
890
891 $this->zip_progress->log(__("Cleanup finished, backup complete.", 'backup-backup'), 'SUCCESS');
892 // $this->zip_progress->log('#001', 'END-CODE');
893 return true;
894
895 }
896
897 public function zip_files($files, $to) {
898 $this->zip_start($to);
899 $this->zip_add($files);
900
901 return $this->zip_end();
902 }
903
904 public function unzip_file($file_path, $target_dir = null, &$zip_progress = null) {
905
906 // Progress
907 $this->zip_progress = $zip_progress;
908
909 // if it doesn't exist
910 if (!file_exists($file_path)) {
911 throw new \Exception("PHP-ZIP: File doesn't Exist");
912 }
913
914 $this->extr_file = $file_path;
915
916 // if (class_exists("ZipArchive")) $this->lib = 1;
917 // else $this->lib = 2;
918 $this->lib = 2;
919
920 if ($target_dir !== null) {
921 return $this->unzip_to($target_dir);
922 } else {
923 return true;
924 }
925 }
926
927 public function extract_files($zip_path, $files, $target_dir = null, &$zip_progress = null, $isFirstExtract = true) {
928
929 $this->zip_progress = $zip_progress;
930
931 // it exists, but it's not a directory
932 if (file_exists($target_dir) && (!is_dir($target_dir))) {
933 throw new \Exception("PHP-ZIPv2: Target directory exists as a file not a directory");
934 }
935 // it doesn't exist
936 if (!file_exists($target_dir)) {
937 if (!mkdir($target_dir)) {
938 throw new \Exception("PHP-ZIPv2: Directory not found, and unable to create it");
939 }
940 }
941 // validations -- end //
942 // TODO: NOTICE: Force disable PCLZIP
943 if (class_exists("ZipArchive") || class_exists("\ZipArchive")) {
944
945 $zip = new \ZipArchive;
946 $res = $zip->open($zip_path);
947
948 if ($res === true) {
949
950 if ($isFirstExtract) {
951 $this->zip_progress->log(__("Using ZipArchive, omiting memory limit calculations...", 'backup-backup'), 'INFO');
952 }
953
954 $zip->extractTo($target_dir, $files);
955 $zip->close();
956 return true;
957
958 } else {
959
960 $this->restore_failed('PHP-ZIPv2: Could not open Backup with ZipArchive.');
961 return false;
962
963 }
964
965 } else {
966
967 if ($isFirstExtract) {
968 $this->zip_progress->log(__("ZipArchive is not available, using PclZIP.", 'backup-backup'), 'INFO');
969 }
970
971 $safe_limit = $this->smartMemory($isFirstExtract);
972 $this->loadPclZip($isFirstExtract);
973 $lib = new \PclZip($zip_path);
974 $restor = $lib->extract(PCLZIP_OPT_BY_NAME, $files, PCLZIP_OPT_PATH, $target_dir, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $safe_limit);
975
976 if ($restor == 0) {
977
978 $this->restore_failed($lib->errorInfo(true));
979 return false;
980
981 }
982
983 return true;
984
985 }
986
987 }
988
989 public function loadPclZip($isFirstExtract = true) {
990
991 if (!class_exists('PclZip')) {
992 if (!defined('PCLZIP_TEMPORARY_DIR')) {
993 $bmi_tmp_dir = BMI_TMP;
994 if (!file_exists($bmi_tmp_dir)) {
995 @mkdir($bmi_tmp_dir, 0775, true);
996 }
997 define('PCLZIP_TEMPORARY_DIR', $bmi_tmp_dir . DIRECTORY_SEPARATOR . 'bmi-');
998 }
999
1000 if (defined('BMI_PRO_PCLZIP') && file_exists(BMI_PRO_PCLZIP)) {
1001 require_once BMI_PRO_PCLZIP;
1002 if ($isFirstExtract) {
1003 $this->zip_progress->log(__('Using dedicated PclZIP for Premium Users.', 'backup-backup'), 'INFO');
1004 }
1005 } else {
1006 require_once trailingslashit(ABSPATH) . 'wp-admin/includes/class-pclzip.php';
1007 }
1008 }
1009
1010 }
1011
1012 public function smartMemory($isFirstExtract = true) {
1013
1014 // Smart memory -- start //
1015 if ($this->zip_progress != null && $isFirstExtract) {
1016 $this->zip_progress->log(__("Smart memory calculation...", 'backup-backup'), 'STEP');
1017 }
1018
1019 if ((intval($this->return_bytes(ini_get('memory_limit'))) / 1024 / 1024) < 384) {
1020 @ini_set('memory_limit', '384M');
1021 }
1022
1023 $memory_limit = (intval($this->return_bytes(ini_get('memory_limit'))) / 1024 / 1024);
1024 if ($this->zip_progress != null && $isFirstExtract) {
1025 $this->zip_progress->log(str_replace('%s', $memory_limit, __("There is %s MBs of memory to use", 'backup-backup')), 'INFO');
1026 }
1027
1028 $safe_limit = intval($memory_limit / 4);
1029 if ($safe_limit > 64) $safe_limit = 64;
1030 if ($memory_limit === 384) $safe_limit = 78;
1031 if ($memory_limit >= 512) $safe_limit = 104;
1032 if ($memory_limit >= 1024) $safe_limit = 228;
1033 if ($memory_limit >= 2048) $safe_limit = 428;
1034
1035 // $real_memory = intval(memory_get_usage() * 0.9 / 1024 / 1024);
1036 // if ($real_memory < $safe_limit) $safe_limit = $real_memory;
1037 $safe_limit = intval($safe_limit * 0.8);
1038
1039 if ($this->zip_progress != null && $isFirstExtract) {
1040 $this->zip_progress->log(str_replace('%s', $safe_limit, __("Setting the safe limit to %s MB", 'backup-backup')), 'SUCCESS');
1041 }
1042 // Smart memory -- end //
1043
1044 return $safe_limit;
1045
1046 }
1047
1048 public function unzip_to($target_dir) {
1049
1050 // validations -- start //
1051 if ($this->lib === 0 && $this->extr_file === 0) {
1052 throw new \Exception("PHP-ZIP: unzip_file hasn't been called");
1053 }
1054 // it exists, but it's not a directory
1055 if (file_exists($target_dir) && (!is_dir($target_dir))) {
1056 throw new \Exception("PHP-ZIP: Target directory exists as a file not a directory");
1057 }
1058 // it doesn't exist
1059 if (!file_exists($target_dir)) {
1060 if (!mkdir($target_dir)) {
1061 throw new \Exception("PHP-ZIP: Directory not found, and unable to create it");
1062 }
1063 }
1064 // validations -- end //
1065
1066 // Target Directory
1067 $this->extr_dirc = $target_dir;
1068
1069 // Smart Memory
1070 $safe_limit = $this->smartMemory();
1071
1072 // Extract msg
1073 $this->zip_progress->log(__('Extracting files into temporary directory (this process can take some time)...', 'backup_migration'), 'STEP');
1074
1075 // Force PCL Zip
1076 $this->lib = 2;
1077
1078 // extract using ZipArchive
1079 // if($this->lib === 1) {
1080 // $lib = new \ZipArchive;
1081 // if(!$lib->open($this->extr_file)) throw new \Exception("PHP-ZIP: Unable to open the zip file");
1082 // if(!$lib->extractTo($this->extr_dirc)) throw new \Exception("PHP-ZIP: Unable to extract files");
1083 // $lib->close();
1084 // }
1085
1086 // extarct using PclZip
1087 if ($this->lib === 2) {
1088 $this->loadPclZip();
1089 $lib = new \PclZip($this->extr_file);
1090 $restor = $lib->extract(PCLZIP_OPT_PATH, $this->extr_dirc, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $safe_limit);
1091 if ($restor == 0) {
1092 $this->restore_failed($lib->errorInfo(true));
1093
1094 return false;
1095 }
1096 }
1097
1098 return true;
1099 }
1100
1101 private function dir_to_assoc_arr(DirectoryIterator $dir) {
1102 $data = [];
1103 foreach ($dir as $node) {
1104 if ($node->isDir() && !$node->isDot()) {
1105 $data[$node->getFilename()] = $this->dir_to_assoc_arr(new DirectoryIterator($node->getPathname()));
1106 } elseif ($node->isFile()) {
1107 $data[] = $node->getFilename();
1108 }
1109 }
1110
1111 return $data;
1112 }
1113
1114 private function path() {
1115 return join(DIRECTORY_SEPARATOR, func_get_args());
1116 }
1117
1118 private function commonPath($files, $remove = true) {
1119 foreach ($files as $index => $filesStr) {
1120 $files[$index] = explode(DIRECTORY_SEPARATOR, $filesStr);
1121 }
1122 $toDiff = $files;
1123 foreach ($toDiff as $arr_i => $arr) {
1124 foreach ($arr as $name_i => $name) {
1125 $toDiff[$arr_i][$name_i] = $name . "___" . $name_i;
1126 }
1127 }
1128 $diff = call_user_func_array("array_diff", $toDiff);
1129 reset($diff);
1130 $i = key($diff) - 1;
1131 if ($remove) {
1132 foreach ($files as $index => $arr) {
1133 $files[$index] = implode(DIRECTORY_SEPARATOR, array_slice($files[$index], $i));
1134 }
1135 } else {
1136 foreach ($files as $index => $arr) {
1137 $files[$index] = implode(DIRECTORY_SEPARATOR, array_slice($files[$index], 0, $i));
1138 }
1139 }
1140
1141 return $files;
1142 }
1143 }
1144