Passed
Push — master ( e1f86a...4e1a3a )
by Siad
05:23
created

DirectoryScanner::setErrorOnMissingDir()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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