Passed
Push — 1.1.x ( 3d3019...6d4a67 )
by f
02:55 queued 01:04
created

PclzipZipInterface::delete()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 26
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 13
c 2
b 0
f 0
dl 0
loc 26
rs 9.8333
cc 4
nc 4
nop 0
1
<?php
2
namespace wapmorgan\UnifiedArchive;
3
4
use RecursiveIteratorIterator;
5
6
if (!defined('PCLZIP_ERR_NO_ERROR')) {
7
    // ----- Constants
8
    if (!defined('PCLZIP_READ_BLOCK_SIZE')) {
9
        define('PCLZIP_READ_BLOCK_SIZE', 2048);
10
    }
11
    if (!defined('PCLZIP_SEPARATOR')) {
12
        define('PCLZIP_SEPARATOR', ',');
13
    }
14
    if (!defined('PCLZIP_ERROR_EXTERNAL')) {
15
        define('PCLZIP_ERROR_EXTERNAL', 0);
16
    }
17
    if (!defined('PCLZIP_TEMPORARY_DIR')) {
18
        define('PCLZIP_TEMPORARY_DIR', sys_get_temp_dir());
19
    }
20
21
    define('PCLZIP_ERR_USER_ABORTED', 2);
22
    define('PCLZIP_ERR_NO_ERROR', 0);
23
    define('PCLZIP_ERR_WRITE_OPEN_FAIL', -1);
24
    define('PCLZIP_ERR_READ_OPEN_FAIL', -2);
25
    define('PCLZIP_ERR_INVALID_PARAMETER', -3);
26
    define('PCLZIP_ERR_MISSING_FILE', -4);
27
    define('PCLZIP_ERR_FILENAME_TOO_LONG', -5);
28
    define('PCLZIP_ERR_INVALID_ZIP', -6);
29
    define('PCLZIP_ERR_BAD_EXTRACTED_FILE', -7);
30
    define('PCLZIP_ERR_DIR_CREATE_FAIL', -8);
31
    define('PCLZIP_ERR_BAD_EXTENSION', -9);
32
    define('PCLZIP_ERR_BAD_FORMAT', -10);
33
    define('PCLZIP_ERR_DELETE_FILE_FAIL', -11);
34
    define('PCLZIP_ERR_RENAME_FILE_FAIL', -12);
35
    define('PCLZIP_ERR_BAD_CHECKSUM', -13);
36
    define('PCLZIP_ERR_INVALID_ARCHIVE_ZIP', -14);
37
    define('PCLZIP_ERR_MISSING_OPTION_VALUE', -15);
38
    define('PCLZIP_ERR_INVALID_OPTION_VALUE', -16);
39
    define('PCLZIP_ERR_ALREADY_A_DIRECTORY', -17);
40
    define('PCLZIP_ERR_UNSUPPORTED_COMPRESSION', -18);
41
    define('PCLZIP_ERR_UNSUPPORTED_ENCRYPTION', -19);
42
    define('PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE', -20);
43
    define('PCLZIP_ERR_DIRECTORY_RESTRICTION', -21);
44
45
    // ----- Options values
46
    define('PCLZIP_OPT_PATH', 77001);
47
    define('PCLZIP_OPT_ADD_PATH', 77002);
48
    define('PCLZIP_OPT_REMOVE_PATH', 77003);
49
    define('PCLZIP_OPT_REMOVE_ALL_PATH', 77004);
50
    define('PCLZIP_OPT_SET_CHMOD', 77005);
51
    define('PCLZIP_OPT_EXTRACT_AS_STRING', 77006);
52
    define('PCLZIP_OPT_NO_COMPRESSION', 77007);
53
    define('PCLZIP_OPT_BY_NAME', 77008);
54
    define('PCLZIP_OPT_BY_INDEX', 77009);
55
    define('PCLZIP_OPT_BY_EREG', 77010);
56
    define('PCLZIP_OPT_BY_PREG', 77011);
57
    define('PCLZIP_OPT_COMMENT', 77012);
58
    define('PCLZIP_OPT_ADD_COMMENT', 77013);
59
    define('PCLZIP_OPT_PREPEND_COMMENT', 77014);
60
    define('PCLZIP_OPT_EXTRACT_IN_OUTPUT', 77015);
61
    define('PCLZIP_OPT_REPLACE_NEWER', 77016);
62
    define('PCLZIP_OPT_STOP_ON_ERROR', 77017);
63
    // Having big trouble with crypt. Need to multiply 2 long int
64
    // which is not correctly supported by PHP ...
65
    //define( 'PCLZIP_OPT_CRYPT', 77018 );
66
    define('PCLZIP_OPT_EXTRACT_DIR_RESTRICTION', 77019);
67
    define('PCLZIP_OPT_TEMP_FILE_THRESHOLD', 77020);
68
    define('PCLZIP_OPT_ADD_TEMP_FILE_THRESHOLD', 77020); // alias
69
    define('PCLZIP_OPT_TEMP_FILE_ON', 77021);
70
    define('PCLZIP_OPT_ADD_TEMP_FILE_ON', 77021); // alias
71
    define('PCLZIP_OPT_TEMP_FILE_OFF', 77022);
72
    define('PCLZIP_OPT_ADD_TEMP_FILE_OFF', 77022); // alias
73
74
    // ----- File description attributes
75
    define('PCLZIP_ATT_FILE_NAME', 79001);
76
    define('PCLZIP_ATT_FILE_NEW_SHORT_NAME', 79002);
77
    define('PCLZIP_ATT_FILE_NEW_FULL_NAME', 79003);
78
    define('PCLZIP_ATT_FILE_MTIME', 79004);
79
    define('PCLZIP_ATT_FILE_CONTENT', 79005);
80
    define('PCLZIP_ATT_FILE_COMMENT', 79006);
81
82
    // ----- Call backs values
83
    define('PCLZIP_CB_PRE_EXTRACT', 78001);
84
    define('PCLZIP_CB_POST_EXTRACT', 78002);
85
    define('PCLZIP_CB_PRE_ADD', 78003);
86
    define('PCLZIP_CB_POST_ADD', 78004);
87
}
88
89
class PclzipZipInterface
90
{
91
    const SELECT_FILTER_PASS = 1;
92
    const SELECT_FILTER_REFUSE = 0;
93
94
    const AVERAGE_ZIP_COMPRESSION_RATIO = 2;
95
96
    /**
97
     * @var UnifiedArchive
98
     */
99
    private $archive;
100
101
    /**
102
     * PclzipZipInterface constructor.
103
     *
104
     * @param UnifiedArchive $archive
105
     */
106
    public function __construct(UnifiedArchive $archive)
107
    {
108
        $this->archive = $archive;
109
    }
110
111
    /**
112
     * @param $localname
113
     * @param $filename
114
     *
115
     * @return object
116
     */
117
    public function createFileHeader($localname, $filename)
118
    {
119
        return (object) array(
120
            'filename' => $filename,
121
            'stored_filename' => $localname,
122
            'size' => filesize($filename),
123
            'compressed_size' => ceil(filesize($filename)
124
                / self::AVERAGE_ZIP_COMPRESSION_RATIO),
125
            'mtime' => filemtime($filename),
126
            'comment' => null,
127
            'folder' => is_dir($filename),
128
            'status' => 'ok',
129
        );
130
    }
131
132
    /**
133
     * Creates a new archive
134
     * Two ways of usage:
135
     * <code>create($content, [$addDir, [$removeDir]])</code>
136
     * <code>create($content, [... options ...]])</code>
137
     */
138
    public function create($content)
139
    {
140
        if (is_array($content)) $paths_list = $content;
141
        else $paths_list = explode(',', $content);
142
143
        $options = $this->makeOptionsFromArguments(func_get_args());
144
145
        // filters initiation
146
        $filters = $this->createFilters($options);
147
        list($preAddCallback, $postAddCallback) = $this->extractCallbacks($options, PCLZIP_CB_PRE_ADD, PCLZIP_CB_POST_ADD);
148
149
        if (!empty($comment = $this->buildComment($options, null)))
150
            $this->archive->setComment($comment);
151
152
        // scan filesystem for files list
153
        return $this->addSnippets($paths_list, $filters, $preAddCallback, $postAddCallback);
154
    }
155
156
    /**
157
     * @param string $fileToAdd
158
     * @param array $filters
159
     * @param callable|null $preAddCallback
160
     * @param callable|null $postAddCallback
161
     * @return object
162
     */
163
    private function addSnippet($fileToAdd, array $filters, callable $preAddCallback, callable $postAddCallback)
164
    {
165
        if (is_file($fileToAdd) || is_dir($fileToAdd)) {
166
            // apply filters to a file
167
            $localname = $fileToAdd;
168
            $filename = $fileToAdd;
169
170
            foreach ($filters as $filter)
171
                call_user_func($filter, $localname, $filename);
172
173
            $file_header = $this->createFileHeader($localname, $filename);
174
            if (call_user_func($preAddCallback, $file_header) == 1) {
175
                //
176
                // Check for max length > 255
177
                //
178
                if (strlen(basename($file_header->stored_filename)) > 255)
179
                    $file_header->status = 'filename_too_long';
180
                if (is_file($filename)) {
181
                    $this->archive->addFiles([
182
                        $file_header->stored_filename => $file_header->filename,
183
                    ]);
184
                } else if (is_dir($filename)) {
185
//                    $this->archive->addEmptyDir($file_header->stored_filename);
186
                }
187
188
                call_user_func($postAddCallback, $file_header);
189
190
            } else {
191
                //
192
                // File was skipped
193
                //
194
                $file_header->status = 'skipped';
195
            }
196
197
            return $file_header;
198
        }
199
    }
200
201
    /**
202
     * Lists archive content
203
     * @throws Exceptions\NonExistentArchiveFileException
204
     */
205
    public function listContent()
206
    {
207
        $filesList = [];
208
209
        foreach ($this->archive->getFileNames() as $i => $fileName) {
210
            $fileData = $this->archive->getFileData($fileName);
211
212
            $filesList[] = (object) array(
213
                'filename' => $fileData->path,
214
                'stored_filename' => $fileData->path,
215
                'size' => $fileData->uncompressedSize,
216
                'compressed_size' => $fileData->compressedSize,
217
                'mtime' => $fileData->modificationTime,
218
                'comment' => $fileData->comment,
219
                'folder' => false/*in_array(substr($statIndex['name'], -1),
220
                    array('/', '\\'))*/,
221
                'index' => $i,
222
                'status' => 'ok',
223
            );
224
        }
225
226
        return $filesList;
227
    }
228
229
    /**
230
     * Extracts files
231
     * Two ways of usage:
232
     * <code>extract([$extractPath, [$removePath]])</code>
233
     * <code>extract([... options ...]])</code>
234
     */
235
    public function extract()
236
    {
237
        $options = func_get_args();
238
        array_shift($options);
239
240
        // parse options
241
        if (isset($options[0]) && is_string($options[0])) {
242
            $options[PCLZIP_OPT_PATH] = $options[0];
243
            if (isset($options[1]) && is_string($options[1])) {
244
                $options[PCLZIP_OPT_REMOVE_PATH] = $options[1];
245
            }
246
        } else {
247
            $options = $this->makeKeyValueArrayFromList($options);
248
        }
249
250
        // filters initiation
251
        if (isset($options[PCLZIP_OPT_PATH]))
252
            $extractPath = rtrim($options[PCLZIP_OPT_PATH], '/');
253
        else $extractPath = rtrim(getcwd(), '/');
254
255
        $filters = $this->createFilters($options);
256
        list($preExtractCallback, $postExtractCallback) = $this->extractCallbacks($options, PCLZIP_CB_PRE_EXTRACT, PCLZIP_CB_POST_EXTRACT);
257
        $selectFilter = $this->createSelector($options);
258
259
        if (isset($options[PCLZIP_OPT_EXTRACT_AS_STRING]))
260
            $anotherOutputFormat = PCLZIP_OPT_EXTRACT_AS_STRING;
261
        else if (isset($options[PCLZIP_OPT_EXTRACT_IN_OUTPUT]))
262
            $anotherOutputFormat = PCLZIP_OPT_EXTRACT_IN_OUTPUT;
263
        else $anotherOutputFormat = false;
264
265
        if (isset($options[PCLZIP_OPT_REPLACE_NEWER]))
266
            $doNotReplaceNewer = false;
267
        else $doNotReplaceNewer = true;
268
269
        if (isset($options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION]))
270
            $restrictExtractDir = $options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION];
271
        else $restrictExtractDir = false;
272
273
        $report = array();
274
        foreach ($this->listContent() as $file_header) {
275
            // add file information to report
276
            $report[] = $file_header;
277
            // refuse by select rule
278
            if (call_user_func($selectFilter, $file_header->stored_filename,
279
                    $file_header->filename, $file_header->index)
280
                === self::SELECT_FILTER_REFUSE) {
281
                //
282
                // I don't know need to remain this file in report or not,
283
                // but for now I remove
284
                array_pop($report);
285
                // $file_header->status = 'filtered';
286
                //
287
                continue;
288
            }
289
290
            //
291
            // add extract path in case of extraction
292
            // for some reason need to do it before call pre extract callback
293
            // (pclzip.lib.php v2.8.2, line 3670)
294
            // so I decided to do it here too
295
            //
296
            if ($anotherOutputFormat === false) {
297
                $file_header->filename = realpath($extractPath.'/'.
298
                    $file_header->filename);
299
                //
300
                // check for path correlation with restricted path
301
                //
302
                if ($restrictExtractDir !== false) {
303
                    $filename = $file_header->filename;
304
                    $restrictedDir = realpath($restrictExtractDir);
305
                    if (strncasecmp($restrictedDir, $filename,
306
                            strlen($restrictedDir)) !== 0) {
307
                        // refuse file extraction
308
                        $file_header->status = 'filtered';
309
                        continue;
310
                    }
311
                }
312
            }
313
314
            // apply pre extract callback
315
            $callback_result = call_user_func($preExtractCallback,
316
                $file_header);
317
            if ($callback_result == 1) {
318
                // go on ...
319
            } elseif ($callback_result == 0) {
320
                // skip current file
321
                $file_header->status = 'skipped';
322
                continue;
323
            } elseif ($callback_result == 2) {
324
                // skip & stop extraction
325
                $file_header->status = 'aborted';
326
                break;
327
            }
328
329
            // return content
330
            if ($anotherOutputFormat == PCLZIP_OPT_EXTRACT_AS_STRING) {
331
                $file_header->content
332
                    = $this->archive->getFileContent($file_header->stored_filename);
333
            }
334
            // echo content
335
            else if ($anotherOutputFormat == PCLZIP_OPT_EXTRACT_IN_OUTPUT) {
336
                echo $this->archive->getFileContent($file_header->stored_filename);
337
            }
338
            // extract content
339
            else if ($anotherOutputFormat === false) {
340
                // apply path filters
341
                foreach ($filters as $filter) call_user_func($filter,
342
                    $file_header->stored_filename, $file_header->filename);
343
                // dir extraction process
344
                if ($file_header->folder) {
345
                    // if dir doesn't exist
346
                    if (!is_dir($file_header->filename)) {
347
                        // try to create folder
348
                        if (!mkdir($file_header)) {
349
                            $file_header->status = 'path_creation_fail';
350
                            continue;
351
                        }
352
                    }
353
                }
354
                // file extraction process
355
                else {
356
                    // check if path is already taken by a folder
357
                    if (is_dir($file_header->filename)) {
358
                        $file_header->status = 'already_a_directory';
359
                        continue;
360
                    }
361
                    // check if file path is not writable
362
                    if (!is_writable($file_header->filename)) {
363
                        $file_header->status = 'write_protected';
364
                        continue;
365
                    }
366
                    // check if file exists and it's newer
367
                    if (is_file($file_header->filename)) {
368
                        if (filemtime($file_header->filename)
369
                            > $file_header->mtime) {
370
                            // skip extraction if option EXTRACT_NEWER isn't set
371
                            if ($doNotReplaceNewer) {
372
                                $file_header->status = 'newer_exist';
373
                                continue;
374
                            }
375
                        }
376
                    }
377
                    $directory = dirname($file_header->filename);
378
                    // check if running process can not create extraction folder
379
                    if (!is_dir($directory)) {
380
                        if (!mkdir($directory)) {
381
                            $file_header->status = 'path_creation_fail';
382
                            continue;
383
                        }
384
                    }
385
                    // extraction
386
                    if (copy('zip://'.$this->archive->filename."#"
0 ignored issues
show
Bug introduced by
The property filename does not seem to exist on wapmorgan\UnifiedArchive\UnifiedArchive.
Loading history...
387
                        .$file_header->stored_filename
388
                        , $file_header->filename)) {
389
                        // ok
390
                    }
391
                    // extraction fails
392
                    else {
393
                        $file_header->status = 'write_error';
394
                        continue;
395
                    }
396
                }
397
            }
398
399
            // apply post extract callback
400
            $callback_result = call_user_func($postExtractCallback,
401
                $file_header);
402
            if ($callback_result == 1) {
403
                // go on
404
            } elseif ($callback_result == 2) {
405
                // skip & stop extraction
406
                break;
407
            }
408
        }
409
410
        return $report;
411
    }
412
413
    /**
414
     * Reads properties of archive
415
     */
416
    public function properties()
417
    {
418
        return [
419
            'nb' => $this->archive->countFiles(),
420
            'comment' => $this->archive->getComment(),
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->archive->getComment() targeting wapmorgan\UnifiedArchive...edArchive::getComment() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
421
            'status' => 'OK',
422
        ];
423
    }
424
425
    /**
426
     * Adds files in archive
427
     * <code>add($content, [$addDir, [$removeDir]])</code>
428
     * <code>add($content, [ ... options ... ])</code>
429
     */
430
    public function add($content)
431
    {
432
        if (is_array($content)) $paths_list = $content;
433
        else $paths_list = explode(',', $content);
434
435
        $options = $this->makeOptionsFromArguments(func_get_args());
436
        $filters = $this->createFilters($options);
437
        list($preAddCallback, $postAddCallback) = $this->extractCallbacks($options, PCLZIP_CB_PRE_ADD, PCLZIP_CB_POST_ADD);
438
439
        if (!empty($comment = $this->buildComment($options, $this->archive->getComment())))
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->archive->getComment() targeting wapmorgan\UnifiedArchive...edArchive::getComment() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
440
            $this->archive->setComment($comment);
441
442
        // scan filesystem for files list
443
        return $this->addSnippets($paths_list, $filters, $preAddCallback, $postAddCallback);
444
    }
445
446
    /**
447
     * Removes files from archive
448
     * Usage:
449
     * <code>delete([... options ...])</code>
450
     */
451
    public function delete()
452
    {
453
        $options = $this->makeKeyValueArrayFromList(func_get_args());
454
        $selectFilter = $this->createSelector($options);
455
456
        $report = [];
457
        foreach ($this->listContent() as $file_header) {
458
            // select by select rule
459
            if (call_user_func($selectFilter, $file_header->stored_filename,
460
                    $file_header->filename, $file_header->index)
461
                === self::SELECT_FILTER_REFUSE) {
462
                // delete file from archive
463
                if ($this->archive->deleteFiles($file_header->stored_filename)) {
464
                    // ok
465
                    continue;
466
                }
467
                // deletion fails
468
                else {
469
                    return 0;
470
                }
471
            }
472
            // unselected file add in report
473
            $report[] = $file_header;
474
        }
475
476
        return $report;
477
    }
478
479
    /**
480
     * Merges given archive into current archive
481
     * Two ways of usage:
482
     * <code>merge($filename)</code>
483
     * <code>merge(UnifiedArchive $unifiedArchiveInstance)</code>
484
     * This implementation is more intelligent than original' one.
485
     */
486
    public function merge($a)
487
    {
488
        // filename
489
        if (is_string($a)) {
490
            if ($a = UnifiedArchive::open($a) !== null) {
491
                // ok
492
            } else {
493
                // // unsupported type of archive
494
                return 0;
495
            }
496
        }
497
        // UnifiedArchive instance
498
        else if ($a instanceof UnifiedArchive) {
499
            // go on
500
        }
501
        // invalid argument
502
        else {
503
            return 0;
504
        }
505
506
        $tempDir = tempnam(PCLZIP_TEMPORARY_DIR, 'merging');
507
        if (file_exists($tempDir)) unlink($tempDir);
508
        if (!mkdir($tempDir)) return 0;
509
510
        // go through archive content list and copy all files
511
        foreach ($a->getFileNames() as $filename) {
512
            // dir merging process
513
            if (in_array(substr($filename, -1), array('/', '\\'))) {
514
                $this->archive->addEmptyDir(rtrim($filename, '/\\'));
0 ignored issues
show
Bug introduced by
The method addEmptyDir() does not exist on wapmorgan\UnifiedArchive\UnifiedArchive. ( Ignorable by Annotation )

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

514
                $this->archive->/** @scrutinizer ignore-call */ 
515
                                addEmptyDir(rtrim($filename, '/\\'));

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...
515
            }
516
            // file merging process
517
            else {
518
                // extract file in temporary dir
519
                if ($a->extractNode($tempDir, '/'.$filename)) {
520
                    // go on
521
                } else {
522
                    // extraction fails
523
                    return 0;
524
                }
525
                // add file in archive
526
                if ($this->archive->addFile($tempDir.'/'.$filename,
527
                    $filename)) {
528
                    // ok
529
                } else {
530
                    return 0;
531
                }
532
            }
533
        }
534
535
        call_user_func(function ($directory) {
536
            foreach (glob($directory.'/*') as $f) {
537
                if (is_dir($f)) call_user_func(__FUNCTION__, $f);
538
                else unlink($f);
539
            }
540
        }, $tempDir);
541
542
        return 1;
543
    }
544
545
    /**
546
     * Duplicates archive
547
     * @param $clone_filename
548
     * @return int
549
     */
550
    public function duplicate($clone_filename)
551
    {
552
        return copy($this->archive->filename, $clone_filename) ? 1 : 0;
0 ignored issues
show
Bug introduced by
The property filename does not seem to exist on wapmorgan\UnifiedArchive\UnifiedArchive.
Loading history...
553
    }
554
555
    /**
556
     * @param array $options
557
     * @return array
558
     */
559
    public function createFilters(array $options)
560
    {
561
        $filters = array();
562
        if (isset($options[PCLZIP_OPT_REMOVE_PATH])
563
            && !isset($options[PCLZIP_OPT_REMOVE_ALL_PATH]))
564
            $filters[] = function (&$key, &$value) use ($options) {
0 ignored issues
show
Unused Code introduced by
The import $options is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
Unused Code introduced by
The parameter $value is not used and could be removed. ( Ignorable by Annotation )

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

564
            $filters[] = function (&$key, /** @scrutinizer ignore-unused */ &$value) use ($options) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
565
                $key = str_replace($key, null, $key);
566
            };
567
        if (isset($options[PCLZIP_OPT_REMOVE_ALL_PATH]))
568
            $filters[] = function (&$key, &$value) {
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed. ( Ignorable by Annotation )

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

568
            $filters[] = function (&$key, /** @scrutinizer ignore-unused */ &$value) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
569
                $key = basename($key);
570
            };
571
        if (isset($options[PCLZIP_OPT_ADD_PATH]))
572
            $filters[] = function (&$key, &$value) use ($options) {
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed. ( Ignorable by Annotation )

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

572
            $filters[] = function (&$key, /** @scrutinizer ignore-unused */ &$value) use ($options) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
573
                $key = rtrim($options[PCLZIP_OPT_ADD_PATH], '/') . '/' .
574
                    ltrim($key, '/');
575
            };
576
        return $filters;
577
    }
578
579
    /**
580
     * @param array $options
581
     * @param string $preCallbackConst
582
     * @param string $postCallbackConst
583
     * @return callable[]|\Closure[]
584
     */
585
    private function extractCallbacks(array $options, $preCallbackConst, $postCallbackConst)
586
    {
587
        $preCallback = $postCallback = function () { return true; };
588
589
        if (isset($options[$preCallbackConst]) && is_callable($options[$preCallbackConst]))
590
            $preCallback = $options[$preCallbackConst];
591
592
        if (isset($options[$postCallbackConst]) && is_callable($options[$postCallbackConst]))
593
            $postCallback = $options[$postCallbackConst];
594
595
        return [$preCallback, $postCallback];
596
    }
597
598
    /**
599
     * @param array $options
600
     * @return array
601
     */
602
    private function makeKeyValueArrayFromList(array $options)
603
    {
604
        return array_combine(
605
            array_filter($options, function ($v) {return (bool) $v&2;}),
606
            array_filter($options, function ($v) {return (bool) ($v-1)&2;})
607
        );
608
    }
609
610
    /**
611
     * @param array $pathsList
612
     * @param array $filters
613
     * @param callable $preAddCallback
614
     * @param callable $postAddCallback
615
     * @return array
616
     */
617
    public function addSnippets(array $pathsList, array $filters, callable $preAddCallback, callable $postAddCallback)
618
    {
619
        $report = [];
620
621
        foreach ($pathsList as $file_to_add) {
622
            $report[] = $this->addSnippet($file_to_add, $filters,
623
                $preAddCallback, $postAddCallback);
624
625
            // additional dir contents
626
            if (is_dir($file_to_add)) {
627
                $directory_contents = new \RecursiveIteratorIterator(
628
                    new \RecursiveDirectoryIterator(
629
                        $file_to_add, \RecursiveDirectoryIterator::SKIP_DOTS),
630
                    RecursiveIteratorIterator::SELF_FIRST);
631
                foreach ($directory_contents as $file_to_add) {
0 ignored issues
show
Comprehensibility Bug introduced by
$file_to_add is overwriting a variable from outer foreach loop.
Loading history...
632
                    $report[] = $this->addSnippet($file_to_add, $filters,
633
                        $preAddCallback, $postAddCallback);
634
                }
635
            }
636
        }
637
        return $report;
638
    }
639
640
    /**
641
     * @param array $indexes
642
     * @return \Closure
643
     */
644
    protected function createByIndexSelector(array $indexes)
645
    {
646
        $allowedIndexes = array();
647
        foreach ($indexes as $rule) {
648
            $parts = explode('-', $rule);
649
            if (count($parts) == 1) $allowedIndexes[] = $rule;
650
            else $allowedIndexes = array_merge(
651
                range($parts[0], $parts[1]), $allowedIndexes);
652
        }
653
654
        return function ($key, $value, $index) use ($allowedIndexes) {
655
            return in_array($index, $allowedIndexes)
656
                ? self::SELECT_FILTER_PASS
657
                : self::SELECT_FILTER_REFUSE;
658
        };
659
    }
660
661
    /**
662
     * @param string|array $names
663
     * @return \Closure
664
     */
665
    protected function createByNameSelector($names)
666
    {
667
        $allowedNames = is_array($names)
668
            ? $names
669
            : explode(',', $names);
670
671
        return function ($key, $value) use ($allowedNames) {
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed. ( Ignorable by Annotation )

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

671
        return function ($key, /** @scrutinizer ignore-unused */ $value) use ($allowedNames) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
672
            foreach ($allowedNames as $name) {
673
                // select directory with nested files
674
                if (in_array(substr($name, -1), ['/', '\\'])) {
675
                    if (strncasecmp($name, $key, strlen($name)) === 0) {
676
                        // that's a file inside a dir or that dir
677
                        return self::SELECT_FILTER_PASS;
678
                    }
679
                } else {
680
                    // select exact name only
681
                    if (strcasecmp($name, $key) === 0) {
682
                        // that's a file with this name
683
                        return self::SELECT_FILTER_PASS;
684
                    }
685
                }
686
            }
687
688
            // that file is not in allowed list
689
            return self::SELECT_FILTER_REFUSE;
690
        };
691
    }
692
693
    /**
694
     * @param string $regex
695
     * @return \Closure
696
     */
697
    protected function createByEregSelector($regex)
698
    {
699
        return function ($key, $value) use ($regex) {
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed. ( Ignorable by Annotation )

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

699
        return function ($key, /** @scrutinizer ignore-unused */ $value) use ($regex) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
700
            return (ereg($regex, $key) !== false)
701
                ? self::SELECT_FILTER_PASS
702
                : self::SELECT_FILTER_REFUSE;
703
        };
704
    }
705
706
    /**
707
     * @param $regex
708
     * @return \Closure
709
     */
710
    protected function createByPregSelector($regex)
711
    {
712
        return function ($key, $value) use ($regex) {
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed. ( Ignorable by Annotation )

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

712
        return function ($key, /** @scrutinizer ignore-unused */ $value) use ($regex) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
713
            return preg_match($regex, $key)
714
                ? self::SELECT_FILTER_PASS
715
                : self::SELECT_FILTER_REFUSE;
716
        };
717
    }
718
719
    /**
720
     * @param array $options
721
     * @return callable
722
     */
723
    protected function createSelector(array $options)
724
    {
725
        // exact matching
726
        if (isset($options[PCLZIP_OPT_BY_NAME]))
727
            $selectFilter = $this->createByNameSelector($options[PCLZIP_OPT_BY_NAME]);
728
        // <ereg> rule
729
        else if (isset($options[PCLZIP_OPT_BY_EREG]) && function_exists('ereg'))
730
            $selectFilter = $this->createByEregSelector($options[PCLZIP_OPT_BY_EREG]);
731
        // <preg_match> rule
732
        else if (isset($options[PCLZIP_OPT_BY_PREG]))
733
            $selectFilter = $this->createByPregSelector($options[PCLZIP_OPT_BY_PREG]);
734
        // index rule
735
        else if (isset($options[PCLZIP_OPT_BY_INDEX]))
736
            $selectFilter = $this->createByIndexSelector($options[PCLZIP_OPT_BY_INDEX]);
737
        // no rule
738
        else
739
            $selectFilter = function () {
740
                return self::SELECT_FILTER_PASS;
741
            };
742
        return $selectFilter;
743
    }
744
745
    /**
746
     * @param array $args
747
     * @return array
748
     */
749
    protected function makeOptionsFromArguments(array $args)
750
    {
751
        array_shift($args);
752
753
        // parse options
754
        if (isset($args[0]) && is_string($args[0])) {
755
            $options = [
756
                PCLZIP_OPT_ADD_PATH => $args[0]
757
            ];
758
759
            if (isset($args[1]) && is_string($args[1])) {
760
                $options[PCLZIP_OPT_REMOVE_PATH] = $args[1];
761
            }
762
        } else {
763
            $options = $this->makeKeyValueArrayFromList($args);
764
        }
765
        return $options;
766
    }
767
768
    /**
769
     * @param array $options
770
     * @param string|null $currentComment
771
     * @return mixed|string|null
772
     */
773
    protected function buildComment(array $options, $currentComment)
774
    {
775
        $comment = null;
776
        if (isset($options[PCLZIP_OPT_COMMENT]))
777
            $comment = $options[PCLZIP_OPT_COMMENT];
778
        else if (isset($options[PCLZIP_OPT_ADD_COMMENT])) {;
779
            $comment = $currentComment . $options[PCLZIP_OPT_ADD_COMMENT];
780
        } else if (isset($options[PCLZIP_OPT_PREPEND_COMMENT])) {
781
            $comment = $options[PCLZIP_OPT_PREPEND_COMMENT] . $currentComment;
782
        }
783
        return $comment;
784
    }
785
}
786