Passed
Push — master ( cbf6d3...d62b89 )
by Michiel
08:56
created

DirectoryScanner::addDefaultExclude()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 10
ccs 5
cts 6
cp 0.8333
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2.0185
1
<?php
2
/**
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the LGPL. For more information please see
17
 * <http://phing.info>.
18
 */
19
20
namespace Phing\Io;
21
22
use Phing\Exception\BuildException;
23
use Phing\Exception\NullPointerException;
24
use Phing\Type\Selector\FileSelector;
25
use Phing\Type\Selector\SelectorScanner;
26
use Phing\Type\Selector\SelectorUtils;
27
use Phing\Util\StringHelper;
28
use UnexpectedValueException;
29
30
/**
31
 * Class for scanning a directory for files/directories that match a certain
32
 * criteria.
33
 *
34
 * These criteria consist of a set of include and exclude patterns. With these
35
 * patterns, you can select which files you want to have included, and which
36
 * files you want to have excluded.
37
 *
38
 * The idea is simple. A given directory is recursively scanned for all files
39
 * and directories. Each file/directory is matched against a set of include
40
 * and exclude patterns. Only files/directories that match at least one
41
 * pattern of the include pattern list, and don't match a pattern of the
42
 * exclude pattern list will be placed in the list of files/directories found.
43
 *
44
 * When no list of include patterns is supplied, "**" will be used, which
45
 * means that everything will be matched. When no list of exclude patterns is
46
 * supplied, an empty list is used, such that nothing will be excluded.
47
 *
48
 * The pattern matching is done as follows:
49
 * The name to be matched is split up in path segments. A path segment is the
50
 * name of a directory or file, which is bounded by DIRECTORY_SEPARATOR
51
 * ('/' under UNIX, '\' under Windows).
52
 * E.g. "abc/def/ghi/xyz.php" is split up in the segments "abc", "def", "ghi"
53
 * and "xyz.php".
54
 * The same is done for the pattern against which should be matched.
55
 *
56
 * Then the segments of the name and the pattern will be matched against each
57
 * other. When '**' is used for a path segment in the pattern, then it matches
58
 * zero or more path segments of the name.
59
 *
60
 * There are special case regarding the use of DIRECTORY_SEPARATOR at
61
 * the beginning of the pattern and the string to match:
62
 * When a pattern starts with a DIRECTORY_SEPARATOR, the string
63
 * to match must also start with a DIRECTORY_SEPARATOR.
64
 * When a pattern does not start with a DIRECTORY_SEPARATOR, the
65
 * string to match may not start with a DIRECTORY_SEPARATOR.
66
 * When one of these rules is not obeyed, the string will not
67
 * match.
68
 *
69
 * When a name path segment is matched against a pattern path segment, the
70
 * following special characters can be used:
71
 *   '*' matches zero or more characters,
72
 *   '?' matches one character.
73
 *
74
 * Examples:
75
 *
76
 * "**\*.php" matches all .php files/dirs in a directory tree.
77
 *
78
 * "test\a??.php" matches all files/dirs which start with an 'a', then two
79
 * more characters and then ".php", in a directory called test.
80
 *
81
 * "**" matches everything in a directory tree.
82
 *
83
 * "**\test\**\XYZ*" matches all files/dirs that start with "XYZ" and where
84
 * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123").
85
 *
86
 * Case sensitivity may be turned off if necessary.  By default, it is
87
 * turned on.
88
 *
89
 * Example of usage:
90
 *   $ds = new DirectroyScanner();
91
 *   $includes = array("**\*.php");
92
 *   $excludes = array("modules\*\**");
93
 *   $ds->SetIncludes($includes);
94
 *   $ds->SetExcludes($excludes);
95
 *   $ds->SetBasedir("test");
96
 *   $ds->SetCaseSensitive(true);
97
 *   $ds->Scan();
98
 *
99
 *   print("FILES:");
100
 *   $files = ds->GetIncludedFiles();
101
 *   for ($i = 0; $i < count($files);$i++) {
102
 *     println("$files[$i]\n");
103
 *   }
104
 *
105
 * This will scan a directory called test for .php files, but excludes all
106
 * .php files in all directories under a directory called "modules"
107
 *
108
 * This class is complete preg/ereg free port of the Java class
109
 * org.apache.tools.ant.DirectoryScanner. Even functions that use preg/ereg
110
 * internally (like split()) are not used. Only the _fast_ string functions
111
 * and comparison operators (=== !=== etc) are used for matching and tokenizing.
112
 *
113
 * @author Arnout J. Kuiper, [email protected]
114
 * @author Magesh Umasankar, [email protected]
115
 * @author Andreas Aderhold, [email protected]
116
 *
117
 */
118
class DirectoryScanner implements FileScanner, SelectorScanner
119
{
120
    /**
121
     * default set of excludes
122
     */
123
    protected static $DEFAULTEXCLUDES = [
124
        "**/*~",
125
        "**/#*#",
126
        "**/.#*",
127
        "**/%*%",
128
        "**/CVS",
129
        "**/CVS/**",
130
        "**/.cvsignore",
131
        "**/SCCS",
132
        "**/SCCS/**",
133
        "**/vssver.scc",
134
        "**/.svn",
135
        "**/.svn/**",
136
        "**/._*",
137
        "**/.DS_Store",
138
        "**/.darcs",
139
        "**/.darcs/**",
140
        "**/.git",
141
        "**/.git/**",
142
        "**/.gitattributes",
143
        "**/.gitignore",
144
        "**/.gitmodules",
145
        "**/.hg",
146
        "**/.hg/**",
147
        "**/.hgignore",
148
        "**/.hgsub",
149
        "**/.hgsubstate",
150
        "**/.hgtags",
151
        "**/.bzr",
152
        "**/.bzr/**",
153
        "**/.bzrignore",
154
    ];
155
156
    private static $defaultExcludeList = [];
157
158
    /**
159
     * The base directory which should be scanned.
160
     *
161
     * @var string
162
     */
163
    protected $basedir;
164
165
    /**
166
     * The patterns for the files that should be included.
167
     *
168
     * @var string[]
169
     */
170
    protected $includes = null;
171
172
    /**
173
     * The patterns for the files that should be excluded.
174
     *
175
     * @var string[]
176
     */
177
    protected $excludes = null;
178
179
    /**
180
     * Whether to expand/dereference symbolic links, default is false
181
     *
182
     * @var bool
183
     */
184
    protected $expandSymbolicLinks = false;
185
186
    /**
187
     * The files that where found and matched at least one includes, and matched
188
     * no excludes.
189
     */
190
    protected $filesIncluded;
191
192
    /**
193
     * The files that where found and did not match any includes. Trie
194
     */
195
    protected $filesNotIncluded;
196
197
    /**
198
     * The files that where found and matched at least one includes, and also
199
     * matched at least one excludes. Trie object.
200
     */
201
    protected $filesExcluded;
202
203
    /**
204
     * The directories that where found and matched at least one includes, and
205
     * matched no excludes.
206
     */
207
    protected $dirsIncluded;
208
209
    /**
210
     * The directories that where found and did not match any includes.
211
     */
212
    protected $dirsNotIncluded;
213
214
    /**
215
     * The files that where found and matched at least one includes, and also
216
     * matched at least one excludes.
217
     */
218
    protected $dirsExcluded;
219
220
    /**
221
     * Have the vars holding our results been built by a slow scan?
222
     */
223
    protected $haveSlowResults = false;
224
225
    /**
226
     * Should the file system be treated as a case sensitive one?
227
     */
228
    protected $isCaseSensitive = true;
229
    /**
230
     * Whether a missing base directory is an error.
231
     */
232
    protected $errorOnMissingDir = false;
233
234
    /**
235
     * @var FileSelector[] Selectors
236
     */
237
    protected $selectorsList = null;
238
239
    protected $filesDeselected;
240
    protected $dirsDeselected;
241
242
    /**
243
     * if there are no deselected files
244
     */
245
    protected $everythingIncluded = true;
246
247 145
    public function __construct()
248
    {
249 145
        if (empty(self::$defaultExcludeList)) {
250
            self::$defaultExcludeList = self::$DEFAULTEXCLUDES;
251
        }
252 145
    }
253
254
    /**
255
     * Does the path match the start of this pattern up to the first "**".
256
     * This is a static mehtod and should always be called static
257
     *
258
     * This is not a general purpose test and should only be used if you
259
     * can live with false positives.
260
     *
261
     * pattern=**\a and str=b will yield true.
262
     *
263
     * @param string $pattern the pattern to match against
264
     * @param string $str the string (path) to match
265
     * @param bool $isCaseSensitive must matches be case sensitive?
266
     * @return boolean true if matches, otherwise false
267
     */
268 38
    public function matchPatternStart($pattern, $str, $isCaseSensitive = true)
269
    {
270 38
        return SelectorUtils::matchPatternStart($pattern, $str, $isCaseSensitive);
271
    }
272
273
    /**
274
     * Matches a path against a pattern.
275
     *
276
     * @param string $pattern the (non-null) pattern to match against
277
     * @param string $str the (non-null) string (path) to match
278
     * @param bool $isCaseSensitive must a case sensitive match be done?
279
     *
280
     * @return bool true when the pattern matches against the string.
281
     *              false otherwise.
282
     */
283 144
    public function matchPath($pattern, $str, $isCaseSensitive = true)
284
    {
285 144
        return SelectorUtils::matchPath($pattern, $str, $isCaseSensitive);
286
    }
287
288
    /**
289
     * Matches a string against a pattern. The pattern contains two special
290
     * characters:
291
     * '*' which means zero or more characters,
292
     * '?' which means one and only one character.
293
     *
294
     * @param string $pattern pattern to match against
295
     * @param string $str string that must be matched against the
296
     *                                pattern
297
     * @param bool $isCaseSensitive
298
     *
299
     * @return boolean true when the string matches against the pattern,
300
     *                 false otherwise.
301
     */
302
    public function match($pattern, $str, $isCaseSensitive = true)
303
    {
304
        return SelectorUtils::match($pattern, $str, $isCaseSensitive);
305
    }
306
307
    /**
308
     * Get the list of patterns that should be excluded by default.
309
     *
310
     * @return string[] An array of <code>String</code> based on the current
311
     *         contents of the <code>defaultExcludes</code>
312
     *         <code>Set</code>.
313
     */
314 128
    public static function getDefaultExcludes()
315
    {
316 128
        return self::$defaultExcludeList;
317
    }
318
319
    /**
320
     * Add a pattern to the default excludes unless it is already a
321
     * default exclude.
322
     *
323
     * @param string $s A string to add as an exclude pattern.
324
     * @return boolean   <code>true</code> if the string was added;
325
     *                   <code>false</code> if it already existed.
326
     */
327 1
    public static function addDefaultExclude($s)
328
    {
329 1
        if (!in_array($s, self::$defaultExcludeList)) {
330 1
            $return = true;
331 1
            self::$defaultExcludeList[] = $s;
332
        } else {
333
            $return = false;
334
        }
335
336 1
        return $return;
337
    }
338
339
    /**
340
     * Remove a string if it is a default exclude.
341
     *
342
     * @param string $s The string to attempt to remove.
343
     * @return boolean    <code>true</code> if <code>s</code> was a default
344
     *                    exclude (and thus was removed);
345
     *                    <code>false</code> if <code>s</code> was not
346
     *                    in the default excludes list to begin with.
347
     */
348 1
    public static function removeDefaultExclude($s)
349
    {
350 1
        $key = array_search($s, self::$defaultExcludeList);
351
352 1
        if ($key !== false) {
353 1
            unset(self::$defaultExcludeList[$key]);
354 1
            self::$defaultExcludeList = array_values(self::$defaultExcludeList);
355 1
            return true;
356
        }
357
358
        return false;
359
    }
360
361
    /**
362
     * Go back to the hardwired default exclude patterns.
363
     */
364 6
    public static function resetDefaultExcludes()
365
    {
366 6
        self::$defaultExcludeList = self::$DEFAULTEXCLUDES;
367 6
    }
368
369
    /**
370
     * Sets the basedir for scanning. This is the directory that is scanned
371
     * recursively. All '/' and '\' characters are replaced by
372
     * DIRECTORY_SEPARATOR
373
     *
374
     * @param string $basedir the (non-null) basedir for scanning
375
     */
376 145
    public function setBasedir($basedir)
377
    {
378 145
        $basedir = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $basedir);
379 145
        $this->basedir = $basedir;
380 145
    }
381
382
    /**
383
     * Gets the basedir that is used for scanning. This is the directory that
384
     * is scanned recursively.
385
     *
386
     * @return string the basedir that is used for scanning
387
     */
388 21
    public function getBasedir()
389
    {
390 21
        return $this->basedir;
391
    }
392
393
    /**
394
     * Sets the case sensitivity of the file system
395
     *
396
     * @param bool $isCaseSensitive specifies if the filesystem is case sensitive
397
     */
398 129
    public function setCaseSensitive($isCaseSensitive)
399
    {
400 129
        $this->isCaseSensitive = ($isCaseSensitive) ? true : false;
401 129
    }
402
403
    /**
404
     * Sets whether or not a missing base directory is an error
405
     *
406
     * @param bool $errorOnMissingDir whether or not a missing base directory
407
     *                        is an error
408
     */
409 128
    public function setErrorOnMissingDir($errorOnMissingDir)
410
    {
411 128
        $this->errorOnMissingDir = $errorOnMissingDir;
412 128
    }
413
414
    /**
415
     * Sets the set of include patterns to use. All '/' and '\' characters are
416
     * replaced by DIRECTORY_SEPARATOR. So the separator used need
417
     * not match DIRECTORY_SEPARATOR.
418
     *
419
     * When a pattern ends with a '/' or '\', "**" is appended.
420
     *
421
     * @param array $includes list of include patterns
422
     */
423 141
    public function setIncludes($includes = [])
424
    {
425 141
        if (empty($includes)) {
426 49
            $this->includes = null;
427
        } else {
428 92
            $numIncludes = count($includes);
429 92
            for ($i = 0; $i < $numIncludes; $i++) {
430 92
                $pattern = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $includes[$i]);
431 92
                if (StringHelper::endsWith(DIRECTORY_SEPARATOR, $pattern)) {
432 2
                    $pattern .= "**";
433
                }
434 92
                $this->includes[] = $pattern;
435
            }
436
        }
437 141
    }
438
439
    /**
440
     * Sets the set of exclude patterns to use. All '/' and '\' characters are
441
     * replaced by <code>File.separatorChar</code>. So the separator used need
442
     * not match <code>File.separatorChar</code>.
443
     *
444
     * When a pattern ends with a '/' or '\', "**" is appended.
445
     *
446
     * @param array $excludes list of exclude patterns
447
     */
448 135
    public function setExcludes($excludes = [])
449
    {
450 135
        if (empty($excludes)) {
451 126
            $this->excludes = null;
452
        } else {
453 10
            $numExcludes = count($excludes);
454 10
            for ($i = 0; $i < $numExcludes; $i++) {
455 10
                $pattern = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $excludes[$i]);
456 10
                if (StringHelper::endsWith(DIRECTORY_SEPARATOR, $pattern)) {
457 1
                    $pattern .= "**";
458
                }
459 10
                $this->excludes[] = $pattern;
460
            }
461
        }
462 135
    }
463
464
    /**
465
     * Sets whether to expand/dereference symbolic links
466
     *
467
     * @param boolean $expandSymbolicLinks
468
     */
469 127
    public function setExpandSymbolicLinks($expandSymbolicLinks)
470
    {
471 127
        $this->expandSymbolicLinks = $expandSymbolicLinks;
472 127
    }
473
474
    /**
475
     * Scans the base directory for files that match at least one include
476
     * pattern, and don't match any exclude patterns.
477
     */
478 145
    public function scan()
479
    {
480 145
        if (empty($this->basedir)) {
481
            return false;
482
        }
483
484 145
        $exception = null;
485
486 145
        if (!@file_exists($this->basedir)) {
487 1
            if ($this->errorOnMissingDir) {
488 1
                $exception = new BuildException(
489 1
                    "basedir  $this->basedir does not exist."
490
                );
491
            } else {
492 1
                return false;
493
            }
494 144
        } elseif (!@is_dir($this->basedir)) {
495
            $exception = new BuildException(
496
                "basedir $this->basedir is not a directory."
497
            );
498
        }
499 145
        if ($exception !== null) {
500 1
            throw $exception;
501
        }
502
503 144
        if ($this->includes === null) {
504
            // No includes supplied, so set it to 'matches all'
505 52
            $this->includes = ["**"];
506
        }
507 144
        if (null === $this->excludes) {
508 11
            $this->excludes = [];
509
        }
510
511 144
        $this->filesIncluded = [];
512 144
        $this->filesNotIncluded = [];
513 144
        $this->filesExcluded = [];
514 144
        $this->dirsIncluded = [];
515 144
        $this->dirsNotIncluded = [];
516 144
        $this->dirsExcluded = [];
517 144
        $this->dirsDeselected = [];
518 144
        $this->filesDeselected = [];
519
520 144
        if ($this->isIncluded("")) {
521 66
            if (!$this->isExcluded("")) {
522 65
                if ($this->isSelected("", $this->basedir)) {
523 59
                    $this->dirsIncluded[] = "";
524
                } else {
525 61
                    $this->dirsDeselected[] = "";
526
                }
527
            } else {
528 62
                $this->dirsExcluded[] = "";
529
            }
530
        } else {
531 78
            $this->dirsNotIncluded[] = "";
532
        }
533
534 140
        $this->scandir($this->basedir, "", true);
535
536 140
        return true;
537
    }
538
539
    /**
540
     * Toplevel invocation for the scan.
541
     *
542
     * Returns immediately if a slow scan has already been requested.
543
     */
544
    protected function slowScan()
545
    {
546
        if ($this->haveSlowResults) {
547
            return;
548
        }
549
550
        // copy trie object add CopyInto() method
551
        $excl = $this->dirsExcluded;
552
        $notIncl = $this->dirsNotIncluded;
553
554
        for ($i = 0, $_i = count($excl); $i < $_i; $i++) {
555
            if (!$this->couldHoldIncluded($excl[$i])) {
556
                $this->scandir($this->basedir . $excl[$i], $excl[$i] . DIRECTORY_SEPARATOR, false);
557
            }
558
        }
559
560
        for ($i = 0, $_i = count($notIncl); $i < $_i; $i++) {
561
            if (!$this->couldHoldIncluded($notIncl[$i])) {
562
                $this->scandir($this->basedir . $notIncl[$i], $notIncl[$i] . DIRECTORY_SEPARATOR, false);
563
            }
564
        }
565
566
        $this->haveSlowResults = true;
567
    }
568
569
    /**
570
     * Lists contents of a given directory and returns array with entries
571
     *
572
     * @param string $_dir directory to list contents for
573
     *
574
     * @return array directory entries
575
     * @author Albert Lash, [email protected]
576
     */
577 140
    public function listDir($_dir)
578
    {
579 140
        return (new File($_dir))->listDir();
580
    }
581
582
    /**
583
     * Scans the passed dir for files and directories. Found files and
584
     * directories are placed in their respective collections, based on the
585
     * matching of includes and excludes. When a directory is found, it is
586
     * scanned recursively.
587
     *
588
     * @param string $_rootdir the directory to scan
589
     * @param string $_vpath the path relative to the basedir (needed to prevent
590
     *                         problems with an absolute path when using dir)
591
     * @param bool $_fast
592
     *
593
     * @see #filesIncluded
594
     * @see #filesNotIncluded
595
     * @see #filesExcluded
596
     * @see #dirsIncluded
597
     * @see #dirsNotIncluded
598
     * @see #dirsExcluded
599
     */
600 140
    private function scandir($_rootdir, $_vpath, $_fast)
601
    {
602 140
        if (!is_readable($_rootdir)) {
603
            return;
604
        }
605
606 140
        $newfiles = $this->listDir($_rootdir);
607
608 140
        for ($i = 0, $_i = count($newfiles); $i < $_i; $i++) {
609 140
            $file = $_rootdir . DIRECTORY_SEPARATOR . $newfiles[$i];
610 140
            $name = $_vpath . $newfiles[$i];
611
612 140
            if (@is_link($file) && !$this->expandSymbolicLinks) {
613 6
                if ($this->isIncluded($name)) {
614 2
                    if (!$this->isExcluded($name)) {
615 2
                        if ($this->isSelected($name, $file)) {
616 2
                            $this->filesIncluded[] = $name;
617
                        } else {
618
                            $this->everythingIncluded = false;
619 2
                            $this->filesDeselected[] = $name;
620
                        }
621
                    } else {
622
                        $this->everythingIncluded = false;
623 2
                        $this->filesExcluded[] = $name;
624
                    }
625
                } else {
626 4
                    $this->everythingIncluded = false;
627 6
                    $this->filesNotIncluded[] = $name;
628
                }
629
            } else {
630 138
                if (@is_dir($file)) {
631 50
                    if ($this->isIncluded($name)) {
632 36
                        if (!$this->isExcluded($name)) {
633 31
                            if ($this->isSelected($name, $file)) {
634 31
                                $this->dirsIncluded[] = $name;
635 31
                                if ($_fast) {
636 31
                                    $this->scandir($file, $name . DIRECTORY_SEPARATOR, $_fast);
637
                                }
638
                            } else {
639
                                $this->everythingIncluded = false;
640
                                $this->dirsDeselected[] = $name;
641
                                if ($_fast && $this->couldHoldIncluded($name)) {
642 31
                                    $this->scandir($file, $name . DIRECTORY_SEPARATOR, $_fast);
643
                                }
644
                            }
645
                        } else {
646 14
                            $this->everythingIncluded = false;
647 14
                            $this->dirsExcluded[] = $name;
648 14
                            if ($_fast && $this->couldHoldIncluded($name)) {
649 36
                                $this->scandir($file, $name . DIRECTORY_SEPARATOR, $_fast);
650
                            }
651
                        }
652
                    } else {
653 26
                        $this->everythingIncluded = false;
654 26
                        $this->dirsNotIncluded[] = $name;
655 26
                        if ($_fast && $this->couldHoldIncluded($name)) {
656 10
                            $this->scandir($file, $name . DIRECTORY_SEPARATOR, $_fast);
657
                        }
658
                    }
659
660 50
                    if (!$_fast) {
661 50
                        $this->scandir($file, $name . DIRECTORY_SEPARATOR, $_fast);
662
                    }
663 134
                } elseif (@is_file($file)) {
664 134
                    if ($this->isIncluded($name)) {
665 122
                        if (!$this->isExcluded($name)) {
666 116
                            if ($this->isSelected($name, $file)) {
667 115
                                $this->filesIncluded[] = $name;
668
                            } else {
669 15
                                $this->everythingIncluded = false;
670 116
                                $this->filesDeselected[] = $name;
671
                            }
672
                        } else {
673 17
                            $this->everythingIncluded = false;
674 122
                            $this->filesExcluded[] = $name;
675
                        }
676
                    } else {
677 59
                        $this->everythingIncluded = false;
678 59
                        $this->filesNotIncluded[] = $name;
679
                    }
680
                }
681
            }
682
        }
683 140
    }
684
685
    /**
686
     * Tests whether a name matches against at least one include pattern.
687
     *
688
     * @param string $_name the name to match
689
     * @return bool <code>true</code> when the name matches against at least one
690
     */
691 144
    protected function isIncluded($_name)
692
    {
693 144
        for ($i = 0, $_i = count($this->includes); $i < $_i; $i++) {
694 144
            if ($this->matchPath($this->includes[$i], $_name, $this->isCaseSensitive)) {
695 141
                return true;
696
            }
697
        }
698
699 78
        return false;
700
    }
701
702
    /**
703
     * Tests whether a name matches the start of at least one include pattern.
704
     *
705
     * @param string $_name the name to match
706
     * @return bool <code>true</code> when the name matches against at least one
707
     *                           include pattern, <code>false</code> otherwise.
708
     */
709 38
    protected function couldHoldIncluded($_name)
710
    {
711 38
        for ($i = 0, $includesCount = count($this->includes); $i < $includesCount; $i++) {
712 38
            if ($this->matchPatternStart($this->includes[$i], $_name, $this->isCaseSensitive)) {
713 22
                return true;
714
            }
715
        }
716
717 20
        return false;
718
    }
719
720
    /**
721
     * Tests whether a name matches against at least one exclude pattern.
722
     *
723
     * @param string $_name the name to match
724
     * @return bool <code>true</code> when the name matches against at least one
725
     *                           exclude pattern, <code>false</code> otherwise.
726
     */
727 141
    protected function isExcluded($_name)
728
    {
729 141
        for ($i = 0, $excludesCount = count($this->excludes); $i < $excludesCount; $i++) {
730 132
            if ($this->matchPath($this->excludes[$i], $_name, $this->isCaseSensitive)) {
731 17
                return true;
732
            }
733
        }
734
735 138
        return false;
736
    }
737
738
    /**
739
     * Return the count of included files.
740
     *
741
     * @return int
742
     *
743
     * @throws UnexpectedValueException
744
     */
745
    public function getIncludedFilesCount(): int
746
    {
747
        if ($this->filesIncluded === null) {
748
            throw new UnexpectedValueException('Must call scan() first');
749
        }
750
        return count($this->filesIncluded);
751
    }
752
753
    /**
754
     * Get the names of the files that matched at least one of the include
755
     * patterns, and matched none of the exclude patterns.
756
     * The names are relative to the basedir.
757
     *
758
     * @return array names of the files
759
     * @throws UnexpectedValueException
760
     */
761 136
    public function getIncludedFiles(): array
762
    {
763 136
        if ($this->filesIncluded === null) {
764
            throw new UnexpectedValueException('Must call scan() first');
765
        }
766
767 136
        sort($this->filesIncluded);
768
769 136
        return $this->filesIncluded;
770
    }
771
772
    /**
773
     * Get the names of the files that matched at none of the include patterns.
774
     * The names are relative to the basedir.
775
     *
776
     * @return array the names of the files
777
     */
778
    public function getNotIncludedFiles()
779
    {
780
        $this->slowScan();
781
782
        return $this->filesNotIncluded;
783
    }
784
785
    /**
786
     * Get the names of the files that matched at least one of the include
787
     * patterns, an matched also at least one of the exclude patterns.
788
     * The names are relative to the basedir.
789
     *
790
     * @return array the names of the files
791
     */
792
793
    public function getExcludedFiles()
794
    {
795
        $this->slowScan();
796
797
        return $this->filesExcluded;
798
    }
799
800
    /**
801
     * <p>Returns the names of the files which were selected out and
802
     * therefore not ultimately included.</p>
803
     *
804
     * <p>The names are relative to the base directory. This involves
805
     * performing a slow scan if one has not already been completed.</p>
806
     *
807
     * @return array the names of the files which were deselected.
808
     *
809
     * @see #slowScan
810
     */
811
    public function getDeselectedFiles()
812
    {
813
        $this->slowScan();
814
815
        return $this->filesDeselected;
816
    }
817
818
    /**
819
     * Get the names of the directories that matched at least one of the include
820
     * patterns, an matched none of the exclude patterns.
821
     * The names are relative to the basedir.
822
     *
823
     * @return array the names of the directories
824
     * @throws UnexpectedValueException
825
     */
826
827 84
    public function getIncludedDirectories()
828
    {
829 84
        if ($this->dirsIncluded === null) {
830
            throw new UnexpectedValueException('Must call scan() first');
831
        }
832
833 84
        sort($this->dirsIncluded);
834
835 84
        return $this->dirsIncluded;
836
    }
837
838
    /**
839
     * Return the count of included directories.
840
     *
841
     * @return int
842
     *
843
     * @throws UnexpectedValueException
844
     */
845
    public function getIncludedDirectoriesCount(): int
846
    {
847
        if ($this->dirsIncluded === null) {
848
            throw new UnexpectedValueException('Must call scan() first');
849
        }
850
        return count($this->dirsIncluded);
851
    }
852
853
    /**
854
     * Get the names of the directories that matched at none of the include
855
     * patterns.
856
     * The names are relative to the basedir.
857
     *
858
     * @return array the names of the directories
859
     */
860
    public function getNotIncludedDirectories()
861
    {
862
        $this->slowScan();
863
864
        return $this->dirsNotIncluded;
865
    }
866
867
    /**
868
     * <p>Returns the names of the directories which were selected out and
869
     * therefore not ultimately included.</p>
870
     *
871
     * <p>The names are relative to the base directory. This involves
872
     * performing a slow scan if one has not already been completed.</p>
873
     *
874
     * @return array the names of the directories which were deselected.
875
     *
876
     * @see #slowScan
877
     */
878
    public function getDeselectedDirectories()
879
    {
880
        $this->slowScan();
881
882
        return $this->dirsDeselected;
883
    }
884
885
    /**
886
     * Get the names of the directories that matched at least one of the include
887
     * patterns, an matched also at least one of the exclude patterns.
888
     * The names are relative to the basedir.
889
     *
890
     * @return array the names of the directories
891
     */
892
    public function getExcludedDirectories()
893
    {
894
        $this->slowScan();
895
896
        return $this->dirsExcluded;
897
    }
898
899
    /**
900
     * Adds the array with default exclusions to the current exclusions set.
901
     */
902 125
    public function addDefaultExcludes()
903
    {
904 125
        $defaultExcludesTemp = self::getDefaultExcludes();
905 125
        $newExcludes = [];
906 125
        foreach ($defaultExcludesTemp as $temp) {
907 125
            $newExcludes[] = str_replace(['\\', '/'], FileUtils::getSeparator(), $temp);
908
        }
909 125
        $this->excludes = array_merge((array) $this->excludes, $newExcludes);
910 125
    }
911
912
    /**
913
     * Sets the selectors that will select the filelist.
914
     *
915
     * @param array $selectors the selectors to be invoked on a scan
916
     */
917 127
    public function setSelectors($selectors)
918
    {
919 127
        $this->selectorsList = $selectors;
920 127
    }
921
922
    /**
923
     * Returns whether or not the scanner has included all the files or
924
     * directories it has come across so far.
925
     *
926
     * @return bool <code>true</code> if all files and directories which have
927
     */
928 38
    public function isEverythingIncluded()
929
    {
930 38
        return $this->everythingIncluded;
931
    }
932
933
    /**
934
     * Tests whether a name should be selected.
935
     *
936
     * @param string $name The filename to check for selecting.
937
     * @param string $file The full file path.
938
     * @return boolean False when the selectors says that the file
939
     *                      should not be selected, True otherwise.
940
     * @throws BuildException
941
     * @throws IOException
942
     * @throws NullPointerException
943
     */
944 138
    protected function isSelected($name, $file)
945
    {
946 138
        if ($this->selectorsList !== null) {
947 125
            $basedir = new File($this->basedir);
948 125
            $file = new File($file);
949 125
            if (!$file->canRead()) {
950
                return false;
951
            }
952
953 125
            foreach ($this->selectorsList as $selector) {
954 23
                if (!$selector->isSelected($basedir, $name, $file)) {
955 16
                    return false;
956
                }
957
            }
958
        }
959
960 133
        return true;
961
    }
962
}
963