Passed
Push — main ( 221f6d...f8c128 )
by Siad
05:28
created

src/Phing/Type/AbstractFileSet.php (9 issues)

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 = false;
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 153
    public function __construct($fileset = null)
91
    {
92 153
        parent::__construct();
93
94 153
        if (null !== $fileset && ($fileset instanceof FileSet)) {
0 ignored issues
show
The condition null !== $fileset is always false.
Loading history...
95 1
            $this->dir = $fileset->dir;
96 1
            $this->additionalPatternSets = $fileset->additionalPatternSets;
97 1
            $this->useDefaultExcludes = $fileset->useDefaultExcludes;
98 1
            $this->isCaseSensitive = $fileset->isCaseSensitive;
99 1
            $this->selectorsList = $fileset->selectorsList;
100 1
            $this->expandSymbolicLinks = $fileset->expandSymbolicLinks;
101 1
            $this->errorOnMissingDir = $fileset->errorOnMissingDir;
102 1
            $this->setProject($fileset->getProject());
103
        }
104
105 153
        $this->defaultPatternSet = new PatternSet();
106 153
    }
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 3
    }
158
159
    /**
160
     * @param File|string $dir
161
     *
162
     * @throws IOException
163
     * @throws \InvalidArgumentException
164
     */
165 150
    public function setDir($dir)
166
    {
167 150
        if ($this->isReference()) {
168 1
            throw $this->tooManyAttributes();
169
        }
170 149
        if ($dir instanceof File) {
171 2
            $dir = $dir->getPath();
172
        }
173 149
        $this->dir = new File((string) $dir);
174 149
        $this->directoryScanner = null;
175 149
    }
176
177
    /**
178
     * @param Project $p
179
     *
180
     * @throws BuildException
181
     */
182 110
    public function getDir(Project $p = null)
183
    {
184 110
        if (null === $p) {
185
            $p = $this->getProject();
186
        }
187
188 110
        if ($this->isReference()) {
189 1
            return $this->getRef($p)->getDir($p);
190
        }
191
192 110
        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 74
    public function createInclude()
212
    {
213 74
        if ($this->isReference()) {
214 1
            throw $this->noChildrenAllowed();
215
        }
216
217 74
        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 2
    public function createExclude()
236
    {
237 2
        if ($this->isReference()) {
238 1
            throw $this->noChildrenAllowed();
239
        }
240
241 1
        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 1
    }
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 19
    public function setIncludes($includes)
274
    {
275 19
        if ($this->isReference()) {
276 1
            throw $this->tooManyAttributes();
277
        }
278 19
        $this->defaultPatternSet->setIncludes($includes);
279 19
    }
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 1
    }
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 1
    public function setIncludesfile(File $incl)
305
    {
306 1
        if ($this->isReference()) {
307 1
            throw $this->tooManyAttributes();
308
        }
309
        $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 1
    public function setExcludesfile($excl)
320
    {
321 1
        if ($this->isReference()) {
322 1
            throw $this->tooManyAttributes();
323
        }
324
        $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 3
    public function setDefaultexcludes($useDefaultExcludes)
337
    {
338 3
        if ($this->isReference()) {
339
            throw $this->tooManyAttributes();
340
        }
341 3
        $this->useDefaultExcludes = $useDefaultExcludes;
342 3
    }
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
     * returns a reference to the dirscanner object belonging to this fileset.
359
     *
360
     * @param Project $p
361
     *
362
     * @throws BuildException
363
     *
364
     * @return DirectoryScanner
365
     */
366 132
    public function getDirectoryScanner(Project $p = null)
367
    {
368 132
        if (null === $p) {
369
            $p = $this->getProject();
370
        }
371
372 132
        if ($this->isReference()) {
373 1
            $o = $this->getRef($p);
374
375 1
            return $o->getDirectoryScanner($p);
376
        }
377
378 132
        if (null === $this->dir) {
379
            throw new BuildException(sprintf('No directory specified for <%s>.', $this->getDataTypeName()));
380
        }
381 132
        if (!$this->dir->exists() && $this->errorOnMissingDir) {
382
            throw new BuildException('Directory ' . $this->dir->getAbsolutePath() . ' not found.');
383
        }
384 132
        if (!$this->dir->isLink() || !$this->expandSymbolicLinks) {
385 132
            if (!$this->dir->isDirectory()) {
386 1
                throw new BuildException($this->dir->getAbsolutePath() . ' is not a directory.');
387
            }
388
        }
389 131
        $ds = new DirectoryScanner();
390 131
        $ds->setExpandSymbolicLinks($this->expandSymbolicLinks);
391 131
        $ds->setErrorOnMissingDir($this->errorOnMissingDir);
392 131
        $this->setupDirectoryScanner($ds, $p);
393 131
        $ds->scan();
394
395 127
        return $ds;
396
    }
397
398 131
    public function dieOnCircularReference(&$stk, Project $p = null)
399
    {
400 131
        if ($this->checked) {
401 110
            return;
402
        }
403 24
        if ($this->isReference()) {
404 2
            parent::dieOnCircularReference($stk, $p);
405
        } else {
406 23
            foreach ($this->selectorsList as $fileSelector) {
407 23
                if ($fileSelector instanceof DataType) {
408 23
                    static::pushAndInvokeCircularReferenceCheck($fileSelector, $stk, $p);
0 ignored issues
show
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

408
                    static::pushAndInvokeCircularReferenceCheck($fileSelector, $stk, /** @scrutinizer ignore-type */ $p);
Loading history...
409
                }
410
            }
411 23
            foreach ($this->additionalPatternSets as $ps) {
412
                static::pushAndInvokeCircularReferenceCheck($ps, $stk, $p);
413
            }
414 23
            $this->setChecked(true);
415
        }
416 24
    }
417
418
    /**
419
     * Performs the check for circular references and returns the
420
     * referenced FileSet.
421
     *
422
     * @throws BuildException
423
     *
424
     * @return FileSet
425
     */
426 2
    public function getRef(Project $p)
0 ignored issues
show
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

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