AbstractFileSet::setExpandSymbolicLinks()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 6
ccs 3
cts 4
cp 0.75
crap 2.0625
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
5
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
6
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
7
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
8
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
9
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
10
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
11
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
12
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
13
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
14
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15
 *
16
 * This software consists of voluntary contributions made by many individuals
17
 * and is licensed under the LGPL. For more information please see
18
 * <http://phing.info>.
19
 */
20
21
namespace Phing\Type;
22
23
use ArrayIterator;
24
use Exception;
25
use IteratorAggregate;
26
use Phing\Exception\BuildException;
27
use Phing\Io\DirectoryScanner;
28
use Phing\Io\File;
29
use Phing\Io\IOException;
30
use Phing\Project;
31
use Phing\ProjectComponent;
32
use Phing\Type\Selector\FileSelector;
33
use Phing\Type\Selector\SelectorAware;
34
use Phing\Type\Selector\SelectorContainer;
35
use Phing\Type\Selector\SelectorScanner;
36
37
/**
38
 * The FileSet class provides methods and properties for accessing
39
 * and managing filesets. It extends ProjectComponent and thus inherits
40
 * all methods and properties (not explicitly declared). See ProjectComponent
41
 * for further detail.
42
 *
43
 * TODO:
44
 *   - merge this with patternsets: FileSet extends PatternSet !!!
45
 *     requires additional mods to the parsing algo
46
 *         [HL] .... not sure if that really makes so much sense.  I think
47
 *            that perhaps they should use common utility class if there really
48
 *            is that much shared functionality
49
 *
50
 * @author  Andreas Aderhold <[email protected]>
51
 * @author  Hans Lellelid <[email protected]>
52
 *
53
 * @see     ProjectComponent
54
 */
55
abstract class AbstractFileSet extends DataType implements SelectorContainer, IteratorAggregate
56
{
57
    use SelectorAware;
58
59
    // These vars are public for cloning purposes
60
61
    /**
62
     * @var bool
63
     */
64
    public $useDefaultExcludes = true;
65
66
    /**
67
     * @var PatternSet
68
     */
69
    public $defaultPatternSet;
70
71
    /**
72
     * @var PatternSet[]
73
     */
74
    public $additionalPatternSets = [];
75
    public $dir;
76
    public $isCaseSensitive = true;
77
78
    /**
79
     * Whether to expand/dereference symbolic links, default is false.
80
     *
81
     * @var bool
82
     */
83
    protected $expandSymbolicLinks = false;
84
    private $errorOnMissingDir = true;
85
    private $directoryScanner;
86
87
    /**
88
     * @param null $fileset
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $fileset is correct as it would always require null to be passed?
Loading history...
89
     */
90 156
    public function __construct($fileset = null)
91
    {
92 156
        parent::__construct();
93
94 156
        if (null !== $fileset && ($fileset instanceof FileSet)) {
0 ignored issues
show
introduced by
The condition null !== $fileset is always false.
Loading history...
95 2
            $this->dir = $fileset->dir;
96 2
            $this->additionalPatternSets = $fileset->additionalPatternSets;
97 2
            $this->useDefaultExcludes = $fileset->useDefaultExcludes;
98 2
            $this->isCaseSensitive = $fileset->isCaseSensitive;
99 2
            $this->selectorsList = $fileset->selectorsList;
100 2
            $this->expandSymbolicLinks = $fileset->expandSymbolicLinks;
101 2
            $this->errorOnMissingDir = $fileset->errorOnMissingDir;
102 2
            $this->setProject($fileset->getProject());
103
        }
104
105 156
        $this->defaultPatternSet = new PatternSet();
106
    }
107
108 1
    public function __toString()
109
    {
110
        try {
111 1
            if ($this->isReference()) {
112
                return (string) $this->getRef($this->getProject());
113
            }
114 1
            $stk[] = $this;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$stk was never initialized. Although not strictly required by PHP, it is generally a good practice to add $stk = array(); before regardless.
Loading history...
115 1
            $this->dieOnCircularReference($stk, $this->getProject());
116 1
            $ds = $this->getDirectoryScanner($this->getProject());
117 1
            $files = $ds->getIncludedFiles();
118 1
            $result = implode(';', $files);
119
        } catch (BuildException $e) {
120
            $result = '';
121
        }
122
123 1
        return $result;
124
    }
125
126
    /**
127
     * Sets whether to expand/dereference symbolic links, default is false.
128
     */
129 1
    public function setExpandSymbolicLinks(bool $expandSymbolicLinks)
130
    {
131 1
        if ($this->isReference()) {
132 1
            throw $this->tooManyAttributes();
133
        }
134
        $this->expandSymbolicLinks = $expandSymbolicLinks;
135
    }
136
137
    /**
138
     * Makes this instance in effect a reference to another PatternSet
139
     * instance.
140
     * You must not set another attribute or nest elements inside
141
     * this element if you make it a reference.
142
     *
143
     * @throws BuildException
144
     */
145 3
    public function setRefid(Reference $r)
146
    {
147 3
        if ((isset($this->dir) && null !== $this->dir) || $this->defaultPatternSet->hasPatterns()) {
148 1
            throw $this->tooManyAttributes();
149
        }
150 3
        if (!empty($this->additionalPatternSets)) {
151 1
            throw $this->noChildrenAllowed();
152
        }
153 3
        if (!empty($this->selectorsList)) {
154
            throw $this->noChildrenAllowed();
155
        }
156 3
        parent::setRefid($r);
157
    }
158
159
    /**
160
     * @param File|string $dir
161
     *
162
     * @throws IOException
163
     * @throws \InvalidArgumentException
164
     */
165 152
    public function setDir($dir)
166
    {
167 152
        if ($this->isReference()) {
168 1
            throw $this->tooManyAttributes();
169
        }
170 151
        if ($dir instanceof File) {
171 5
            $dir = $dir->getPath();
172
        }
173 151
        $this->dir = new File((string) $dir);
174 151
        $this->directoryScanner = null;
175
    }
176
177
    /**
178
     * @param Project|null $p
179
     *
180
     * @throws BuildException
181
     */
182 106
    public function getDir(?Project $p = null)
183
    {
184 106
        if (null === $p) {
185
            $p = $this->getProject();
186
        }
187
188 106
        if ($this->isReference()) {
189 1
            return $this->getRef($p)->getDir($p);
190
        }
191
192 106
        return $this->dir;
193
    }
194
195
    /**
196
     * @throws BuildException
197
     */
198 2
    public function createPatternSet()
199
    {
200 2
        if ($this->isReference()) {
201 1
            throw $this->noChildrenAllowed();
202
        }
203 2
        $num = array_push($this->additionalPatternSets, new PatternSet());
204
205 2
        return $this->additionalPatternSets[$num - 1];
206
    }
207
208
    /**
209
     * add a name entry on the include list.
210
     */
211 67
    public function createInclude()
212
    {
213 67
        if ($this->isReference()) {
214 1
            throw $this->noChildrenAllowed();
215
        }
216
217 67
        return $this->defaultPatternSet->createInclude();
218
    }
219
220
    /**
221
     * add a name entry on the include files list.
222
     */
223 1
    public function createIncludesFile()
224
    {
225 1
        if ($this->isReference()) {
226 1
            throw $this->noChildrenAllowed();
227
        }
228
229
        return $this->defaultPatternSet->createIncludesFile();
230
    }
231
232
    /**
233
     * add a name entry on the exclude list.
234
     */
235 3
    public function createExclude()
236
    {
237 3
        if ($this->isReference()) {
238 1
            throw $this->noChildrenAllowed();
239
        }
240
241 2
        return $this->defaultPatternSet->createExclude();
242
    }
243
244
    /**
245
     * add a name entry on the include files list.
246
     */
247 1
    public function createExcludesFile()
248
    {
249 1
        if ($this->isReference()) {
250 1
            throw $this->noChildrenAllowed();
251
        }
252
253
        return $this->defaultPatternSet->createExcludesFile();
254
    }
255
256 2
    public function setFile(File $file)
257
    {
258 2
        if ($this->isReference()) {
259 1
            throw $this->tooManyAttributes();
260
        }
261 1
        $this->setDir($file->getParentFile());
262 1
        $this->createInclude()->setName($file->getName());
263
    }
264
265
    /**
266
     * Sets the set of include patterns. Patterns may be separated by a comma
267
     * or a space.
268
     *
269
     * @param string $includes
270
     *
271
     * @throws BuildException
272
     */
273 25
    public function setIncludes($includes)
274
    {
275 25
        if ($this->isReference()) {
276 1
            throw $this->tooManyAttributes();
277
        }
278 25
        $this->defaultPatternSet->setIncludes($includes);
279
    }
280
281
    /**
282
     * Sets the set of exclude patterns. Patterns may be separated by a comma
283
     * or a space.
284
     *
285
     * @param string $excludes
286
     *
287
     * @throws BuildException
288
     */
289 2
    public function setExcludes($excludes)
290
    {
291 2
        if ($this->isReference()) {
292 1
            throw $this->tooManyAttributes();
293
        }
294 1
        $this->defaultPatternSet->setExcludes($excludes);
295
    }
296
297
    /**
298
     * Sets the name of the file containing the includes patterns.
299
     *
300
     * @param File $incl the file to fetch the include patterns from
301
     *
302
     * @throws BuildException
303
     */
304 2
    public function setIncludesfile(File $incl)
305
    {
306 2
        if ($this->isReference()) {
307 1
            throw $this->tooManyAttributes();
308
        }
309 1
        $this->defaultPatternSet->setIncludesFile($incl);
310
    }
311
312
    /**
313
     * Sets the name of the file containing the includes patterns.
314
     *
315
     * @param File $excl the file to fetch the exclude patterns from
316
     *
317
     * @throws BuildException
318
     */
319 2
    public function setExcludesfile(File $excl)
320
    {
321 2
        if ($this->isReference()) {
322 1
            throw $this->tooManyAttributes();
323
        }
324 1
        $this->defaultPatternSet->setExcludesFile($excl);
325
    }
326
327
    /**
328
     * Sets whether default exclusions should be used or not.
329
     *
330
     * @param bool $useDefaultExcludes "true"|"on"|"yes" when default exclusions
331
     *                                 should be used, "false"|"off"|"no" when they
332
     *                                 shouldn't be used
333
     *
334
     * @throws BuildException
335
     */
336 4
    public function setDefaultexcludes($useDefaultExcludes)
337
    {
338 4
        if ($this->isReference()) {
339
            throw $this->tooManyAttributes();
340
        }
341 4
        $this->useDefaultExcludes = $useDefaultExcludes;
342
    }
343
344
    /**
345
     * Sets case sensitivity of the file system.
346
     *
347
     * @param bool $isCaseSensitive
348
     */
349 1
    public function setCaseSensitive($isCaseSensitive)
350
    {
351 1
        if ($this->isReference()) {
352 1
            throw $this->tooManyAttributes();
353
        }
354
        $this->isCaseSensitive = $isCaseSensitive;
355
    }
356
357
    /**
358
     * Sets whether to fail the build if the base directory does not exist
359
     *
360
     * @param bool $errorOnMissingDir
361
     * @return void
362
     */
363 2
    public function setErrorOnMissingDir(bool $errorOnMissingDir)
364
    {
365 2
        $this->errorOnMissingDir = $errorOnMissingDir;
366
    }
367
368
    /**
369
     * returns a reference to the dirscanner object belonging to this fileset.
370
     *
371
     * @param Project|null $p
372
     *
373
     * @throws BuildException
374
     *
375
     * @return DirectoryScanner
376
     */
377 136
    public function getDirectoryScanner(?Project $p = null)
378
    {
379 136
        if (null === $p) {
380 2
            $p = $this->getProject();
381
        }
382
383 136
        if ($this->isReference()) {
384 1
            $o = $this->getRef($p);
385
386 1
            return $o->getDirectoryScanner($p);
387
        }
388
389 136
        if (null === $this->dir) {
390
            throw new BuildException(sprintf('No directory specified for <%s>.', $this->getDataTypeName()));
391
        }
392 136
        if (!$this->dir->exists() && $this->errorOnMissingDir) {
393 2
            throw new BuildException('Directory ' . $this->dir->getAbsolutePath() . ' not found.');
394
        }
395 134
        if (!$this->dir->isLink() || !$this->expandSymbolicLinks) {
396 134
            if (!$this->dir->isDirectory() && $this->errorOnMissingDir) {
397
                throw new BuildException($this->dir->getAbsolutePath() . ' is not a directory.');
398
            }
399
        }
400 134
        $ds = new DirectoryScanner();
401 134
        $ds->setExpandSymbolicLinks($this->expandSymbolicLinks);
402 134
        $ds->setErrorOnMissingDir($this->errorOnMissingDir);
403 134
        $this->setupDirectoryScanner($ds, $p);
404 134
        $ds->scan();
405
406 130
        return $ds;
407
    }
408
409 134
    public function dieOnCircularReference(&$stk, ?Project $p = null)
410
    {
411 134
        if ($this->checked) {
412 113
            return;
413
        }
414 24
        if ($this->isReference()) {
415 2
            parent::dieOnCircularReference($stk, $p);
416
        } else {
417 23
            foreach ($this->selectorsList as $fileSelector) {
418 23
                if ($fileSelector instanceof DataType) {
419 23
                    static::pushAndInvokeCircularReferenceCheck($fileSelector, $stk, $p);
0 ignored issues
show
Bug introduced by
It seems like $p can also be of type null; however, parameter $p of Phing\Type\DataType::pus...ircularReferenceCheck() does only seem to accept Phing\Project, maybe add an additional type check? ( Ignorable by Annotation )

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

419
                    static::pushAndInvokeCircularReferenceCheck($fileSelector, $stk, /** @scrutinizer ignore-type */ $p);
Loading history...
420
                }
421
            }
422 23
            foreach ($this->additionalPatternSets as $ps) {
423
                static::pushAndInvokeCircularReferenceCheck($ps, $stk, $p);
424
            }
425 23
            $this->setChecked(true);
426
        }
427
    }
428
429
    /**
430
     * Performs the check for circular references and returns the
431
     * referenced FileSet.
432
     *
433
     * @throws BuildException
434
     *
435
     * @return FileSet
436
     */
437 2
    public function getRef(Project $p)
0 ignored issues
show
Unused Code introduced by
The parameter $p is not used and could be removed. ( Ignorable by Annotation )

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

437
    public function getRef(/** @scrutinizer ignore-unused */ Project $p)

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

Loading history...
438
    {
439 2
        return $this->getCheckedRef(__CLASS__, $this->getDataTypeName());
440
    }
441
442
    // SelectorContainer methods
443
444
    /**
445
     * Indicates whether there are any selectors here.
446
     *
447
     * @return bool Whether any selectors are in this container
448
     */
449
    public function hasSelectors()
450
    {
451
        if ($this->isReference() && null !== $this->getProject()) {
452
            return $this->getRef($this->getProject())->hasSelectors();
453
        }
454
        $stk[] = $this;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$stk was never initialized. Although not strictly required by PHP, it is generally a good practice to add $stk = array(); before regardless.
Loading history...
455
        $this->dieOnCircularReference($stk, $this->getProject());
456
457
        return !empty($this->selectorsList);
458
    }
459
460
    /**
461
     * Indicates whether there are any patterns here.
462
     *
463
     * @return bool whether any patterns are in this container
464
     */
465
    public function hasPatterns()
466
    {
467
        if ($this->isReference() && null !== $this->getProject()) {
468
            return $this->getRef($this->getProject())->hasPatterns();
469
        }
470
        $stk[] = $this;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$stk was never initialized. Although not strictly required by PHP, it is generally a good practice to add $stk = array(); before regardless.
Loading history...
471
        $this->dieOnCircularReference($stk, $this->getProject());
472
473
        if ($this->defaultPatternSet->hasPatterns()) {
474
            return true;
475
        }
476
477
        for ($i = 0, $size = count($this->additionalPatternSets); $i < $size; ++$i) {
478
            $ps = $this->additionalPatternSets[$i];
479
            if ($ps->hasPatterns()) {
480
                return true;
481
            }
482
        }
483
484
        return false;
485
    }
486
487
    /**
488
     * Gives the count of the number of selectors in this container.
489
     *
490
     * @throws Exception
491
     *
492
     * @return int The number of selectors in this container
493
     */
494
    public function count()
495
    {
496
        if ($this->isReference() && null !== $this->getProject()) {
497
            try {
498
                return $this->getRef($this->getProject())->count();
499
            } catch (Exception $e) {
500
                throw $e;
501
            }
502
        }
503
504
        return count($this->selectorsList);
505
    }
506
507
    /**
508
     * Returns the set of selectors as an array.
509
     *
510
     * @throws BuildException
511
     *
512
     * @return array of selectors in this container
513
     */
514 134
    public function getSelectors(Project $p)
515
    {
516 134
        if ($this->isReference()) {
517
            return $this->getRef($p)->getSelectors($p);
518
        }
519
520
        // *copy* selectors
521 134
        $result = [];
522 134
        for ($i = 0, $size = count($this->selectorsList); $i < $size; ++$i) {
523 23
            $result[] = clone $this->selectorsList[$i];
524
        }
525
526 134
        return $result;
527
    }
528
529
    /**
530
     * Returns an array for accessing the set of selectors.
531
     *
532
     * @return array The array of selectors
533
     */
534
    public function selectorElements()
535
    {
536
        if ($this->isReference() && null !== $this->getProject()) {
537
            return $this->getRef($this->getProject())->selectorElements();
538
        }
539
540
        return $this->selectorsList;
541
    }
542
543
    /**
544
     * Add a new selector into this container.
545
     *
546
     * @param FileSelector $selector new selector to add
547
     *
548
     * @throws BuildException
549
     */
550 25
    public function appendSelector(FileSelector $selector)
551
    {
552 25
        if ($this->isReference()) {
553
            throw $this->noChildrenAllowed();
554
        }
555 25
        $this->selectorsList[] = $selector;
556 25
        $this->directoryScanner = null;
557 25
        $this->setChecked(false);
558
    }
559
560
    /**
561
     * @param array ...$options
562
     */
563 27
    public function getIterator(...$options): ArrayIterator
564
    {
565 27
        if ($this->isReference()) {
566 1
            return $this->getRef($this->getProject())->getIterator($options);
567
        }
568
569 27
        return new ArrayIterator($this->getFiles($options));
570
    }
571
572
    /**
573
     * feed dirscanner with infos defined by this fileset.
574
     *
575
     * @param Project|null $p
576
     *
577
     * @throws BuildException
578
     */
579 134
    protected function setupDirectoryScanner(DirectoryScanner $ds, ?Project $p = null)
580
    {
581 134
        if (null === $p) {
582
            $p = $this->getProject();
583
        }
584
585 134
        if ($this->isReference()) {
586
            $this->getRef($p)->setupDirectoryScanner($ds, $p);
587
588
            return;
589
        }
590
591 134
        $stk[] = $this;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$stk was never initialized. Although not strictly required by PHP, it is generally a good practice to add $stk = array(); before regardless.
Loading history...
592 134
        $this->dieOnCircularReference($stk, $p);
593 134
        array_pop($stk);
594
595
        // FIXME - pass dir directly when dirscanner supports File
596 134
        $ds->setBasedir($this->dir->getPath());
597
598 134
        foreach ($this->additionalPatternSets as $addPattern) {
599 1
            $this->defaultPatternSet->append($addPattern, $p);
600
        }
601
602 134
        $ds->setIncludes($this->defaultPatternSet->getIncludePatterns($p));
603 134
        $ds->setExcludes($this->defaultPatternSet->getExcludePatterns($p));
604
605 134
        $p->log(
606 134
            $this->getDataTypeName() . ': Setup file scanner in dir ' . (string) $this->dir . ' with ' . (string) $this->defaultPatternSet,
607 134
            Project::MSG_DEBUG
608 134
        );
609
610 134
        if ($ds instanceof SelectorScanner) {
0 ignored issues
show
introduced by
$ds is always a sub-type of Phing\Type\Selector\SelectorScanner.
Loading history...
611 134
            $selectors = $this->getSelectors($p);
612 134
            foreach ($selectors as $selector) {
613 23
                $p->log((string) $selector . PHP_EOL, Project::MSG_DEBUG);
614
            }
615 134
            $ds->setSelectors($selectors);
616
        }
617
618 134
        if ($this->useDefaultExcludes) {
619 131
            $ds->addDefaultExcludes();
620
        }
621 134
        $ds->setCaseSensitive($this->isCaseSensitive);
622
    }
623
624
    abstract protected function getFiles(...$options);
625
}
626