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

AbstractFileSet::createPatternSet()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 2
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\Type;
21
22
use ArrayIterator;
23
use Exception;
24
use IteratorAggregate;
25
use Phing\Exception\BuildException;
26
use Phing\Exception\NullPointerException;
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
 * @see     ProjectComponent
53
 * @package phing.types
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 boolean
63
     */
64
    public $useDefaultExcludes = true;
65
66
    /**
67
     * Whether to expand/dereference symbolic links, default is false
68
     *
69
     * @var boolean
70
     */
71
    protected $expandSymbolicLinks = false;
72
73
    /**
74
     * @var PatternSet
75
     */
76
    public $defaultPatterns;
77
78
    public $additionalPatterns = [];
79
    public $dir;
80
    public $isCaseSensitive = true;
81
    private $errorOnMissingDir = false;
82
    private $directoryScanner;
83
84
    /**
85
     * @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...
86
     */
87 149
    public function __construct($fileset = null)
88
    {
89 149
        parent::__construct();
90
91 149
        if ($fileset !== null && ($fileset instanceof FileSet)) {
0 ignored issues
show
introduced by
The condition $fileset !== null is always false.
Loading history...
92 1
            $this->dir = $fileset->dir;
93 1
            $this->additionalPatterns = $fileset->additionalPatterns;
94 1
            $this->useDefaultExcludes = $fileset->useDefaultExcludes;
95 1
            $this->isCaseSensitive = $fileset->isCaseSensitive;
96 1
            $this->selectorsList = $fileset->selectorsList;
97 1
            $this->expandSymbolicLinks = $fileset->expandSymbolicLinks;
98 1
            $this->errorOnMissingDir = $fileset->errorOnMissingDir;
99 1
            $this->setProject($fileset->getProject());
100
        }
101
102 149
        $this->defaultPatterns = new PatternSet();
103 149
    }
104
105
    /**
106
     * Sets whether to expand/dereference symbolic links, default is false
107
     *
108
     * @var boolean
109
     */
110 1
    public function setExpandSymbolicLinks(bool $expandSymbolicLinks)
111
    {
112 1
        if ($this->isReference()) {
113 1
            throw $this->tooManyAttributes();
114
        }
115
        $this->expandSymbolicLinks = $expandSymbolicLinks;
116
    }
117
118
    /**
119
     * Makes this instance in effect a reference to another PatternSet
120
     * instance.
121
     * You must not set another attribute or nest elements inside
122
     * this element if you make it a reference.
123
     *
124
     * @param Reference $r
125
     * @throws BuildException
126
     */
127 3
    public function setRefid(Reference $r)
128
    {
129 3
        if ((isset($this->dir) && null !== $this->dir) || $this->defaultPatterns->hasPatterns()) {
130 1
            throw $this->tooManyAttributes();
131
        }
132 3
        if (!empty($this->additionalPatterns)) {
133 1
            throw $this->noChildrenAllowed();
134
        }
135 3
        if (!empty($this->selectorsList)) {
136
            throw $this->noChildrenAllowed();
137
        }
138 3
        parent::setRefid($r);
139 3
    }
140
141
    /**
142
     * @param $dir
143
     * @throws IOException
144
     * @throws NullPointerException
145
     */
146 146
    public function setDir($dir)
147
    {
148 146
        if ($this->isReference()) {
149 1
            throw $this->tooManyAttributes();
150
        }
151 145
        if ($dir instanceof File) {
152 2
            $dir = $dir->getPath();
153
        }
154 145
        $this->dir = new File((string) $dir);
155 145
        $this->directoryScanner = null;
156 145
    }
157
158
    /**
159
     * @param Project $p
160
     * @return mixed
161
     * @throws BuildException
162
     */
163 106
    public function getDir(Project $p = null)
164
    {
165 106
        if ($p === null) {
166
            $p = $this->getProject();
167
        }
168
169 106
        if ($this->isReference()) {
170 1
            return $this->getRef($p)->getDir($p);
171
        }
172
173 106
        return $this->dir;
174
    }
175
176
    /**
177
     * @return mixed
178
     * @throws BuildException
179
     */
180 2
    public function createPatternSet()
181
    {
182 2
        if ($this->isReference()) {
183 1
            throw $this->noChildrenAllowed();
184
        }
185 2
        $num = array_push($this->additionalPatterns, new PatternSet());
186
187 2
        return $this->additionalPatterns[$num - 1];
188
    }
189
190
    /**
191
     * add a name entry on the include list
192
     */
193 73
    public function createInclude()
194
    {
195 73
        if ($this->isReference()) {
196 1
            throw $this->noChildrenAllowed();
197
        }
198
199 73
        return $this->defaultPatterns->createInclude();
200
    }
201
202
    /**
203
     * add a name entry on the include files list
204
     */
205 1
    public function createIncludesFile()
206
    {
207 1
        if ($this->isReference()) {
208 1
            throw $this->noChildrenAllowed();
209
        }
210
211
        return $this->defaultPatterns->createIncludesFile();
212
    }
213
214
    /**
215
     * add a name entry on the exclude list
216
     */
217 2
    public function createExclude()
218
    {
219 2
        if ($this->isReference()) {
220 1
            throw $this->noChildrenAllowed();
221
        }
222
223 1
        return $this->defaultPatterns->createExclude();
224
    }
225
226
    /**
227
     * add a name entry on the include files list
228
     */
229 1
    public function createExcludesFile()
230
    {
231 1
        if ($this->isReference()) {
232 1
            throw $this->noChildrenAllowed();
233
        }
234
235
        return $this->defaultPatterns->createExcludesFile();
236
    }
237
238 2
    public function setFile(File $file)
239
    {
240 2
        if ($this->isReference()) {
241 1
            throw $this->tooManyAttributes();
242
        }
243 1
        $this->setDir($file->getParentFile());
244 1
        $this->createInclude()->setName($file->getName());
245 1
    }
246
247
    /**
248
     * Sets the set of include patterns. Patterns may be separated by a comma
249
     * or a space.
250
     *
251
     * @param  $includes
252
     * @throws BuildException
253
     */
254 16
    public function setIncludes($includes)
255
    {
256 16
        if ($this->isReference()) {
257 1
            throw $this->tooManyAttributes();
258
        }
259 16
        $this->defaultPatterns->setIncludes($includes);
260 16
    }
261
262
    /**
263
     * Sets the set of exclude patterns. Patterns may be separated by a comma
264
     * or a space.
265
     *
266
     * @param  $excludes
267
     * @throws BuildException
268
     */
269 2
    public function setExcludes($excludes)
270
    {
271 2
        if ($this->isReference()) {
272 1
            throw $this->tooManyAttributes();
273
        }
274 1
        $this->defaultPatterns->setExcludes($excludes);
275 1
    }
276
277
    /**
278
     * Sets the name of the file containing the includes patterns.
279
     *
280
     * @param File $incl The file to fetch the include patterns from.
281
     * @throws BuildException
282
     */
283 1
    public function setIncludesfile(File $incl)
284
    {
285 1
        if ($this->isReference()) {
286 1
            throw $this->tooManyAttributes();
287
        }
288
        $this->defaultPatterns->setIncludesFile($incl);
289
    }
290
291
    /**
292
     * Sets the name of the file containing the includes patterns.
293
     *
294
     * @param File $excl The file to fetch the exclude patterns from.
295
     * @throws BuildException
296
     */
297 1
    public function setExcludesfile($excl)
298
    {
299 1
        if ($this->isReference()) {
300 1
            throw $this->tooManyAttributes();
301
        }
302
        $this->defaultPatterns->setExcludesFile($excl);
303
    }
304
305
    /**
306
     * Sets whether default exclusions should be used or not.
307
     *
308
     * @param  $useDefaultExcludes "true"|"on"|"yes" when default exclusions
0 ignored issues
show
Documentation Bug introduced by
The doc comment "true"|"on"|"yes" at position 0 could not be parsed: Unknown type name '"true"' at position 0 in "true"|"on"|"yes".
Loading history...
309
     *                           should be used, "false"|"off"|"no" when they
310
     *                           shouldn't be used.
311
     * @return void
312
     * @throws BuildException
313
     */
314 3
    public function setDefaultexcludes($useDefaultExcludes)
315
    {
316 3
        if ($this->isReference()) {
317
            throw $this->tooManyAttributes();
318
        }
319 3
        $this->useDefaultExcludes = $useDefaultExcludes;
320 3
    }
321
322
    /**
323
     * Sets case sensitivity of the file system
324
     *
325
     * @param $isCaseSensitive
326
     */
327 1
    public function setCaseSensitive($isCaseSensitive)
328
    {
329 1
        if ($this->isReference()) {
330 1
            throw $this->tooManyAttributes();
331
        }
332
        $this->isCaseSensitive = $isCaseSensitive;
333
    }
334
335
    /**
336
     * returns a reference to the dirscanner object belonging to this fileset
337
     *
338
     * @param Project $p
339
     * @return \Phing\Io\DirectoryScanner
340
     * @throws BuildException
341
     */
342 128
    public function getDirectoryScanner(Project $p = null)
343
    {
344 128
        if ($p === null) {
345
            $p = $this->getProject();
346
        }
347
348 128
        if ($this->isReference()) {
349 1
            $o = $this->getRef($p);
350
351 1
            return $o->getDirectoryScanner($p);
352
        }
353
354 128
        if ($this->dir === null) {
355
            throw new BuildException(sprintf("No directory specified for <%s>.", $this->getDataTypeName()));
356
        }
357 128
        if (!$this->dir->exists() && $this->errorOnMissingDir) {
358
            throw new BuildException("Directory " . $this->dir->getAbsolutePath() . " not found.");
359
        }
360 128
        if (!$this->dir->isLink() || !$this->expandSymbolicLinks) {
361 128
            if (!$this->dir->isDirectory()) {
362 1
                throw new BuildException($this->dir->getAbsolutePath() . " is not a directory.");
363
            }
364
        }
365 127
        $ds = new DirectoryScanner();
366 127
        $ds->setExpandSymbolicLinks($this->expandSymbolicLinks);
367 127
        $ds->setErrorOnMissingDir($this->errorOnMissingDir);
368 127
        $this->setupDirectoryScanner($ds, $p);
369 127
        $ds->scan();
370
371 123
        return $ds;
372
    }
373
374
    /**
375
     * feed dirscanner with infos defined by this fileset
376
     *
377
     * @param DirectoryScanner $ds
378
     * @param Project $p
379
     * @throws BuildException
380
     */
381 127
    protected function setupDirectoryScanner(DirectoryScanner $ds, Project $p = null)
382
    {
383 127
        if ($p === null) {
384
            $p = $this->getProject();
385
        }
386
387 127
        if ($this->isReference()) {
388
            $this->getRef($p)->setupDirectoryScanner($ds, $p);
389
            return;
390
        }
391
392 127
        $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...
393 127
        $this->dieOnCircularReference($stk, $p);
394 127
        array_pop($stk);
395
396
        // FIXME - pass dir directly when dirscanner supports File
397 127
        $ds->setBasedir($this->dir->getPath());
398
399 127
        foreach ($this->additionalPatterns as $addPattern) {
400 1
            $this->defaultPatterns->append($addPattern, $p);
401
        }
402
403 127
        $ds->setIncludes($this->defaultPatterns->getIncludePatterns($p));
404 127
        $ds->setExcludes($this->defaultPatterns->getExcludePatterns($p));
405
406 127
        $p->log(
407 127
            $this->getDataTypeName() . ": Setup file scanner in dir " . (string) $this->dir . " with " . (string) $this->defaultPatterns,
408 127
            Project::MSG_DEBUG
409
        );
410
411 127
        if ($ds instanceof SelectorScanner) {
0 ignored issues
show
introduced by
$ds is always a sub-type of Phing\Type\Selector\SelectorScanner.
Loading history...
412 127
            $selectors = $this->getSelectors($p);
413 127
            foreach ($selectors as $selector) {
414 23
                $p->log((string) $selector . PHP_EOL, Project::MSG_DEBUG);
415
            }
416 127
            $ds->setSelectors($selectors);
417
        }
418
419 127
        if ($this->useDefaultExcludes) {
420 125
            $ds->addDefaultExcludes();
421
        }
422 127
        $ds->setCaseSensitive($this->isCaseSensitive);
423 127
    }
424
425 127
    public function dieOnCircularReference(&$stk, Project $p = null)
426
    {
427 127
        if ($this->checked) {
428 106
            return;
429
        }
430 24
        if ($this->isReference()) {
431 2
            parent::dieOnCircularReference($stk, $p);
432
        } else {
433 23
            foreach ($this->selectorsList as $fileSelector) {
434 23
                if ($fileSelector instanceof DataType) {
435 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

435
                    static::pushAndInvokeCircularReferenceCheck($fileSelector, $stk, /** @scrutinizer ignore-type */ $p);
Loading history...
436
                }
437
            }
438 23
            foreach ($this->additionalPatterns as $ps) {
439
                static::pushAndInvokeCircularReferenceCheck($ps, $stk, $p);
440
            }
441 23
            $this->setChecked(true);
442
        }
443 24
    }
444
445
    /**
446
     * Performs the check for circular references and returns the
447
     * referenced FileSet.
448
     *
449
     * @param Project $p
450
     *
451
     * @return FileSet
452
     * @throws BuildException
453
     *
454
     */
455 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

455
    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...
456
    {
457 2
        return $this->getCheckedRef(__CLASS__, $this->getDataTypeName());
458
    }
459
460
    // SelectorContainer methods
461
462
    /**
463
     * Indicates whether there are any selectors here.
464
     *
465
     * @return boolean Whether any selectors are in this container
466
     */
467
    public function hasSelectors()
468
    {
469
        if ($this->isReference() && $this->getProject() !== null) {
470
            return $this->getRef($this->getProject())->hasSelectors();
471
        }
472
        $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...
473
        $this->dieOnCircularReference($stk, $this->getProject());
474
475
        return !empty($this->selectorsList);
476
    }
477
478
    /**
479
     * Indicates whether there are any patterns here.
480
     *
481
     * @return boolean Whether any patterns are in this container.
482
     */
483
    public function hasPatterns()
484
    {
485
        if ($this->isReference() && $this->getProject() !== null) {
486
            return $this->getRef($this->getProject())->hasPatterns();
487
        }
488
        $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...
489
        $this->dieOnCircularReference($stk, $this->getProject());
490
491
        if ($this->defaultPatterns->hasPatterns()) {
492
            return true;
493
        }
494
495
        for ($i = 0, $size = count($this->additionalPatterns); $i < $size; $i++) {
496
            $ps = $this->additionalPatterns[$i];
497
            if ($ps->hasPatterns()) {
498
                return true;
499
            }
500
        }
501
502
        return false;
503
    }
504
505
    /**
506
     * Gives the count of the number of selectors in this container
507
     *
508
     * @return int The number of selectors in this container
509
     * @throws Exception
510
     */
511
    public function count()
512
    {
513
        if ($this->isReference() && $this->getProject() !== null) {
514
            try {
515
                return $this->getRef($this->getProject())->count();
516
            } catch (Exception $e) {
517
                throw $e;
518
            }
519
        }
520
521
        return count($this->selectorsList);
522
    }
523
524
    /**
525
     * Returns the set of selectors as an array.
526
     *
527
     * @param Project $p
528
     * @return array of selectors in this container
529
     * @throws BuildException
530
     */
531 127
    public function getSelectors(Project $p)
532
    {
533 127
        if ($this->isReference()) {
534
            return $this->getRef($p)->getSelectors($p);
535
        }
536
537
// *copy* selectors
538 127
        $result = [];
539 127
        for ($i = 0, $size = count($this->selectorsList); $i < $size; $i++) {
540 23
            $result[] = clone $this->selectorsList[$i];
541
        }
542
543 127
        return $result;
544
    }
545
546
    /**
547
     * Returns an array for accessing the set of selectors.
548
     *
549
     * @return array The array of selectors
550
     */
551
    public function selectorElements()
552
    {
553
        if ($this->isReference() && $this->getProject() !== null) {
554
            return $this->getRef($this->getProject())->selectorElements();
555
        }
556
557
        return $this->selectorsList;
558
    }
559
560
    /**
561
     * Add a new selector into this container.
562
     *
563
     * @param FileSelector $selector new selector to add
564
     *
565
     * @return void
566
     * @throws BuildException
567
     *
568
     */
569 27
    public function appendSelector(FileSelector $selector)
570
    {
571 27
        if ($this->isReference()) {
572
            throw $this->noChildrenAllowed();
573
        }
574 27
        $this->selectorsList[] = $selector;
575 27
        $this->directoryScanner = null;
576 27
        $this->setChecked(false);
577 27
    }
578
579
    /**
580
     * @param array ...$options
581
     * @return ArrayIterator
582
     */
583 21
    public function getIterator(...$options): \ArrayIterator
584
    {
585 21
        if ($this->isReference()) {
586 1
            return $this->getRef($this->getProject())->getIterator($options);
587
        }
588 21
        return new ArrayIterator($this->getFiles($options));
589
    }
590
591
    abstract protected function getFiles(...$options);
592
593 1
    public function __toString()
594
    {
595
        try {
596 1
            if ($this->isReference()) {
597
                return (string) $this->getRef($this->getProject());
598
            }
599 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...
600 1
            $this->dieOnCircularReference($stk, $this->getProject());
601 1
            $ds = $this->getDirectoryScanner($this->getProject());
602 1
            $files = $ds->getIncludedFiles();
603 1
            $result = implode(';', $files);
604
        } catch (BuildException $e) {
605
            $result = '';
606
        }
607
608 1
        return $result;
609
    }
610
}
611