GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( 5fe6bb...4a99fb )
by Liuta
05:51
created

Xcloner_File_System   F

Complexity

Total Complexity 190

Size/Duplication

Total Lines 1046
Duplicated Lines 0 %

Importance

Changes 21
Bugs 10 Features 0
Metric Value
eloc 453
c 21
b 10
f 0
dl 0
loc 1046
rs 2
wmc 190

55 Methods

Rating   Name   Duplication   Size   Complexity  
A is_multipart() 0 7 2
A get_tmp_filesystem_append() 0 3 1
A get_backup_attachments() 0 21 5
A get_storage_path_file_info() 0 3 1
A get_storage_usage() 0 12 3
A get_tmp_filesystem() 0 3 1
A get_included_files_handler() 0 10 2
A get_start_path_file_info() 0 5 1
A set_diff_timestamp_start() 0 8 2
A get_xcloner_container() 0 3 1
A get_latest_backup() 0 18 5
A get_backup_size() 0 11 3
A is_part() 0 7 2
A getMetadataFull() 0 6 1
A get_latest_backups() 0 17 4
A get_tmp_filesystem_adapter() 0 3 1
A get_start_filesystem() 0 3 1
A get_hash() 0 3 1
A set_hash() 0 3 1
A delete_backup_by_name() 0 19 5
A get_temp_dir_handler() 0 3 1
C get_backup_archives_list() 0 47 14
A get_diff_timestamp_start() 0 3 1
A get_logger() 0 3 1
A get_multipart_files() 0 15 4
A check_file_diff_time() 0 15 4
A estimate_reading_time() 0 13 2
A get_scanned_files_total_size() 0 3 1
C is_excluded() 0 40 13
A do_system_init() 0 18 5
B process_backup_name() 0 22 7
A array_orderby() 0 23 5
A calc_to_bytes() 0 3 1
A remove_tmp_filesystem() 0 21 4
A scan_finished() 0 14 4
A cleanup_tmp_directories() 0 20 4
A last_logged_file() 0 3 1
A get_scanned_files_num() 0 3 1
A is_regex() 0 3 1
C backup_storage_cleanup() 0 55 12
A get_adapter() 0 8 3
A sort_by() 0 11 3
A get_excluded_files() 0 3 1
B store_file() 0 52 10
A get_storage_filesystem() 0 16 3
A list_directory() 0 3 1
B is_excluded_regex() 0 38 7
A __construct() 0 40 3
A estimate_read_write_time() 0 26 2
B start_file_recursion() 0 55 9
B build_files_list() 0 44 8
A get_filesystem() 0 12 5
A set_excluded_files() 0 16 4
A get_start_adapter() 0 3 1
A get_fileystem_handler() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Xcloner_File_System often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Xcloner_File_System, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * XCloner - Backup and Restore backup plugin for Wordpress
4
 *
5
 * class-xcloner-file-system.php
6
 * @author Liuta Ovidiu <[email protected]>
7
 *
8
 *        This program is free software; you can redistribute it and/or modify
9
 *        it under the terms of the GNU General Public License as published by
10
 *        the Free Software Foundation; either version 2 of the License, or
11
 *        (at your option) any later version.
12
 *
13
 *        This program is distributed in the hope that it will be useful,
14
 *        but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 *        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 *        GNU General Public License for more details.
17
 *
18
 *        You should have received a copy of the GNU General Public License
19
 *        along with this program; if not, write to the Free Software
20
 *        Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21
 *        MA 02110-1301, USA.
22
 *
23
 * @link https://github.com/ovidiul/XCloner-Wordpress
24
 *
25
 * @modified 7/31/18 3:46 PM
26
 *
27
 */
28
29
use League\Flysystem\Config;
30
use League\Flysystem\Filesystem;
31
use League\Flysystem\Util;
32
use League\Flysystem\Adapter\Local;
33
34
/**
35
 * Class Xcloner_File_System
36
 */
37
class Xcloner_File_System
38
{
39
40
    private $excluded_files = "";
41
    private $additional_regex_patterns = array();
42
    private $excluded_files_by_default = array("administrator/backups", "wp-content/backups");
43
    private $included_files_handler = "backup_files.csv";
44
    private $temp_dir_handler = ".dir";
45
    public $filesystem;
46
    public $tmp_filesystem;
47
    public $storage_filesystem;
48
    private $xcloner_container;
49
    private $diff_timestamp_start = "";
50
51
    private $logger;
52
    private $start_adapter;
53
    private $tmp_adapter;
54
    private $storage_adapter;
55
    private $xcloner_settings;
56
    private $start_filesystem;
57
    private $tmp_filesystem_append;
58
    private $storage_filesystem_append;
59
60
    private $files_counter;
61
    private $files_size;
62
    private $last_logged_file;
63
    private $folders_to_process_per_session = 25;
64
    private $backup_archive_extensions = array("tar", "tgz", "tar.gz", "gz", "csv", "encrypted", "decrypted");
65
    private $backup_name_tags = array('[time]', '[hostname]', '[domain]');
66
67
    /**
68
     * Xcloner_File_System constructor.
69
     * @param Xcloner $xcloner_container
70
     * @param string $hash
71
     */
72
    public function __construct(Xcloner $xcloner_container, $hash = "")
73
    {
74
        $this->xcloner_container = $xcloner_container;
75
76
        $this->logger = $xcloner_container->get_xcloner_logger()->withName("xcloner_file_system");
77
        $this->xcloner_settings = $xcloner_container->get_xcloner_settings();
78
79
        try {
80
81
            $this->start_adapter = new Local($this->xcloner_settings->get_xcloner_start_path(), LOCK_EX, 'SKIP_LINKS');
0 ignored issues
show
Bug introduced by
'SKIP_LINKS' of type string is incompatible with the type integer expected by parameter $linkHandling of League\Flysystem\Adapter\Local::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

81
            $this->start_adapter = new Local($this->xcloner_settings->get_xcloner_start_path(), LOCK_EX, /** @scrutinizer ignore-type */ 'SKIP_LINKS');
Loading history...
Bug introduced by
The method get_xcloner_start_path() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

81
            $this->start_adapter = new Local($this->xcloner_settings->/** @scrutinizer ignore-call */ get_xcloner_start_path(), LOCK_EX, 'SKIP_LINKS');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
82
            $this->start_filesystem = new Filesystem($this->start_adapter, new Config([
83
                'disable_asserts' => true,
84
            ]));
85
86
            $this->tmp_adapter = new Local($this->xcloner_settings->get_xcloner_tmp_path(), LOCK_EX, 'SKIP_LINKS');
87
            $this->tmp_filesystem = new Filesystem($this->tmp_adapter, new Config([
88
                'disable_asserts' => true,
89
            ]));
90
            $adapter = new Local($this->xcloner_settings->get_xcloner_tmp_path(), LOCK_EX | FILE_APPEND, 'SKIP_LINKS');
91
            $this->tmp_filesystem_append = new Filesystem($adapter, new Config([
92
                'disable_asserts' => true,
93
            ]));
94
95
            $adapter = new Local($this->xcloner_settings->get_xcloner_store_path(), LOCK_EX, 'SKIP_LINKS');
96
            $this->storage_filesystem = new Filesystem($adapter, new Config([
97
                'disable_asserts' => true,
98
            ]));
99
100
            $this->storage_adapter = new Local($this->xcloner_settings->get_xcloner_store_path(), FILE_APPEND,
101
                'SKIP_LINKS');
102
            $this->storage_filesystem_append = new Filesystem($this->storage_adapter, new Config([
103
                'disable_asserts' => true,
104
            ]));
105
        }catch (Exception $e) {
106
            $this->logger->error("Filesystem Initialization Error: ".$e->getMessage());
107
        }
108
109
110
        if ($value = get_option('xcloner_directories_to_scan_per_request')) {
111
            $this->folders_to_process_per_session = $value;
112
        }
113
114
    }
115
116
    /**
117
     * Set differential timestamp date
118
     * @param string $timestamp
119
     */
120
    public function set_diff_timestamp_start($timestamp = "")
121
    {
122
        if ($timestamp) {
123
            $this->logger->info(sprintf("Setting Differential Timestamp To %s", date("Y-m-d", $timestamp)), array(
0 ignored issues
show
Bug introduced by
$timestamp of type string is incompatible with the type integer expected by parameter $timestamp of date(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

123
            $this->logger->info(sprintf("Setting Differential Timestamp To %s", date("Y-m-d", /** @scrutinizer ignore-type */ $timestamp)), array(
Loading history...
124
                "FILESYSTEM",
125
                "DIFF"
126
            ));
127
            $this->diff_timestamp_start = $timestamp;
128
        }
129
    }
130
131
    /**
132
     * Gets the differential timestamp date
133
     * @return string
134
     */
135
    public function get_diff_timestamp_start()
136
    {
137
        return $this->diff_timestamp_start;
138
    }
139
140
    private function get_xcloner_container()
141
    {
142
        return $this->xcloner_container;
143
    }
144
145
    public function set_hash($hash)
146
    {
147
        $this->xcloner_settings->set_hash($hash);
148
    }
149
150
    public function get_hash($hash)
151
    {
152
        $this->xcloner_settings->get_hash();
153
    }
154
155
    public function get_tmp_filesystem()
156
    {
157
        return $this->tmp_filesystem;
158
    }
159
160
    public function get_storage_filesystem($remote_storage_selection = "")
161
    {
162
        if ($remote_storage_selection != "") {
163
            $remote_storage = $this->get_xcloner_container()->get_xcloner_remote_storage();
164
            $method = "get_".$remote_storage_selection."_filesystem";
165
166
            if (!method_exists($remote_storage, $method)) {
167
                return false;
168
            }
169
170
            list($adapter, $filesystem) = $remote_storage->$method();
171
172
            return $filesystem;
173
        }
174
175
        return $this->storage_filesystem;
176
    }
177
178
    public function get_tmp_filesystem_adapter()
179
    {
180
        return $this->tmp_adapter;
181
    }
182
183
    public function get_tmp_filesystem_append()
184
    {
185
        return $this->tmp_filesystem_append;
186
    }
187
188
    public function get_start_adapter()
189
    {
190
        return $this->start_adapter;
191
    }
192
193
    public function get_start_filesystem()
194
    {
195
        return $this->start_filesystem;
196
    }
197
198
    public function get_logger()
199
    {
200
        return $this->logger;
201
    }
202
203
    public function get_start_path_file_info($file)
204
    {
205
        $info = $this->getMetadataFull('start_adapter', $file);
206
207
        return $this->start_filesystem->normalizeFileInfo($info);
208
    }
209
210
    /**
211
     * @param string $file
212
     */
213
    public function get_storage_path_file_info($file)
214
    {
215
        return $this->getMetadataFull('storage_adapter', $file);
216
    }
217
218
    public function get_included_files_handler($metadata = 0)
219
    {
220
        $path = $this->included_files_handler;
221
        if (!$metadata) {
222
            return $path;
223
        }
224
225
        $spl_info = $this->getMetadataFull('tmp_adapter', $path);
226
227
        return $spl_info;
228
229
    }
230
231
    public function get_temp_dir_handler()
232
    {
233
        return $this->temp_dir_handler;
234
    }
235
236
    public function get_latest_backup()
237
    {
238
        $files = $this->get_backup_archives_list();
239
240
        if (is_array($files)) {
0 ignored issues
show
introduced by
The condition is_array($files) is always true.
Loading history...
241
            $this->sort_by($files, "timestamp", "desc");
242
        }
243
244
        $new_list = array();
245
246
        foreach ($files as $key => $file) {
247
            if (!isset($file['parent'])) {
248
                $new_list[] = ($files[$key]);
249
            }
250
        }
251
252
        if (isset($new_list[0])) {
253
            return $new_list[0];
254
        }
255
    }
256
257
    public function get_latest_backups()
258
    {
259
        $files = $this->get_backup_archives_list();
260
261
        if (is_array($files)) {
0 ignored issues
show
introduced by
The condition is_array($files) is always true.
Loading history...
262
            $this->sort_by($files, "timestamp", "desc");
263
        }
264
265
        $new_list = array();
266
267
        foreach ($files as $key => $file) {
268
            if (!isset($file['parent'])) {
269
                $new_list[] = ($files[$key]);
270
            }
271
        }
272
273
        return $new_list;
274
    }
275
276
    public function get_storage_usage()
277
    {
278
        $files = $this->get_backup_archives_list();
279
        $total = 0;
280
281
        if (is_array($files)) {
0 ignored issues
show
introduced by
The condition is_array($files) is always true.
Loading history...
282
            foreach ($files as $file) {
283
                $total += $file['size'];
284
            }
285
        }
286
287
        return $total;
288
    }
289
290
    public function is_part($backup_name)
291
    {
292
        if (stristr($backup_name, "-part")) {
293
            return true;
294
        }
295
296
        return false;
297
    }
298
299
    public function is_multipart($backup_name)
300
    {
301
        if (stristr($backup_name, "-multipart")) {
302
            return true;
303
        }
304
305
        return false;
306
    }
307
308
    public function get_backup_size($backup_name)
309
    {
310
        $backup_size = $this->get_storage_filesystem()->getSize($backup_name);
311
        if ($this->is_multipart($backup_name)) {
312
            $backup_parts = $this->get_multipart_files($backup_name);
313
            foreach ($backup_parts as $part_file) {
314
                $backup_size += $this->get_storage_filesystem()->getSize($part_file);
315
            }
316
        }
317
318
        return $backup_size;
319
    }
320
321
    public function get_multipart_files($backup_name, $storage_selection = "")
322
    {
323
        $files = array();
324
325
        if ($this->is_multipart($backup_name)) {
326
            $lines = explode(PHP_EOL, $this->get_storage_filesystem($storage_selection)->read($backup_name));
327
            foreach ($lines as $line) {
328
                if ($line) {
329
                    $data = str_getcsv($line);
330
                    $files[] = $data[0];
331
                }
332
            }
333
        }
334
335
        return $files;
336
    }
337
338
    public function delete_backup_by_name($backup_name, $storage_selection = "")
339
    {
340
        if ($this->is_multipart($backup_name)) {
341
            $lines = explode(PHP_EOL, $this->get_storage_filesystem($storage_selection)->read($backup_name));
342
            foreach ($lines as $line) {
343
                if ($line) {
344
                    $data = str_getcsv($line);
345
                    $this->get_storage_filesystem($storage_selection)->delete($data[0]);
346
                }
347
            }
348
        }
349
350
        if ($this->get_storage_filesystem($storage_selection)->delete($backup_name)) {
351
            $return = true;
352
        } else {
353
            $return = false;
354
        }
355
356
        return $return;
357
    }
358
359
    public function getMetadataFull($adapter = "storage_adapter", $path)
360
    {
361
        $location = $this->$adapter->applyPathPrefix($path);
362
        $spl_info = new SplFileInfo($location);
363
364
        return ($spl_info);
365
    }
366
367
368
    public function get_backup_archives_list($storage_selection = "")
369
    {
370
        $list = array();
371
372
373
        if (method_exists($this->get_storage_filesystem($storage_selection), "listContents")) {
374
            $list = $this->get_storage_filesystem($storage_selection)->listContents();
375
        }
376
377
        $backup_files = array();
378
        $parents = array();
379
380
        foreach ($list as $file_info) {
381
            if (isset($file_info['extension']) and $file_info['extension'] == "csv") {
382
                $data = array();
383
384
                $lines = explode(PHP_EOL, $this->get_storage_filesystem($storage_selection)->read($file_info['path']));
385
                foreach ($lines as $line) {
386
                    if ($line) {
387
                        $data = str_getcsv($line);
388
                        if (is_array($data)) {
389
                            $parents[$data[0]] = $file_info['basename'];
390
                            $file_info['childs'][] = $data;
391
                            $file_info['size'] += $data[2];
392
                        }
393
                    }
394
                }
395
396
            }
397
398
            if ($file_info['type'] == 'file' and isset($file_info['extension']) and in_array($file_info['extension'],
399
                    $this->backup_archive_extensions)) {
400
                $backup_files[$file_info['path']] = $file_info;
401
            }
402
        }
403
404
        foreach ($backup_files as $key => $file_info) {
405
            if (!isset($backup_files[$key]['timestamp'])) {
406
                //$backup_files[$key]['timestamp'] = $this->get_storage_filesystem($storage_selection)->getTimestamp($file_info['path']);
407
            }
408
409
            if (isset($parents[$file_info['basename']])) {
410
                $backup_files[$key]['parent'] = $parents[$file_info['basename']];
411
            }
412
        }
413
414
        return $backup_files;
415
    }
416
417
    public function start_file_recursion($init = 0)
418
    {
419
        if ($init) {
420
            $this->logger->info(sprintf(__("Starting the filesystem scanner on root folder %s"),
421
                $this->xcloner_settings->get_xcloner_start_path()));
422
            $this->do_system_init();
423
        }
424
425
        if ($this->tmp_filesystem->has($this->get_temp_dir_handler())) {
426
            //.dir exists, we presume we have files to iterate
427
            $content = $this->tmp_filesystem->read($this->get_temp_dir_handler());
428
            $files = array_filter(explode("\n", $content));
429
            $this->tmp_filesystem->delete($this->get_temp_dir_handler());
430
431
            $counter = 0;
432
            foreach ($files as $file) {
433
                if ($counter < $this->folders_to_process_per_session) {
434
                    $this->build_files_list($file);
435
                    $counter++;
436
                } else {
437
                    $this->tmp_filesystem_append->write($this->get_temp_dir_handler(), $file."\n");
438
                }
439
            }
440
        } else {
441
            $this->build_files_list();
442
        }
443
444
        if ($this->scan_finished()) {
445
            $metadata_dumpfile = $this->get_tmp_filesystem()->getMetadata("index.html");
446
            $this->store_file($metadata_dumpfile, 'tmp_filesystem');
447
            $this->files_counter++;
448
449
            //adding included dump file to the included files list
450
            if ($this->get_tmp_filesystem()->has($this->get_included_files_handler())) {
451
                $metadata_dumpfile = $this->get_tmp_filesystem()->getMetadata($this->get_included_files_handler());
452
                $this->store_file($metadata_dumpfile, 'tmp_filesystem');
453
                $this->files_counter++;
454
            }
455
456
            //adding a default index.html to the temp xcloner folder
457
            if (!$this->get_tmp_filesystem()->has("index.html")) {
458
                $this->get_tmp_filesystem()->write("index.html", "");
459
            }
460
461
            //adding the default log file
462
            if ($this->get_tmp_filesystem()->has($this->xcloner_settings->get_logger_filename(1))) {
463
                $metadata_dumpfile = $this->get_tmp_filesystem()->getMetadata($this->xcloner_settings->get_logger_filename(1));
464
                $this->store_file($metadata_dumpfile, 'tmp_filesystem');
465
                $this->files_counter++;
466
            }
467
468
            return false;
469
        }
470
471
        return true;
472
    }
473
474
    public function get_backup_attachments()
475
    {
476
        $return = array();
477
478
        $files_list_file = $this->xcloner_settings->get_xcloner_tmp_path().DS.$this->get_included_files_handler();
479
        if (file_exists($files_list_file)) {
480
            $return[] = $files_list_file;
481
        }
482
483
        if ($this->xcloner_settings->get_xcloner_option('xcloner_enable_log')) {
484
            $log_file = $this->xcloner_settings->get_xcloner_tmp_path().DS.$this->xcloner_settings->get_logger_filename(1);
485
            if (!file_exists($log_file)) {
486
                $log_file = $this->xcloner_settings->get_xcloner_store_path().DS.$this->xcloner_settings->get_logger_filename();
487
            }
488
489
            if (file_exists($log_file)) {
490
                $return[] = $log_file;
491
            }
492
        }
493
494
        return $return;
495
    }
496
497
    public function remove_tmp_filesystem()
498
    {
499
        //delete the temporary folder
500
        $this->logger->debug(sprintf("Deleting the temporary storage folder %s",
501
            $this->xcloner_settings->get_xcloner_tmp_path()));
502
503
        $contents = $this->get_tmp_filesystem()->listContents();
504
505
        if (is_array($contents)) {
506
            foreach ($contents as $file_info) {
507
                $this->get_tmp_filesystem()->delete($file_info['path']);
508
            }
509
        }
510
511
        try {
512
            rmdir($this->xcloner_settings->get_xcloner_tmp_path());
513
        }catch (Exception $e) {
514
            //silent continue
515
        }
516
517
        return;
518
    }
519
520
    public function cleanup_tmp_directories()
521
    {
522
        $adapter = new Local($this->xcloner_settings->get_xcloner_tmp_path(false), LOCK_EX | FILE_APPEND, 'SKIP_LINKS');
0 ignored issues
show
Bug introduced by
'SKIP_LINKS' of type string is incompatible with the type integer expected by parameter $linkHandling of League\Flysystem\Adapter\Local::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

522
        $adapter = new Local($this->xcloner_settings->get_xcloner_tmp_path(false), LOCK_EX | FILE_APPEND, /** @scrutinizer ignore-type */ 'SKIP_LINKS');
Loading history...
523
        $tmp_filesystem = new Filesystem($adapter, new Config([
524
            'disable_asserts' => true,
525
        ]));
526
527
        $contents = $tmp_filesystem->listContents();
528
529
        foreach ($contents as $file) {
530
531
            if (preg_match("/.xcloner-(.*)/", $file['path'])) {
532
                if ($file['timestamp'] < strtotime("-1days")) {
533
                    $tmp_filesystem->deleteDir($file['path']);
534
                    $this->logger->debug(sprintf("Delete temporary directory %s", $file['path']));
535
                }
536
            }
537
        }
538
539
        return true;
540
    }
541
542
    private function do_system_init()
543
    {
544
        $this->files_counter = 0;
545
546
        if (!$this->storage_filesystem->has("index.html")) {
547
            $this->storage_filesystem->write("index.html", "");
548
        }
549
550
        if (!$this->tmp_filesystem->has("index.html")) {
551
            $this->tmp_filesystem->write("index.html", "");
552
        }
553
554
        if ($this->tmp_filesystem->has($this->get_included_files_handler())) {
555
            $this->tmp_filesystem->delete($this->get_included_files_handler());
556
        }
557
558
        if ($this->tmp_filesystem->has($this->get_temp_dir_handler())) {
559
            $this->tmp_filesystem->delete($this->get_temp_dir_handler());
560
        }
561
    }
562
563
    public function get_scanned_files_num()
564
    {
565
        return $this->files_counter;
566
    }
567
568
    public function get_scanned_files_total_size()
569
    {
570
        return $this->files_size;
571
    }
572
573
    public function last_logged_file()
574
    {
575
        return $this->last_logged_file;
576
    }
577
578
    public static function is_regex($regex)
579
    {
580
        return preg_match("/^\^(.*)\$$/i", $regex);
581
    }
582
583
    public function set_excluded_files($excluded_files = array())
584
    {
585
        if (!is_array($excluded_files)) {
586
            $excluded_files = array();
587
        }
588
589
        foreach ($excluded_files as $excl) {
590
591
            if ($this->is_regex($excl)) {
592
                $this->additional_regex_patterns[] = $excl;
593
            }
594
        }
595
596
        $this->excluded_files = array_merge($excluded_files, $this->excluded_files_by_default);
597
598
        return $this->excluded_files;
599
    }
600
601
    public function get_excluded_files()
602
    {
603
        return $this->excluded_files_by_default;
604
    }
605
606
    public function list_directory($path)
607
    {
608
        return $this->start_filesystem->listContents($path);
609
    }
610
611
    public function build_files_list($folder = "")
612
    {
613
        $this->logger->debug(sprintf(("Building the files system list")));
614
615
        //if we start with the root folder(empty value), we initializa the file system
616
        if (!$folder) {
617
618
        }
619
620
        try {
621
622
            $files = $this->start_filesystem->listContents($folder);
623
            foreach ($files as $file) {
624
                if (!is_readable($this->xcloner_settings->get_xcloner_start_path().DS.$file['path'])) {
625
                    $this->logger->info(sprintf(__("Excluding %s from the filesystem list, file not readable"),
626
                        $file['path']), array(
627
                        "FILESYSTEM SCAN",
628
                        "NOT READABLE"
629
                    ));
630
                } elseif (!$matching_pattern = $this->is_excluded($file)) {
631
                    $this->logger->info(sprintf(__("Adding %s to the filesystem list"), $file['path']), array(
632
                        "FILESYSTEM SCAN",
633
                        "INCLUDE"
634
                    ));
635
                    $file['visibility'] = $this->start_filesystem->getVisibility($file['path']);
636
                    if ($this->store_file($file)) {
637
                        $this->files_counter++;
638
                    }
639
                    if (isset($file['size'])) {
640
                        $this->files_size += $file['size'];
641
                    }
642
643
                } else {
644
                    $this->logger->info(sprintf(__("Excluding %s from the filesystem list, matching pattern %s"),
645
                        $file['path'], $matching_pattern), array(
646
                        "FILESYSTEM SCAN",
647
                        "EXCLUDE"
648
                    ));
649
                }
650
            }
651
652
        }catch (Exception $e) {
653
654
            $this->logger->error($e->getMessage());
655
656
        }
657
658
    }
659
660
    public function estimate_read_write_time()
661
    {
662
        $tmp_file = ".xcloner".substr(md5(time()), 0, 5);
663
664
        $start_time = microtime(true);
665
666
        $data = str_repeat(rand(0, 9), 1024 * 1024); //write 1MB data
667
668
        try {
669
            $this->tmp_filesystem->write($tmp_file, $data);
670
671
            $end_time = microtime(true) - $start_time;
672
673
            $return['writing_time'] = $end_time;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$return was never initialized. Although not strictly required by PHP, it is generally a good practice to add $return = array(); before regardless.
Loading history...
674
675
            $return['reading_time'] = $this->estimate_reading_time($tmp_file);
676
677
            $this->tmp_filesystem->delete($tmp_file);
678
679
        }catch (Exception $e) {
680
681
            $this->logger->error($e->getMessage());
682
683
        }
684
685
        return $return;
686
    }
687
688
    public function backup_storage_cleanup()
689
    {
690
        $this->logger->info(sprintf(("Cleaning the backup storage on matching rules")));
691
692
        $_storage_size = 0;
693
        $_backup_files_list = array();
694
695
        //rule date limit
696
        $current_timestamp = strtotime("-".$this->xcloner_settings->get_xcloner_option('xcloner_cleanup_retention_limit_days')." days");
697
698
        $files = $this->storage_filesystem->listContents();
699
700
        if (is_array($files)) {
701
            foreach ($files as $file) {
702
                if (isset($file['extension']) and in_array($file['extension'], $this->backup_archive_extensions)) {
703
                    $_storage_size += $file['size']; //bytes
704
                    $_backup_files_list[] = $file;
705
                }
706
            }
707
        }
708
709
710
        $this->sort_by($_backup_files_list, "timestamp", "asc");
711
712
        $_backups_counter = sizeof($_backup_files_list);
713
714
        foreach ($_backup_files_list as $file) {
715
            //processing rule folder capacity
716
            if ($this->xcloner_settings->get_xcloner_option('xcloner_cleanup_capacity_limit') &&
717
                $_storage_size >= ($set_storage_limit = 1024 * 1024 * $this->xcloner_settings->get_xcloner_option('xcloner_cleanup_capacity_limit')))    //bytes
718
            {
719
                $this->storage_filesystem->delete($file['path']);
720
                $_storage_size -= $file['size'];
721
                $this->logger->info("Deleting backup ".$file['path']." matching rule", array(
722
                    "STORAGE SIZE LIMIT",
723
                    $_storage_size." >= ".$set_storage_limit
724
                ));
725
            }
726
727
            //processing rule days limit
728
            if ($this->xcloner_settings->get_xcloner_option('xcloner_cleanup_retention_limit_days') && $current_timestamp >= $file['timestamp']) {
729
                $this->storage_filesystem->delete($file['path']);
730
                $this->logger->info("Deleting backup ".$file['path']." matching rule", array(
731
                    "RETENTION LIMIT TIMESTAMP",
732
                    $file['timestamp']." =< ".$this->xcloner_settings->get_xcloner_option('xcloner_cleanup_retention_limit_days')
733
                ));
734
            }
735
736
            //processing backup countert limit
737
            if ($this->xcloner_settings->get_xcloner_option('xcloner_cleanup_retention_limit_archives') && $_backups_counter >= $this->xcloner_settings->get_xcloner_option('xcloner_cleanup_retention_limit_archives')) {
738
                $this->storage_filesystem->delete($file['path']);
739
                $_backups_counter--;
740
                $this->logger->info("Deleting backup ".$file['path']." matching rule", array(
741
                    "BACKUP QUANTITY LIMIT",
742
                    $_backups_counter." >= ".$this->xcloner_settings->get_xcloner_option('xcloner_cleanup_retention_limit_archives')
743
                ));
744
            }
745
746
747
        }
748
749
    }
750
751
    /**
752
     * @param string $tmp_file
753
     */
754
    public function estimate_reading_time($tmp_file)
755
    {
756
        $this->logger->debug(sprintf(("Estimating file system reading time")));
757
758
        $start_time = microtime(true);
759
760
        if ($this->tmp_filesystem->has($tmp_file)) {
761
            $this->tmp_filesystem->read($tmp_file);
762
        }
763
764
        $end_time = microtime(true) - $start_time;
765
766
        return $end_time;
767
768
    }
769
770
    public function process_backup_name($name = "", $max_length = 100)
771
    {
772
        if (!$name) {
773
            $name = $this->xcloner_settings->get_default_backup_name();
774
        }
775
776
        foreach ($this->backup_name_tags as $tag) {
777
            if ($tag == '[time]') {
778
                $name = str_replace($tag, date("Y-m-d_H-i"), $name);
779
            } elseif ($tag == '[hostname]') {
780
                $name = str_replace($tag, gethostname(), $name);
781
            } elseif ($tag == '[domain]') {
782
                $domain = parse_url(admin_url(), PHP_URL_HOST);
783
                $name = str_replace($tag, $domain, $name);
784
            }
785
        }
786
787
        if ($max_length) {
788
            $name = substr($name, 0, $max_length);
789
        }
790
791
        return $name;
792
    }
793
794
    /**
795
     * @param string $field
796
     */
797
    public function sort_by(&$array, $field, $direction = 'asc')
798
    {
799
        if (strtolower($direction) == "desc" || $direction == SORT_DESC) {
800
            $direction = SORT_DESC;
801
        } else {
802
            $direction = SORT_ASC;
803
        }
804
805
        $array = $this->array_orderby($array, $field, $direction);
806
807
        return true;
808
    }
809
810
    private function array_orderby()
811
    {
812
        $args = func_get_args();
813
        $data = array_shift($args);
814
815
        foreach ($args as $n => $field) {
816
            if (is_string($field)) {
817
                $tmp = array();
818
                foreach ($data as $key => $row) {
819
                    if (is_array($row)) {
820
                        $tmp[$key] = $row[$field];
821
                    } else {
822
                        $tmp[$key] = $row->$field;
823
                    }
824
                }
825
                $args[$n] = $tmp;
826
            }
827
        }
828
        $args[] = &$data;
829
830
        call_user_func_array('array_multisort', $args);
831
832
        return array_pop($args);
833
    }
834
835
    private function check_file_diff_time($file)
836
    {
837
        if ($this->get_diff_timestamp_start() != "") {
838
            $fileMeta = $this->getMetadataFull("start_adapter", $file['path']);
839
            $timestamp = $fileMeta->getMTime();
840
            if ($timestamp < $fileMeta->getCTime()) {
841
                $timestamp = $fileMeta->getCTime();
842
            }
843
844
            if ($timestamp <= $this->get_diff_timestamp_start()) {
845
                return " file DIFF timestamp ".$timestamp." < ".$this->diff_timestamp_start;
846
            }
847
        }
848
849
        return false;
850
    }
851
852
    public function is_excluded($file)
853
    {
854
        $this->logger->debug(sprintf(("Checking if %s is excluded"), $file['path']));
855
856
        if ($xcloner_exclude_files_larger_than_mb = $this->xcloner_settings->get_xcloner_option('xcloner_exclude_files_larger_than_mb')) {
857
            if (isset($file['size']) && $file['size'] > $this->calc_to_bytes($xcloner_exclude_files_larger_than_mb)) {
858
                return "> ".$xcloner_exclude_files_larger_than_mb."MB";
859
            }
860
        }
861
862
        if (!is_array($this->excluded_files) || !sizeof($this->excluded_files)) {
0 ignored issues
show
introduced by
The condition is_array($this->excluded_files) is always false.
Loading history...
863
            $this->set_excluded_files();
864
        }
865
866
        if (is_array($this->excluded_files)) {
0 ignored issues
show
introduced by
The condition is_array($this->excluded_files) is always true.
Loading history...
867
            foreach ($this->excluded_files as $excluded_file_pattern) {
868
                if ($excluded_file_pattern == "/") {
869
                    $needle = "$";
870
                } else {
871
                    $needle = "$".$excluded_file_pattern;
872
                }
873
874
                if (strstr("$".$file['path'], $needle)) {
875
                    return $excluded_file_pattern;
876
                }
877
            }
878
        }
879
880
        if ($regex = $this->is_excluded_regex($file)) {
881
            return $regex;
882
        }
883
884
        if ($file['type'] == "file") {
885
            $check_file_diff_timestamp = $this->check_file_diff_time($file);
886
            if ($check_file_diff_timestamp) {
887
                return $check_file_diff_timestamp;
888
            }
889
        }
890
891
        return false;
892
    }
893
894
    /*REGEX examples
895
     *
896
    * exclude all except .php file
897
    * PATTERN: ^(.*)\.(.+)$(?<!(php))
898
    *
899
    * exclude all except .php and .txt
900
    * PATTERN: ^(.*)\.(.+)$(?<!(php|txt))";
901
    *
902
    * exclude all .svn and .git
903
    * PATTERN: ^(.*)\.(svn|git)(.*)$";
904
    *
905
    * exclude root directory /test
906
    * PATTERN: "^\/test(.*)$";
907
    *
908
    * exclude the wp-admin folder
909
    * PATTERN: ^(\/wp-admin)(.*)$";
910
    *
911
    * exclude the wp-admin, wp-includes and wp-config.php
912
    * PATTERN: ^\/(wp-admin|wp-includes|wp-config.php)(.*)$";
913
    *
914
    * exclude all .avi files
915
    * PATTERN: ^(.*)$(?<=(avi))";
916
    *
917
    * exclude all .jpg and gif files
918
    * PATTERN: ^(.*)$(?<=(gif|jpg))";
919
    *
920
    * exclude all cache folders from wp-content/
921
    * PATTERN: ^\/wp-content(.*)\/cache($|\/)(.*)";
922
    *
923
    * exclude the backup folders
924
    * PATTERN: (^|^\/)(wp-content\/backups|administrator\/backups)(.*)$";
925
    */
926
	private function is_excluded_regex($file)
927
	{
928
		//$this->logger->debug(sprintf(("Checking if %s is excluded"), $file['path']));
929
930
		$regex_patterns = explode(PHP_EOL, $this->xcloner_settings->get_xcloner_option('xcloner_regex_exclude'));
931
932
		if (is_array($this->additional_regex_patterns)) {
0 ignored issues
show
introduced by
The condition is_array($this->additional_regex_patterns) is always true.
Loading history...
933
			$regex_patterns = array_merge($regex_patterns, $this->additional_regex_patterns);
934
		}
935
936
		//print_r($regex_patterns);exit;
937
938
		if (is_array($regex_patterns)) {
0 ignored issues
show
introduced by
The condition is_array($regex_patterns) is always true.
Loading history...
939
			//$this->excluded_files = array();
940
			//$this->excluded_files[] ="(.*)\.(git)(.*)$";
941
			//$this->excluded_files[] ="wp-content\/backups(.*)$";
942
943
			foreach ($regex_patterns as $excluded_file_pattern) {
944
945
				if (substr($excluded_file_pattern, strlen($excluded_file_pattern) - 1,
946
						strlen($excluded_file_pattern)) == "\r") {
947
					$excluded_file_pattern = substr($excluded_file_pattern, 0, strlen($excluded_file_pattern) - 1);
948
				}
949
950
				if ($file['path'] == "/") {
951
					$needle = "/";
952
				} else {
953
					$needle = "/".$file['path'];
954
				}
955
				//echo $needle."---".$excluded_file_pattern."---\n";
956
957
				if (@preg_match("/(^|^\/)".$excluded_file_pattern."/i", $needle)) {
958
					return $excluded_file_pattern;
959
				}
960
			}
961
		}
962
963
		return false;
964
	}
965
966
	public function store_file($file, $storage = 'start_filesystem')
967
	{
968
		$this->logger->debug(sprintf("Storing %s in the backup list", $file['path']));
969
970
		if (!isset($file['size'])) {
971
			$file['size'] = 0;
972
		}
973
		if (!isset($file['visibility'])) {
974
			$file['visibility'] = "private";
975
		}
976
977
		$csv_filename = str_replace('"', '""', $file['path']);
978
979
		$line = '"'.($csv_filename).'","'.$file['timestamp'].'","'.$file['size'].'","'.$file['visibility'].'","'.$storage.'"'.PHP_EOL;
980
981
		$this->last_logged_file = $file['path'];
982
983
		if ($file['type'] == "dir") {
984
			try {
985
				$this->tmp_filesystem_append->write($this->get_temp_dir_handler(), $file['path']."\n");
986
			}catch (Exception $e) {
987
				$this->logger->error($e->getMessage());
988
			}
989
		}
990
991
		if ($this->get_diff_timestamp_start()) {
992
			if ($file['type'] != "file" && $response = $this->check_file_diff_time($file)) {
993
				$this->logger->info(sprintf("Directory %s archiving skipped on differential backup %s", $file['path'],
994
					$response), array(
995
					"FILESYSTEM SCAN",
996
					"DIR DIFF"
997
				));
998
999
				return false;
1000
			}
1001
		}
1002
1003
		try {
1004
			if (!$this->tmp_filesystem_append->has($this->get_included_files_handler())) {
1005
				//adding fix for UTF-8 CSV preview
1006
				$start_line = "\xEF\xBB\xBF".'"Filename","Timestamp","Size","Visibility","Storage"'.PHP_EOL;
1007
				$this->tmp_filesystem_append->write($this->get_included_files_handler(), $start_line);
1008
			}
1009
1010
			$this->tmp_filesystem_append->write($this->get_included_files_handler(), $line);
1011
1012
		}catch (Exception $e) {
1013
1014
			$this->logger->error($e->getMessage());
1015
		}
1016
1017
		return true;
1018
	}
1019
1020
	public function get_fileystem_handler()
1021
	{
1022
		return $this;
1023
	}
1024
1025
	public function get_filesystem($system = "")
1026
	{
1027
		if ($system == "storage_filesystem_append") {
1028
			return $this->storage_filesystem_append;
1029
		} elseif ($system == "tmp_filesystem_append") {
1030
			return $this->tmp_filesystem_append;
1031
		} elseif ($system == "tmp_filesystem") {
1032
			return $this->tmp_filesystem;
1033
		} elseif ($system == "storage_filesystem") {
1034
			return $this->storage_filesystem;
1035
		} else {
1036
			return $this->start_filesystem;
1037
		}
1038
	}
1039
1040
	public function get_adapter($system)
1041
	{
1042
		if ($system == "tmp_filesystem") {
1043
			return $this->tmp_adapter;
1044
		} elseif ($system == "storage_filesystem") {
1045
			return $this->storage_adapter;
1046
		} else {
1047
			return $this->start_adapter;
1048
		}
1049
	}
1050
1051
	/**
1052
	 * File scan finished
1053
	 * Method called when file scan is finished
1054
	 *
1055
	 * @return bool
1056
	 */
1057
	private function scan_finished()
1058
	{
1059
		if ($this->tmp_filesystem_append->has($this->get_temp_dir_handler()) &&
1060
			$this->tmp_filesystem_append->getSize($this->get_temp_dir_handler())) {
1061
			return false;
1062
		}
1063
1064
		if ($this->tmp_filesystem->has($this->get_temp_dir_handler())) {
1065
			$this->tmp_filesystem->delete($this->get_temp_dir_handler());
1066
		}
1067
1068
		$this->logger->debug(sprintf(("File scan finished")));
1069
1070
		return true;
1071
	}
1072
1073
	/**
1074
	 * Calculate bytes from MB value
1075
	 *
1076
	 * @param int $mb_size
1077
	 *
1078
	 * @return float|int
1079
	 */
1080
	private function calc_to_bytes($mb_size)
1081
	{
1082
		return $mb_size * (1024 * 1024);
1083
	}
1084
1085
}
1086