Passed
Push — master ( a862c8...fef60d )
by Siad
05:54
created

DirectoryScanner::scan()   B

Complexity

Conditions 11
Paths 53

Size

Total Lines 59
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 32
CRAP Score 11.0761

Importance

Changes 0
Metric Value
cc 11
eloc 38
c 0
b 0
f 0
nc 53
nop 0
dl 0
loc 59
ccs 32
cts 35
cp 0.9143
crap 11.0761
rs 7.3166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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