SubPhing::main()   F
last analyzed

Complexity

Conditions 27
Paths 1647

Size

Total Lines 91
Code Lines 63

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 448.8243

Importance

Changes 0
Metric Value
eloc 63
dl 0
loc 91
ccs 10
cts 60
cp 0.1666
rs 0
c 0
b 0
f 0
cc 27
nc 1647
nop 0
crap 448.8243

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
/**
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\Task\System;
22
23
use Phing\Exception\BuildException;
24
use Phing\Io\File;
25
use Phing\Io\IOException;
26
use Phing\Project;
27
use Phing\Target;
28
use Phing\Task;
29
use Phing\Type\Element\FileSetAware;
30
use Phing\Type\Path;
31
use Phing\Type\PathElement;
32
use Phing\Type\Reference;
33
use RuntimeException;
34
use Throwable;
35
36
class SubPhing extends Task
37
{
38
    use FileSetAware;
39
40
    /** @var Path */
41
    private $buildpath;
42
43
    private $phing;
44
    private $subTarget;
45
    private $phingfile = 'build.xml';
46
    private $genericphingfile;
47
    private $verbose = false;
48
    private $inheritAll = false;
49
    private $inheritRefs = false;
50
    private $failOnError = true;
51
    private $output;
52
53
    private $properties = [];
54
    private $references = [];
55
56
    /**
57
     * Runs the various sub-builds.
58
     */
59 3
    public function main()
60
    {
61 3
        $filenames = [];
62 3
        if (null !== $this->buildpath) {
63
            $filenames = $this->buildpath->listPaths();
64 3
        } elseif (count($this->filesets) > 0) {
65 3
            foreach ($this->filesets as $fileset) {
66 3
                foreach ($fileset as $filename) {
67
                    $filenames[] = $filename;
68
                }
69
            }
70
        } else {
71
            throw new BuildException('No buildpath specified');
72
        }
73 3
        $count = count($filenames);
74 3
        if ($count < 1) {
75 3
            $this->log('No sub-builds to iterate on', Project::MSG_WARN);
76
77 3
            return;
78
        }
79
80
        $buildException = null;
81
        foreach ($filenames as $filename) {
82
            $file = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $file is dead and can be removed.
Loading history...
83
            $subdirPath = null;
84
            $thrownException = null;
85
86
            try {
87
                $directory = null;
88
                $file = new File($filename);
89
                if ($file->isDirectory()) {
90
                    if ($this->verbose) {
91
                        $subdirPath = $file->getPath();
92
                        $this->log('Entering directory: ' . $subdirPath . "\n");
93
                    }
94
                    if (null !== $this->genericphingfile) {
95
                        $directory = $file;
96
                        $file = $this->genericphingfile;
97
                    } else {
98
                        $file = new File($file, $this->phingfile);
99
                    }
100
                }
101
                $this->execute($file, $directory);
102
                if ($this->verbose && null !== $subdirPath) {
103
                    $this->log('Leaving directory: ' . $subdirPath . "\n");
104
                }
105
            } catch (RuntimeException $ex) {
106
                if (!$this->getProject()->isKeepGoingMode()) {
107
                    if ($this->verbose && null !== $subdirPath) {
108
                        $this->log('Leaving directory: ' . $subdirPath . "\n");
109
                    }
110
111
                    throw $ex; // throw further
112
                }
113
                $thrownException = $ex;
114
            } catch (Throwable $ex) {
115
                if (!$this->getProject()->isKeepGoingMode()) {
116
                    if ($this->verbose && null !== $subdirPath) {
117
                        $this->log('Leaving directory: ' . $subdirPath . "\n");
118
                    }
119
120
                    throw new BuildException($ex);
121
                }
122
                $thrownException = $ex;
123
            }
124
            if (null !== $thrownException) {
125
                if ($thrownException instanceof BuildException) {
126
                    $this->log("File '" . $file
127
                        . "' failed with message '"
128
                        . $thrownException->getMessage() . "'.", Project::MSG_ERR);
129
                    // only the first build exception is reported
130
                    if (null === $buildException) {
131
                        $buildException = $thrownException;
132
                    }
133
                } else {
134
                    $this->log("Target '" . $file
135
                        . "' failed with message '"
136
                        . $thrownException->getMessage() . "'.", Project::MSG_ERR);
137
                    $this->log($thrownException->getTraceAsString(), Project::MSG_ERR);
138
                    if (null === $buildException) {
139
                        $buildException = new BuildException($thrownException);
140
                    }
141
                }
142
                if ($this->verbose && null !== $subdirPath) {
143
                    $this->log('Leaving directory: ' . $subdirPath . "\n");
144
                }
145
            }
146
        }
147
        // check if one of the builds failed in keep going mode
148
        if (null !== $buildException) {
149
            throw $buildException;
150
        }
151
    }
152
153
    /**
154
     * This method builds the file name to use in conjunction with directories.
155
     *
156
     * <p>Defaults to "build.xml".
157
     * If <code>genericantfile</code> is set, this attribute is ignored.</p>
158
     *
159
     * @param string $phingfile the short build file name. Defaults to "build.xml".
160
     */
161 1
    public function setPhingfile(string $phingfile)
162
    {
163 1
        $this->phingfile = $phingfile;
164
    }
165
166
    /**
167
     * This method builds a file path to use in conjunction with directories.
168
     *
169
     * <p>Use <code>genericantfile</code>, in order to run the same build file
170
     * with different basedirs.</p>
171
     * If this attribute is set, <code>antfile</code> is ignored.
172
     *
173
     * @param File $afile (path of the generic ant file, absolute or relative to
174
     *                    project base directory)
175
     */
176 2
    public function setGenericPhingfile(File $afile)
177
    {
178 2
        $this->genericphingfile = $afile;
179
    }
180
181
    /**
182
     * The target to call on the different sub-builds. Set to "" to execute
183
     * the default target.
184
     *
185
     * @param target the target
186
     */
187
    // REVISIT: Defaults to the target name that contains this task if not specified.
188
189
    /**
190
     * Sets whether to fail with a build exception on error, or go on.
191
     *
192
     * @param bool $failOnError the new value for this bool flag
193
     */
194 1
    public function setFailonerror(bool $failOnError)
195
    {
196 1
        $this->failOnError = $failOnError;
197
    }
198
199
    public function setTarget(string $target)
200
    {
201
        $this->subTarget = $target;
202
    }
203
204
    /**
205
     * Enable/ disable verbose log messages showing when each sub-build path is entered/ exited.
206
     * The default value is "false".
207
     *
208
     * @param bool $verbose true to enable verbose mode, false otherwise (default)
209
     */
210 1
    public function setVerbose(bool $verbose)
211
    {
212 1
        $this->verbose = $verbose;
213
    }
214
215
    /**
216
     * Corresponds to <code>&lt;ant&gt;</code>'s
217
     * <code>output</code> attribute.
218
     *
219
     * @param string $output the filename to write the output to
220
     */
221
    public function setOutput(string $output)
222
    {
223
        $this->output = $output;
224
    }
225
226
    /**
227
     * Corresponds to <code>&lt;ant&gt;</code>'s
228
     * <code>inheritall</code> attribute.
229
     *
230
     * @param bool $inheritAll the new value for this bool flag
231
     */
232 1
    public function setInheritall(bool $inheritAll)
233
    {
234 1
        $this->inheritAll = $inheritAll;
235
    }
236
237
    /**
238
     * Corresponds to <code>&lt;ant&gt;</code>'s
239
     * <code>inheritrefs</code> attribute.
240
     *
241
     * @param bool $inheritRefs the new value for this bool flag
242
     */
243 1
    public function setInheritrefs(bool $inheritRefs)
244
    {
245 1
        $this->inheritRefs = $inheritRefs;
246
    }
247
248
    /**
249
     * Property to pass to the new project.
250
     * The property is passed as a 'user property'.
251
     */
252
    public function createProperty()
253
    {
254
        $p = new PropertyTask();
255
        $p->setUserProperty(true);
256
        $p->setTaskName('property');
257
        $this->properties[] = $p;
258
259
        return $p;
260
    }
261
262
    /**
263
     * Corresponds to <code>&lt;ant&gt;</code>'s
264
     * nested <code>&lt;reference&gt;</code> element.
265
     *
266
     * @param PhingReference $r the reference to pass on explicitly to the sub-build
267
     */
268
    public function addReference(PhingReference $r)
269
    {
270
        $this->references[] = $r;
271
    }
272
273
    /**
274
     * Set the buildpath to be used to find sub-projects.
275
     *
276
     * @param Path $s an Ant Path object containing the buildpath
277
     */
278
    public function setBuildpath(Path $s)
279
    {
280
        $this->getBuildpath()->append($s);
281
    }
282
283
    /**
284
     * Creates a nested build path, and add it to the implicit build path.
285
     *
286
     * @return Path the newly created nested build path
287
     */
288
    public function createBuildpath(): Path
289
    {
290
        return $this->getBuildpath()->createPath();
291
    }
292
293
    /**
294
     * Creates a nested <code>&lt;buildpathelement&gt;</code>,
295
     * and add it to the implicit build path.
296
     *
297
     * @return PathElement the newly created nested build path element
298
     */
299
    public function createBuildpathElement()
300
    {
301
        return $this->getBuildpath()->createPathElement();
302
    }
303
304
    /**
305
     * Buildpath to use, by reference.
306
     *
307
     * @param Reference $r a reference to an Ant Path object containing the buildpath
308
     */
309
    public function setBuildpathRef(Reference $r)
310
    {
311
        $this->createBuildpath()->setRefid($r);
312
    }
313
314
    /**
315
     * Assigns an Ant property to another.
316
     *
317
     * @param PropertyTask $to   the destination property whose content is modified
318
     * @param PropertyTask $from the source property whose content is copied
319
     */
320
    private static function copyProperty(PropertyTask $to, PropertyTask $from)
321
    {
322
        $to->setName($from->getName());
323
324
        if (null !== $from->getValue()) {
0 ignored issues
show
introduced by
The condition null !== $from->getValue() is always true.
Loading history...
325
            $to->setValue($from->getValue());
326
        }
327
        if (null !== $from->getFile()) {
328
            $to->setFile($from->getFile());
329
        }
330
        if (null !== $from->getPrefix()) {
0 ignored issues
show
introduced by
The condition null !== $from->getPrefix() is always true.
Loading history...
331
            $to->setPrefix($from->getPrefix());
332
        }
333
        if (null !== $from->getRefid()) {
334
            $to->setRefid($from->getRefid());
335
        }
336
        if (null !== $from->getEnvironment()) {
0 ignored issues
show
introduced by
The condition null !== $from->getEnvironment() is always true.
Loading history...
337
            $to->setEnvironment($from->getEnvironment());
338
        }
339
    }
340
341
    /**
342
     * Runs the given target on the provided build file.
343
     *
344
     * @param File      $file      the build file to execute
345
     * @param null|File $directory the directory of the current iteration
346
     *
347
     * @throws BuildException|IOException is the file cannot be found, read, is
348
     *                                    a directory, or the target called failed, but only if
349
     *                                    <code>failOnError</code> is <code>true</code>. Otherwise,
350
     *                                    a warning log message is simply output.
351
     */
352
    private function execute(File $file, ?File $directory)
353
    {
354
        if (!$file->exists() || $file->isDirectory() || !$file->canRead()) {
355
            $msg = 'Invalid file: ' . $file;
356
            if ($this->failOnError) {
357
                throw new BuildException($msg);
358
            }
359
            $this->log($msg, Project::MSG_WARN);
360
361
            return;
362
        }
363
364
        $this->phing = $this->createPhingTask($directory);
365
        $phingfilename = $file->getAbsolutePath();
366
        $this->phing->setPhingfile($phingfilename);
367
        if (null !== $this->subTarget) {
368
            $this->phing->setTarget($this->subTarget);
369
        }
370
371
        try {
372
            if ($this->verbose) {
373
                $this->log('Executing: ' . $phingfilename);
374
            }
375
            $this->phing->main();
376
        } catch (BuildException $e) {
377
            if ($this->failOnError || $this->isHardError($e)) {
378
                throw $e;
379
            }
380
            $this->log("Failure for target '" . $this->subTarget
381
                . "' of: " . $phingfilename . "\n"
382
                . $e->getMessage(), Project::MSG_WARN);
383
        } catch (Throwable $e) {
384
            if ($this->failOnError || $this->isHardError($e)) {
385
                throw new BuildException($e);
386
            }
387
            $this->log(
388
                "Failure for target '" . $this->subTarget
389
                . "' of: " . $phingfilename . "\n"
390
                . $e,
391
                Project::MSG_WARN
392
            );
393
        } finally {
394
            $this->phing = null;
395
        }
396
    }
397
398
    /**
399
     * Creates the &lt;phing&gt; task configured to run a specific target.
400
     *
401
     * @param ?File $directory : if not null the directory where the build should run
402
     *
403
     * @return PhingTask the phing task, configured with the explicit properties and
404
     *                   references necessary to run the sub-build
405
     */
406
    private function createPhingTask(?File $directory): PhingTask
407
    {
408
        $phingTask = new PhingTask($this);
409
        $phingTask->setHaltOnFailure($this->failOnError);
410
        $phingTask->init();
411
        if (null !== $this->subTarget && '' !== $this->subTarget) {
412
            $phingTask->setTarget($this->subTarget);
413
        }
414
415
        if (null !== $this->output) {
416
            $phingTask->setOutput($this->output);
417
        }
418
419
        if (null !== $directory) {
420
            $phingTask->setDir($directory);
421
        } else {
422
            $phingTask->setUseNativeBasedir(true);
423
        }
424
425
        $phingTask->setInheritAll($this->inheritAll);
426
427
        foreach ($this->properties as $p) {
428
            self::copyProperty($phingTask->createProperty(), $p);
429
        }
430
431
        $phingTask->setInheritRefs($this->inheritRefs);
432
433
        foreach ($this->references as $reference) {
434
            $phingTask->addReference($reference);
435
        }
436
437
        foreach ($this->filesets as $fileset) {
438
            $phingTask->addFileSet($fileset);
439
        }
440
441
        return $phingTask;
442
    }
443
444
    /** whether we should even try to continue after this error */
445
    private function isHardError(Throwable $t)
446
    {
447
        if ($t instanceof BuildException) {
448
            return $this->isHardError($t->getPrevious());
449
        }
450
451
        return false;
452
    }
453
454
    /**
455
     * Gets the implicit build path, creating it if <code>null</code>.
456
     *
457
     * @return Path the implicit build path
458
     */
459
    private function getBuildpath(): Path
460
    {
461
        if (null === $this->buildpath) {
462
            $this->buildpath = new Path($this->getProject());
463
        }
464
465
        return $this->buildpath;
466
    }
467
}
468