Completed
Push — master ( 0f28ca...ccc575 )
by Siad
17:36 queued 13s
created

classes/phing/types/AbstractFileSet.php (1 issue)

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
 * The FileSet class provides methods and properties for accessing
22
 * and managing filesets. It extends ProjectComponent and thus inherits
23
 * all methods and properties (not explicitly declared). See ProjectComponent
24
 * for further detail.
25
 *
26
 * TODO:
27
 *   - merge this with patternsets: FileSet extends PatternSet !!!
28
 *     requires additional mods to the parsing algo
29
 *         [HL] .... not sure if that really makes so much sense.  I think
30
 *            that perhaps they should use common utility class if there really
31
 *            is that much shared functionality
32
 *
33
 * @author  Andreas Aderhold <[email protected]>
34
 * @author  Hans Lellelid <[email protected]>
35
 * @see     ProjectComponent
36
 * @package phing.types
37
 */
38
abstract class AbstractFileSet extends DataType implements SelectorContainer, IteratorAggregate
39
{
40
    use SelectorAware;
41
42
    // These vars are public for cloning purposes
43
44
    /**
45
     * @var boolean
46
     */
47
    public $useDefaultExcludes = true;
48
49
    /**
50
     * Whether to expand/dereference symbolic links, default is false
51
     *
52
     * @var boolean
53
     */
54
    protected $expandSymbolicLinks = false;
55
56
    /**
57
     * @var PatternSet
58
     */
59
    public $defaultPatterns;
60
61
    public $additionalPatterns = [];
62
    public $dir;
63
    public $isCaseSensitive = true;
64
    private $errorOnMissingDir = false;
65
    private $directoryScanner;
66
67
    /**
68
     * @param null $fileset
69
     */
70 119
    public function __construct($fileset = null)
71
    {
72 119
        parent::__construct();
73
74 119
        if ($fileset !== null && ($fileset instanceof FileSet)) {
75
            $this->dir = $fileset->dir;
76
            $this->additionalPatterns = $fileset->additionalPatterns;
77
            $this->useDefaultExcludes = $fileset->useDefaultExcludes;
78
            $this->isCaseSensitive = $fileset->isCaseSensitive;
79
            $this->selectorsList = $fileset->selectorsList;
80
            $this->expandSymbolicLinks = $fileset->expandSymbolicLinks;
81
            $this->errorOnMissingDir = $fileset->errorOnMissingDir;
82
            $this->setProject($fileset->getProject());
83
        }
84
85 119
        $this->defaultPatterns = new PatternSet();
86 119
    }
87
88
    /**
89
     * Sets whether to expand/dereference symbolic links, default is false
90
     *
91
     * @var boolean
92
     */
93 1
    public function setExpandSymbolicLinks(bool $expandSymbolicLinks)
94
    {
95 1
        if ($this->isReference()) {
96 1
            throw $this->tooManyAttributes();
97
        }
98
        $this->expandSymbolicLinks = $expandSymbolicLinks;
99
    }
100
101
    /**
102
     * Makes this instance in effect a reference to another PatternSet
103
     * instance.
104
     * You must not set another attribute or nest elements inside
105
     * this element if you make it a reference.
106
     *
107
     * @param  Reference $r
108
     * @throws BuildException
109
     */
110 2
    public function setRefid(Reference $r)
111
    {
112 2
        if ((isset($this->dir) && null !== $this->dir) || $this->defaultPatterns->hasPatterns()) {
113 1
            throw $this->tooManyAttributes();
114
        }
115 2
        if (!empty($this->additionalPatterns)) {
116 1
            throw $this->noChildrenAllowed();
117
        }
118 2
        if (!empty($this->selectorsList)) {
119
            throw $this->noChildrenAllowed();
120
        }
121 2
        parent::setRefid($r);
122 2
    }
123
124
    /**
125
     * @param $dir
126
     * @throws IOException
127
     * @throws NullPointerException
128
     */
129 116
    public function setDir($dir)
130
    {
131 116
        if ($this->isReference()) {
132 1
            throw $this->tooManyAttributes();
133
        }
134 115
        if ($dir instanceof PhingFile) {
135
            $dir = $dir->getPath();
136
        }
137 115
        $this->dir = new PhingFile((string) $dir);
138 115
        $this->directoryScanner = null;
139 115
    }
140
141
    /**
142
     * @param Project $p
143
     * @return mixed
144
     * @throws BuildException
145
     */
146 91
    public function getDir(Project $p = null)
147
    {
148 91
        if ($p === null) {
149
            $p = $this->getProject();
150
        }
151
152 91
        if ($this->isReference()) {
153 1
            return $this->getRef($p)->getDir($p);
154
        }
155
156 91
        return $this->dir;
157
    }
158
159
    /**
160
     * @return mixed
161
     * @throws BuildException
162
     */
163 2
    public function createPatternSet()
164
    {
165 2
        if ($this->isReference()) {
166 1
            throw $this->noChildrenAllowed();
167
        }
168 2
        $num = array_push($this->additionalPatterns, new PatternSet());
169
170 2
        return $this->additionalPatterns[$num - 1];
171
    }
172
173
    /**
174
     * add a name entry on the include list
175
     */
176 79
    public function createInclude()
177
    {
178 79
        if ($this->isReference()) {
179 1
            throw $this->noChildrenAllowed();
180
        }
181
182 79
        return $this->defaultPatterns->createInclude();
183
    }
184
185
    /**
186
     * add a name entry on the include files list
187
     */
188 1
    public function createIncludesFile()
189
    {
190 1
        if ($this->isReference()) {
191 1
            throw $this->noChildrenAllowed();
192
        }
193
194
        return $this->defaultPatterns->createIncludesFile();
195
    }
196
197
    /**
198
     * add a name entry on the exclude list
199
     */
200 2
    public function createExclude()
201
    {
202 2
        if ($this->isReference()) {
203 1
            throw $this->noChildrenAllowed();
204
        }
205
206 1
        return $this->defaultPatterns->createExclude();
207
    }
208
209
    /**
210
     * add a name entry on the include files list
211
     */
212 1
    public function createExcludesFile()
213
    {
214 1
        if ($this->isReference()) {
215 1
            throw $this->noChildrenAllowed();
216
        }
217
218
        return $this->defaultPatterns->createExcludesFile();
219
    }
220
221 1
    public function setFile(PhingFile $file)
222
    {
223 1
        if ($this->isReference()) {
224 1
            throw $this->tooManyAttributes();
225
        }
226
        $this->setDir($file->getParentFile());
227
        $this->createInclude()->setName($file->getName());
228
    }
229
230
    /**
231
     * Sets the set of include patterns. Patterns may be separated by a comma
232
     * or a space.
233
     *
234
     * @param  $includes
235
     * @throws BuildException
236
     */
237 5
    public function setIncludes($includes)
238
    {
239 5
        if ($this->isReference()) {
240 1
            throw $this->tooManyAttributes();
241
        }
242 5
        $this->defaultPatterns->setIncludes($includes);
243 5
    }
244
245
    /**
246
     * Sets the set of exclude patterns. Patterns may be separated by a comma
247
     * or a space.
248
     *
249
     * @param  $excludes
250
     * @throws BuildException
251
     */
252 1
    public function setExcludes($excludes)
253
    {
254 1
        if ($this->isReference()) {
255 1
            throw $this->tooManyAttributes();
256
        }
257
        $this->defaultPatterns->setExcludes($excludes);
258
    }
259
260
    /**
261
     * Sets the name of the file containing the includes patterns.
262
     *
263
     * @param  PhingFile $incl The file to fetch the include patterns from.
264
     * @throws BuildException
265
     */
266 1
    public function setIncludesfile(PhingFile $incl)
267
    {
268 1
        if ($this->isReference()) {
269 1
            throw $this->tooManyAttributes();
270
        }
271
        $this->defaultPatterns->setIncludesFile($incl);
272
    }
273
274
    /**
275
     * Sets the name of the file containing the includes patterns.
276
     *
277
     * @param  PhingFile $excl The file to fetch the exclude patterns from.
278
     * @throws BuildException
279
     */
280 1
    public function setExcludesfile($excl)
281
    {
282 1
        if ($this->isReference()) {
283 1
            throw $this->tooManyAttributes();
284
        }
285
        $this->defaultPatterns->setExcludesFile($excl);
286
    }
287
288
    /**
289
     * Sets whether default exclusions should be used or not.
290
     *
291
     * @param  $useDefaultExcludes "true"|"on"|"yes" when default exclusions
292
     *                           should be used, "false"|"off"|"no" when they
293
     *                           shouldn't be used.
294
     * @throws BuildException
295
     * @return void
296
     */
297 3
    public function setDefaultexcludes($useDefaultExcludes)
298
    {
299 3
        if ($this->isReference()) {
300
            throw $this->tooManyAttributes();
301
        }
302 3
        $this->useDefaultExcludes = $useDefaultExcludes;
303 3
    }
304
305
    /**
306
     * Sets case sensitivity of the file system
307
     *
308
     * @param $isCaseSensitive
309
     */
310 1
    public function setCaseSensitive($isCaseSensitive)
311
    {
312 1
        if ($this->isReference()) {
313 1
            throw $this->tooManyAttributes();
314
        }
315
        $this->isCaseSensitive = $isCaseSensitive;
316
    }
317
318
    /**
319
     * returns a reference to the dirscanner object belonging to this fileset
320
     *
321
     * @param  Project $p
322
     * @throws BuildException
323
     * @return \DirectoryScanner
324
     */
325 100
    public function getDirectoryScanner(Project $p = null)
326
    {
327 100
        if ($p === null) {
328
            $p = $this->getProject();
329
        }
330
331 100
        if ($this->isReference()) {
332 1
            $o = $this->getRef($p);
333
334 1
            return $o->getDirectoryScanner($p);
335
        }
336
337 100
        if ($this->dir === null) {
338
            throw new BuildException(sprintf("No directory specified for <%s>.", $this->getDataTypeName()));
339
        }
340 100
        if (!$this->dir->exists() && $this->errorOnMissingDir) {
341
            throw new BuildException("Directory " . $this->dir->getAbsolutePath() . " not found.");
342
        }
343 100
        if (!$this->dir->isLink() || !$this->expandSymbolicLinks) {
344 100
            if (!$this->dir->isDirectory()) {
345 1
                throw new BuildException($this->dir->getAbsolutePath() . " is not a directory.");
346
            }
347
        }
348 99
        $ds = new DirectoryScanner();
349 99
        $ds->setExpandSymbolicLinks($this->expandSymbolicLinks);
350 99
        $ds->setErrorOnMissingDir($this->errorOnMissingDir);
351 99
        $this->setupDirectoryScanner($ds, $p);
352 99
        $ds->scan();
353
354 99
        return $ds;
355
    }
356
357
    /**
358
     * feed dirscanner with infos defined by this fileset
359
     *
360
     * @param  DirectoryScanner $ds
361
     * @param  Project $p
362
     * @throws BuildException
363
     */
364 99
    protected function setupDirectoryScanner(DirectoryScanner $ds, Project $p = null)
365
    {
366 99
        if ($p === null) {
367
            $p = $this->getProject();
368
        }
369
370 99
        if ($this->isReference()) {
371
            $this->getRef($p)->setupDirectoryScanner($ds, $p);
372
            return;
373
        }
374
375 99
        $stk[] = $this;
376 99
        $this->dieOnCircularReference($stk, $p);
377 99
        array_pop($stk);
378
379
        // FIXME - pass dir directly when dirscanner supports File
380 99
        $ds->setBasedir($this->dir->getPath());
381
382 99
        foreach ($this->additionalPatterns as $addPattern) {
383 1
            $this->defaultPatterns->append($addPattern, $p);
384
        }
385
386 99
        $ds->setIncludes($this->defaultPatterns->getIncludePatterns($p));
387 99
        $ds->setExcludes($this->defaultPatterns->getExcludePatterns($p));
388
389 99
        $p->log(
390 99
            $this->getDataTypeName() . ": Setup file scanner in dir " . (string) $this->dir . " with " . (string) $this->defaultPatterns,
391 99
            Project::MSG_DEBUG
392
        );
393
394 99
        if ($ds instanceof SelectorScanner) {
395 99
            $selectors = $this->getSelectors($p);
396 99
            foreach ($selectors as $selector) {
397 4
                $p->log((string) $selector . PHP_EOL, Project::MSG_DEBUG);
398
            }
399 99
            $ds->setSelectors($selectors);
400
        }
401
402 99
        if ($this->useDefaultExcludes) {
403 97
            $ds->addDefaultExcludes();
404
        }
405 99
        $ds->setCaseSensitive($this->isCaseSensitive);
406 99
    }
407
408 99
    public function dieOnCircularReference(&$stk, Project $p = null)
409
    {
410 99
        if ($this->checked) {
411 96
            return;
412
        }
413 4
        if ($this->isReference()) {
414 1
            parent::dieOnCircularReference($stk, $p);
415
        } else {
416 4
            foreach ($this->selectorsList as $fileSelector) {
417 4
                if ($fileSelector instanceof DataType) {
418 4
                    static::pushAndInvokeCircularReferenceCheck($fileSelector, $stk, $p);
419
                }
420
            }
421 4
            foreach ($this->additionalPatterns as $ps) {
422
                static::pushAndInvokeCircularReferenceCheck($ps, $stk, $p);
423
            }
424 4
            $this->setChecked(true);
425
        }
426 4
    }
427
428
    /**
429
     * Performs the check for circular references and returns the
430
     * referenced FileSet.
431
     *
432
     * @param Project $p
433
     *
434
     * @throws BuildException
435
     *
436
     * @return FileSet
437
     */
438 1
    public function getRef(Project $p)
439
    {
440 1
        return $this->getCheckedRef(__CLASS__, $this->getDataTypeName());
441
    }
442
443
    // SelectorContainer methods
444
445
    /**
446
     * Indicates whether there are any selectors here.
447
     *
448
     * @return boolean Whether any selectors are in this container
449
     */
450
    public function hasSelectors()
451
    {
452
        if ($this->isReference() && $this->getProject() !== null) {
453
            return $this->getRef($this->getProject())->hasSelectors();
454
        }
455
        $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...
456
        $this->dieOnCircularReference($stk, $this->getProject());
457
458
        return !empty($this->selectorsList);
459
    }
460
461
    /**
462
     * Indicates whether there are any patterns here.
463
     *
464
     * @return boolean Whether any patterns are in this container.
465
     */
466
    public function hasPatterns()
467
    {
468
        if ($this->isReference() && $this->getProject() !== null) {
469
            return $this->getRef($this->getProject())->hasPatterns();
470
        }
471
        $stk[] = $this;
472
        $this->dieOnCircularReference($stk, $this->getProject());
473
474
        if ($this->defaultPatterns->hasPatterns()) {
475
            return true;
476
        }
477
478
        for ($i = 0, $size = count($this->additionalPatterns); $i < $size; $i++) {
479
            $ps = $this->additionalPatterns[$i];
480
            if ($ps->hasPatterns()) {
481
                return true;
482
            }
483
        }
484
485
        return false;
486
    }
487
488
    /**
489
     * Gives the count of the number of selectors in this container
490
     *
491
     * @throws Exception
492
     * @return int The number of selectors in this container
493
     */
494
    public function count()
495
    {
496
        if ($this->isReference() && $this->getProject() !== null) {
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
     * @param  Project $p
511
     * @throws BuildException
512
     * @return array of selectors in this container
513
     */
514 99
    public function getSelectors(Project $p)
515
    {
516 99
        if ($this->isReference()) {
517
            return $this->getRef($p)->getSelectors($p);
518
        }
519
520
// *copy* selectors
521 99
        $result = [];
522 99
        for ($i = 0, $size = count($this->selectorsList); $i < $size; $i++) {
523 4
            $result[] = clone $this->selectorsList[$i];
524
        }
525
526 99
        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() && $this->getProject() !== null) {
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
     * @return void
551
     */
552 7
    public function appendSelector(FileSelector $selector)
553
    {
554 7
        if ($this->isReference()) {
555
            throw $this->noChildrenAllowed();
556
        }
557 7
        $this->selectorsList[] = $selector;
558 7
        $this->directoryScanner = null;
559 7
        $this->setChecked(false);
560 7
    }
561
562
    /**
563
     * @param array ...$options
564
     * @return ArrayIterator
565
     */
566 12
    public function getIterator(...$options): \ArrayIterator
567
    {
568 12
        if ($this->isReference()) {
569
            return $this->getRef($this->getProject())->getIterator($options);
570
        }
571 12
        return new ArrayIterator($this->getFiles($options));
572
    }
573
574
    abstract protected function getFiles(...$options);
575
576 1
    public function __toString()
577
    {
578
        try {
579 1
            if ($this->isReference()) {
580
                return (string) $this->getRef($this->getProject());
581
            }
582 1
            $stk[] = $this;
583 1
            $this->dieOnCircularReference($stk, $this->getProject());
584 1
            $ds = $this->getDirectoryScanner($this->getProject());
585 1
            $files = $ds->getIncludedFiles();
586 1
            $result = implode(';', $files);
587
        } catch (BuildException $e) {
588
            $result = '';
589
        }
590
591 1
        return $result;
592
    }
593
}
594