Passed
Push — master ( ef6ec0...e80653 )
by Michiel
08:36
created

Path::listPaths()   D

Complexity

Conditions 19
Paths 74

Size

Total Lines 74
Code Lines 54

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 69.3214

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 19
eloc 54
nc 74
nop 1
dl 0
loc 74
ccs 26
cts 54
cp 0.4815
crap 69.3214
rs 4.5166
c 1
b 0
f 0

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
 * This object represents a path as used by include_path or PATH
22
 * environment variable.
23
 *
24
 * This class has been adopted from the Java Ant equivalent.  The ability have
25
 * path structures in Phing is important; however, because of how PHP classes interact
26
 * the ability to specify CLASSPATHs makes less sense than Java.Rather than providing
27
 * CLASSPATH for any tasks that take classes as parameters, perhaps a better
28
 * solution in PHP is to have an IncludePath task, which prepends paths to PHP's include_path
29
 * INI variable. This gets around the problem that simply using a path to load the initial
30
 * PHP class is not enough (in most cases the loaded class may assume that it is on the global
31
 * PHP include_path, and will try to load dependent classes accordingly).  The other option is
32
 * to provide a way for this class to add paths to the include path, if desired -- or to create
33
 * an IncludePath subclass.  Once added, though, when would a path be removed from the include path?
34
 *
35
 * <p>
36
 * <code>
37
 * &lt;sometask&gt;<br>
38
 * &nbsp;&nbsp;&lt;somepath&gt;<br>
39
 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="/path/to/file" /&gt;<br>
40
 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement path="/path/to/class2;/path/to/class3" /&gt;<br>
41
 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="/path/to/file3" /&gt;<br>
42
 * &nbsp;&nbsp;&lt;/somepath&gt;<br>
43
 * &lt;/sometask&gt;<br>
44
 * </code>
45
 * <p>
46
 * The object implemention <code>sometask</code> must provide a method called
47
 * <code>createSomepath</code> which returns an instance of <code>Path</code>.
48
 * Nested path definitions are handled by the Path object and must be labeled
49
 * <code>pathelement</code>.<p>
50
 *
51
 * The path element takes a parameter <code>path</code> which will be parsed
52
 * and split into single elements. It will usually be used
53
 * to define a path from an environment variable.
54
 *
55
 * @author  Hans Lellelid <[email protected]> (Phing)
56
 * @author  [email protected] (Ant)
57
 * @author  Stefan Bodewig <[email protected]> (Ant)
58
 * @package phing.types
59
 */
60
class Path extends DataType
61
{
62
    private $elements = [];
63
64
    /**
65
     * Constructor for internally instantiated objects sets project.
66
     *
67
     * @param Project $project
68
     * @param string $path (for use by IntrospectionHelper)
69
     */
70 41
    public function __construct($project = null, $path = null)
71
    {
72 41
        parent::__construct();
73 41
        if ($project !== null) {
74 29
            $this->setProject($project);
75
        }
76 41
        if ($path !== null) {
77 16
            $this->createPathElement()->setPath($path);
78
        }
79 41
    }
80
81
    /**
82
     * Adds a element definition to the path.
83
     *
84
     * @param PhingFile $location the location of the element to add (must not be
85
     *                            <code>null</code> nor empty.
86
     *
87
     * @return void
88
     *
89
     * @throws BuildException
90
     */
91 2
    public function setDir(PhingFile $location)
92
    {
93 2
        if ($this->isReference()) {
94
            throw $this->tooManyAttributes();
95
        }
96 2
        $this->createPathElement()->setDir($location);
97 2
    }
98
99
    /**
100
     * Parses a path definition and creates single PathElements.
101
     *
102
     * @param $path the path definition.
103
     *
104
     * @throws BuildException
105
     */
106 10
    public function setPath($path)
107
    {
108 10
        if ($this->isReference()) {
109
            throw $this->tooManyAttributes();
110
        }
111 10
        $this->createPathElement()->setPath($path);
112 10
    }
113
114
    /**
115
     * Makes this instance in effect a reference to another Path instance.
116
     *
117
     * <p>You must not set another attribute or nest elements inside
118
     * this element if you make it a reference.</p>
119
     *
120
     * @param Reference $r
121
     *
122
     * @return void
123
     *
124
     * @throws BuildException
125
     */
126 14
    public function setRefid(Reference $r)
127
    {
128 14
        if (!empty($this->elements)) {
129
            throw $this->tooManyAttributes();
130
        }
131 14
        $this->elements[] = $r;
132 14
        parent::setRefid($r);
133 14
    }
134
135
    /**
136
     * Creates the nested <code>&lt;pathelement&gt;</code> element.
137
     *
138
     * @return PathElement
139
     *
140
     * @throws BuildException
141
     */
142 41
    public function createPathElement()
143
    {
144 41
        if ($this->isReference()) {
145
            throw $this->noChildrenAllowed();
146
        }
147 41
        $pe = new PathElement($this);
148 41
        $this->elements[] = $pe;
149
150 41
        return $pe;
151
    }
152
153
    /**
154
     * Adds a nested <code>&lt;filelist&gt;</code> element.
155
     *
156
     * @param FileList $fl
157
     *
158
     * @return void
159
     *
160
     * @throws BuildException
161
     */
162
    public function addFilelist(FileList $fl)
163
    {
164
        if ($this->isReference()) {
165
            throw $this->noChildrenAllowed();
166
        }
167
        $this->elements[] = $fl;
168
        $this->checked = false;
169
    }
170
171
    /**
172
     * Adds a nested <code>&lt;fileset&gt;</code> element.
173
     *
174
     * @param FileSet $fs
175
     *
176
     * @return void
177
     *
178
     * @throws BuildException
179
     */
180
    public function addFileset(FileSet $fs)
181
    {
182
        if ($this->isReference()) {
183
            throw $this->noChildrenAllowed();
184
        }
185
        $this->elements[] = $fs;
186
        $this->checked = false;
187
    }
188
189
    /**
190
     * Adds a nested <code>&lt;dirset&gt;</code> element.
191
     *
192
     * @param DirSet $dset
193
     *
194
     * @return void
195
     *
196
     * @throws BuildException
197
     */
198
    public function addDirset(DirSet $dset)
199
    {
200
        if ($this->isReference()) {
201
            throw $this->noChildrenAllowed();
202
        }
203
        $this->elements[] = $dset;
204
        $this->checked = false;
205
    }
206
207
    /**
208
     * Creates a nested <code>&lt;path&gt;</code> element.
209
     *
210
     * @return Path
211
     *
212
     * @throws BuildException
213
     */
214 16
    public function createPath()
215
    {
216 16
        if ($this->isReference()) {
217
            throw $this->noChildrenAllowed();
218
        }
219 16
        $p = new Path($this->project);
220 16
        $this->elements[] = $p;
221 16
        $this->checked = false;
222
223 16
        return $p;
224
    }
225
226
    /**
227
     * Append the contents of the other Path instance to this.
228
     *
229
     * @param Path $other
230
     *
231
     * @return void
232
     *
233
     * @throws BuildException
234
     */
235 1
    public function append(Path $other)
236
    {
237 1
        if ($other === null) {
238
            return;
239
        }
240 1
        $l = $other->listPaths();
241 1
        foreach ($l as $path) {
242 1
            if (!in_array($path, $this->elements, true)) {
243 1
                $this->elements[] = $path;
244
            }
245
        }
246 1
    }
247
248
    /**
249
     * Adds the components on the given path which exist to this
250
     * Path. Components that don't exist, aren't added.
251
     *
252
     * @param Path $source - Source path whose components are examined for existence.
253
     *
254
     * @return void
255
     */
256
    public function addExisting(Path $source)
257
    {
258
        $list = $source->listPaths();
259
        foreach ($list as $el) {
260
            $f = null;
261
            if ($this->project !== null) {
262
                $f = $this->project->resolveFile($el);
263
            } else {
264
                $f = new PhingFile($el);
265
            }
266
267
            if ($f->exists()) {
268
                $this->setDir($f);
269
            } else {
270
                $this->log(
271
                    "dropping " . $f->__toString() . " from path as it doesn't exist",
272
                    Project::MSG_VERBOSE
273
                );
274
            }
275
        }
276
    }
277
278
    /**
279
     * Returns all path elements defined by this and nested path objects.
280
     *
281
     * @param  bool $preserveDuplicates
282
     * @return array List of path elements.
283
     * @throws IOException
284
     * @throws NullPointerException
285
     */
286 29
    public function listPaths($preserveDuplicates = false)
287
    {
288 29
        if (!$this->checked) {
289
            // make sure we don't have a circular reference here
290 4
            $stk = [];
291 4
            $stk[] = $this;
292 4
            $this->dieOnCircularReference($stk, $this->project);
293
        }
294
295 29
        $result = [];
296 29
        for ($i = 0, $elSize = count($this->elements); $i < $elSize; $i++) {
297 29
            $o = $this->elements[$i];
298 29
            if ($o instanceof Reference) {
299 2
                $refId = $o->getRefId();
300 2
                $o = $o->getReferencedObject($this->project);
301
                // we only support references to paths right now
302 2
                if (!($o instanceof Path)) {
303
                    $msg = $refId . " doesn't denote a path";
304
                    throw new BuildException($msg);
305
                }
306
            }
307
308 29
            if (is_string($o)) {
309 1
                $result[] = $o;
310 29
            } elseif ($o instanceof PathElement) {
311 29
                $parts = $o->getParts();
312 29
                if ($parts === null) {
313
                    throw new BuildException(
314
                        "You must either set location or"
315
                        . " path on <pathelement>"
316
                    );
317
                }
318 29
                foreach ($parts as $part) {
319 29
                    $result[] = $part;
320
                }
321 4
            } elseif ($o instanceof Path) {
322 4
                $p = $o;
323 4
                if ($p->getProject() === null) {
324
                    $p->setProject($this->getProject());
325
                }
326 4
                $parts = $p->listPaths();
327 4
                foreach ($parts as $part) {
328 4
                    $result[] = $part;
329
                }
330
            } elseif ($o instanceof DirSet) {
331
                $dset = $o;
332
                $ds = $dset->getDirectoryScanner($this->project);
333
                $dirstrs = $ds->getIncludedDirectories();
334
                $dir = $dset->getDir($this->project);
335
                foreach ($dirstrs as $dstr) {
336
                    $d = new PhingFile($dir, $dstr);
337
                    $result[] = $d->getAbsolutePath();
338
                }
339
            } elseif ($o instanceof FileSet) {
340
                $fs = $o;
341
                $ds = $fs->getDirectoryScanner($this->getProject());
342
                $filestrs = $ds->getIncludedFiles();
343
                $dir = $fs->getDir($this->getProject());
344
                foreach ($filestrs as $fstr) {
345
                    $d = new PhingFile($dir, $fstr);
346
                    $result[] = $d->getAbsolutePath();
347
                }
348
            } elseif ($o instanceof FileList) {
349
                $fl = $o;
350
                $dirstrs = $fl->getFiles($this->project);
351
                $dir = $fl->getDir($this->project);
352
                foreach ($dirstrs as $dstr) {
353
                    $d = new PhingFile($dir, $dstr);
354
                    $result[] = $d->getAbsolutePath();
355
                }
356
            }
357
        }
358
359 29
        return $preserveDuplicates ? $result : array_unique($result);
360
    }
361
362
363
    /**
364
     * Returns a textual representation of the path, which can be used as
365
     * CLASSPATH or PATH environment variable definition.
366
     *
367
     * @return string A textual representation of the path.
368
     */
369 15
    public function __toString()
370
    {
371 15
        $list = $this->listPaths();
372
373
        // empty path return empty string
374 15
        if (empty($list)) {
375
            return "";
376
        }
377
378 15
        return implode(PATH_SEPARATOR, $list);
379
    }
380
381
    /**
382
     * Splits a PATH (with : or ; as separators) into its parts.
383
     *
384
     * @param Project $project
385
     * @param string $source
386
     *
387
     * @return array
388
     */
389 28
    public static function translatePath(Project $project, $source)
390
    {
391 28
        $result = [];
392 28
        if ($source == null) {
393
            return "";
0 ignored issues
show
Bug Best Practice introduced by
The expression return '' returns the type string which is incompatible with the documented return type array.
Loading history...
394
        }
395
396 28
        $tok = new PathTokenizer($source);
397 28
        while ($tok->hasMoreTokens()) {
398 28
            $pathElement = $tok->nextToken();
399
            try {
400 28
                $element = self::resolveFile($project, $pathElement);
401 28
                for ($i = 0, $_i = strlen($element); $i < $_i; $i++) {
402 28
                    self::translateFileSep($element, $i);
403
                }
404 28
                $result[] = $element;
405
            } catch (BuildException $e) {
406
                $project->log(
407
                    "Dropping path element " . $pathElement
408
                    . " as it is not valid relative to the project",
409
                    Project::MSG_VERBOSE
410
                );
411
            }
412
        }
413
414 28
        return $result;
415
    }
416
417
    /**
418
     * Returns its argument with all file separator characters
419
     * replaced so that they match the local OS conventions.
420
     *
421
     * @param string $source
422
     *
423
     * @return string
424
     */
425 14
    public static function translateFile($source)
426
    {
427 14
        if ($source == null) {
428
            return "";
429
        }
430
431 14
        $result = $source;
432 14
        for ($i = 0, $_i = strlen($source); $i < $_i; $i++) {
433 14
            self::translateFileSep($result, $i);
434
        }
435
436 14
        return $result;
437
    }
438
439
    /**
440
     * Translates all occurrences of / or \ to correct separator of the
441
     * current platform and returns whether it had to do any
442
     * replacements.
443
     *
444
     * @param string $buffer
445
     * @param int $pos
446
     *
447
     * @return bool
448
     */
449 41
    protected static function translateFileSep(&$buffer, $pos)
450
    {
451 41
        if ($buffer[$pos] == '/' || $buffer[$pos] == '\\') {
452 41
            $buffer[$pos] = DIRECTORY_SEPARATOR;
453
454 41
            return true;
455
        }
456
457 41
        return false;
458
    }
459
460
    /**
461
     * How many parts does this Path instance consist of.
462
     * DEV NOTE: expensive call! list is generated, counted, and then
463
     * discareded.
464
     *
465
     * @return int
466
     */
467
    public function size()
468
    {
469
        return count($this->listPaths());
470
    }
471
472
    /**
473
     * Overrides the version of DataType to recurse on all DataType
474
     * child elements that may have been added.
475
     *
476
     * @param $stk
477
     * @param Project $p
478
     *
479
     * @return void
480
     *
481
     * @throws BuildException
482
     */
483 4
    public function dieOnCircularReference(&$stk, Project $p = null)
484
    {
485 4
        if ($this->checked) {
486 4
            return;
487
        }
488
489
        // elements can contain strings, FileSets, Reference, etc.
490 4
        foreach ($this->elements as $o) {
491 4
            if ($o instanceof Reference) {
492 2
                $o = $o->getReferencedObject($p);
493
            }
494
495 4
            if ($o instanceof DataType) {
496 4
                if (in_array($o, $stk, true)) {
497
                    throw $this->circularReference();
498
                }
499
500 4
                $stk[] = $o;
501 4
                $o->dieOnCircularReference($stk, $p);
502 4
                array_pop($stk);
503
            }
504
        }
505
506 4
        $this->checked = true;
507 4
    }
508
509
    /**
510
     * Resolve a filename with Project's help - if we know one that is.
511
     *
512
     * <p>Assume the filename is absolute if project is null.</p>
513
     *
514
     * @param Project $project
515
     * @param $relativeName
516
     *
517
     * @return string
518
     */
519 28
    private static function resolveFile(Project $project, $relativeName)
520
    {
521 28
        if ($project !== null) {
522 28
            $f = $project->resolveFile($relativeName);
523
524 28
            return $f->getAbsolutePath();
525
        }
526
527
        return $relativeName;
528
    }
529
}
530