Passed
Push — master ( b9bc63...f1e2bc )
by Michiel
06:23
created

SubPhing::main()   F

Complexity

Conditions 27
Paths 1647

Size

Total Lines 87
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 87
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
 * 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\Task\System;
21
22
use Phing\Exception\BuildException;
23
use Phing\Io\File;
24
use Phing\Project;
25
use Phing\Target;
26
use Phing\Task;
27
use Phing\Type\Element\FileSetAware;
28
use Phing\Type\Path;
29
use Phing\Type\PathElement;
30
use Phing\Type\Reference;
31
use RuntimeException;
32
use Throwable;
33
34
class SubPhing extends Task
35
{
36
    use FileSetAware;
37
38
    /** @var Path */
39
    private $buildpath;
40
41
    private $phing;
42
    private $subTarget;
43
    private $phingfile = 'build.xml';
44
    private $genericphingfile;
45
    private $verbose = false;
46
    private $inheritAll = false;
47
    private $inheritRefs = false;
48
    private $failOnError = true;
49
    private $output;
50
51
    private $properties = [];
52
    private $references = [];
53
54
    /**
55
     * Assigns an Ant property to another.
56
     *
57
     * @param PropertyTask $to the destination property whose content is modified.
58
     * @param PropertyTask $from the source property whose content is copied.
59
     */
60
    private static function copyProperty(PropertyTask $to, PropertyTask $from)
61
    {
62
        $to->setName($from->getName());
63
64
        if ($from->getValue() !== null) {
0 ignored issues
show
introduced by
The condition $from->getValue() !== null is always true.
Loading history...
65
            $to->setValue($from->getValue());
66
        }
67
        if ($from->getFile() !== null) {
68
            $to->setFile($from->getFile());
69
        }
70
        if ($from->getPrefix() !== null) {
0 ignored issues
show
introduced by
The condition $from->getPrefix() !== null is always true.
Loading history...
71
            $to->setPrefix($from->getPrefix());
72
        }
73
        if ($from->getRefid() !== null) {
74
            $to->setRefid($from->getRefid());
75
        }
76
        if ($from->getEnvironment() !== null) {
0 ignored issues
show
introduced by
The condition $from->getEnvironment() !== null is always true.
Loading history...
77
            $to->setEnvironment($from->getEnvironment());
78
        }
79
    }
80
81
    /**
82
     * Runs the various sub-builds.
83
     */
84 3
    public function main()
85
    {
86 3
        $filenames = [];
87 3
        if ($this->buildpath !== null) {
88
            $filenames = $this->buildpath->listPaths();
89 3
        } elseif (count($this->filesets) > 0) {
90 3
            foreach ($this->filesets as $fileset) {
91 3
                foreach ($fileset as $filename) {
92
                    $filenames[] = $filename;
93
                }
94
            }
95
        } else {
96
            throw new BuildException("No buildpath specified");
97
        }
98 3
        $count = count($filenames);
99 3
        if ($count < 1) {
100 3
            $this->log('No sub-builds to iterate on', Project::MSG_WARN);
101 3
            return;
102
        }
103
104
        $buildException = null;
105
        foreach ($filenames as $filename) {
106
            $file = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $file is dead and can be removed.
Loading history...
107
            $subdirPath = null;
108
            $thrownException = null;
109
            try {
110
                $directory = null;
111
                $file = new File($filename);
112
                if ($file->isDirectory()) {
113
                    if ($this->verbose) {
114
                        $subdirPath = $file->getPath();
115
                        $this->log("Entering directory: " . $subdirPath . "\n");
116
                    }
117
                    if ($this->genericphingfile !== null) {
118
                        $directory = $file;
119
                        $file = $this->genericphingfile;
120
                    } else {
121
                        $file = new File($file, $this->phingfile);
122
                    }
123
                }
124
                $this->execute($file, $directory);
125
                if ($this->verbose && $subdirPath !== null) {
126
                    $this->log('Leaving directory: ' . $subdirPath . "\n");
127
                }
128
            } catch (RuntimeException $ex) {
129
                if (!$this->getProject()->isKeepGoingMode()) {
130
                    if ($this->verbose && $subdirPath !== null) {
131
                        $this->log('Leaving directory: ' . $subdirPath . "\n");
132
                    }
133
                    throw $ex; // throw further
134
                }
135
                $thrownException = $ex;
136
            } catch (Throwable $ex) {
137
                if (!$this->getProject()->isKeepGoingMode()) {
138
                    if ($this->verbose && $subdirPath !== null) {
139
                        $this->log('Leaving directory: ' . $subdirPath . "\n");
140
                    }
141
                    throw new BuildException($ex);
142
                }
143
                $thrownException = $ex;
144
            }
145
            if ($thrownException !== null) {
146
                if ($thrownException instanceof BuildException) {
147
                    $this->log("File '" . $file
148
                        . "' failed with message '"
149
                        . $thrownException->getMessage() . "'.", Project::MSG_ERR);
150
                    // only the first build exception is reported
151
                    if ($buildException === null) {
152
                        $buildException = $thrownException;
153
                    }
154
                } else {
155
                    $this->log("Target '" . $file
156
                        . "' failed with message '"
157
                        . $thrownException->getMessage() . "'.", Project::MSG_ERR);
158
                    $this->log($thrownException->getTraceAsString(), Project::MSG_ERR);
159
                    if ($buildException === null) {
160
                        $buildException = new BuildException($thrownException);
161
                    }
162
                }
163
                if ($this->verbose && $subdirPath !== null) {
164
                    $this->log('Leaving directory: ' . $subdirPath . "\n");
165
                }
166
            }
167
        }
168
        // check if one of the builds failed in keep going mode
169
        if ($buildException !== null) {
170
            throw $buildException;
171
        }
172
    }
173
174
    /**
175
     * Runs the given target on the provided build file.
176
     *
177
     * @param file the build file to execute
178
     * @param directory the directory of the current iteration
0 ignored issues
show
Bug introduced by
The type Phing\Task\System\the was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
179
     * @throws BuildException is the file cannot be found, read, is
180
     *         a directory, or the target called failed, but only if
181
     *         <code>failOnError</code> is <code>true</code>. Otherwise,
182
     *         a warning log message is simply output.
183
     */
184
    private function execute(File $file, ?File $directory)
185
    {
186
        if (!$file->exists() || $file->isDirectory() || !$file->canRead()) {
187
            $msg = "Invalid file: " . $file;
188
            if ($this->failOnError) {
189
                throw new BuildException($msg);
190
            }
191
            $this->log($msg, Project::MSG_WARN);
192
            return;
193
        }
194
195
        $this->phing = $this->createPhingTask($directory);
196
        $phingfilename = $file->getAbsolutePath();
197
        $this->phing->setPhingfile($phingfilename);
198
        if ($this->subTarget !== null) {
199
            $this->phing->setTarget($this->subTarget);
200
        }
201
202
        try {
203
            if ($this->verbose) {
204
                $this->log("Executing: " . $phingfilename);
205
            }
206
            $this->phing->main();
207
        } catch (BuildException $e) {
208
            if ($this->failOnError || $this->isHardError($e)) {
209
                throw $e;
210
            }
211
            $this->log("Failure for target '" . $this->subTarget
212
                . "' of: " . $phingfilename . "\n"
213
                . $e->getMessage(), Project::MSG_WARN);
214
        } catch (Throwable $e) {
215
            if ($this->failOnError || $this->isHardError($e)) {
216
                throw new BuildException($e);
217
            }
218
            $this->log(
219
                "Failure for target '" . $this->subTarget
220
                . "' of: " . $phingfilename . "\n"
221
                . $e,
222
                Project::MSG_WARN
223
            );
224
        } finally {
225
            $this->phing = null;
226
        }
227
    }
228
229
    /**
230
     * Creates the &lt;phing&gt; task configured to run a specific target.
231
     *
232
     * @param ?File $directory : if not null the directory where the build should run
233
     *
234
     * @return PhingTask the phing task, configured with the explicit properties and
235
     *         references necessary to run the sub-build.
236
     */
237
    private function createPhingTask(?File $directory): PhingTask
238
    {
239
        $phingTask = new PhingTask($this);
240
        $phingTask->setHaltOnFailure($this->failOnError);
241
        $phingTask->init();
242
        if ($this->subTarget !== null && $this->subTarget !== '') {
243
            $phingTask->setTarget($this->subTarget);
244
        }
245
246
        if ($this->output !== null) {
247
            $phingTask->setOutput($this->output);
248
        }
249
250
        if ($directory !== null) {
251
            $phingTask->setDir($directory);
252
        } else {
253
            $phingTask->setUseNativeBasedir(true);
254
        }
255
256
        $phingTask->setInheritAll($this->inheritAll);
257
258
        foreach ($this->properties as $p) {
259
            self::copyProperty($phingTask->createProperty(), $p);
260
        }
261
262
        $phingTask->setInheritRefs($this->inheritRefs);
263
264
        foreach ($this->references as $reference) {
265
            $phingTask->addReference($reference);
266
        }
267
268
        foreach ($this->filesets as $fileset) {
269
            $phingTask->addFileSet($fileset);
270
        }
271
272
        return $phingTask;
273
    }
274
275
    /** whether we should even try to continue after this error */
276
    private function isHardError(Throwable $t)
277
    {
278
        if ($t instanceof BuildException) {
279
            return $this->isHardError($t->getPrevious());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->isHardError($t->getPrevious()) targeting Phing\Task\System\SubPhing::isHardError() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
280
        }
281
    }
282
283
    /**
284
     * This method builds the file name to use in conjunction with directories.
285
     *
286
     * <p>Defaults to "build.xml".
287
     * If <code>genericantfile</code> is set, this attribute is ignored.</p>
288
     *
289
     * @param string $phingfile the short build file name. Defaults to "build.xml".
290
     */
291 1
    public function setPhingfile(string $phingfile)
292
    {
293 1
        $this->phingfile = $phingfile;
294 1
    }
295
296
    /**
297
     * This method builds a file path to use in conjunction with directories.
298
     *
299
     * <p>Use <code>genericantfile</code>, in order to run the same build file
300
     * with different basedirs.</p>
301
     * If this attribute is set, <code>antfile</code> is ignored.
302
     *
303
     * @param File $afile (path of the generic ant file, absolute or relative to
304
     *               project base directory)
305
     *
306
     */
307 2
    public function setGenericPhingfile(File $afile)
308
    {
309 2
        $this->genericphingfile = $afile;
310 2
    }
311
312
    /**
313
     * The target to call on the different sub-builds. Set to "" to execute
314
     * the default target.
315
     *
316
     * @param target the target
317
     */
318
    // REVISIT: Defaults to the target name that contains this task if not specified.
319
320
    /**
321
     * Sets whether to fail with a build exception on error, or go on.
322
     *
323
     * @param bool $failOnError the new value for this boolean flag.
324
     */
325 1
    public function setFailonerror(bool $failOnError)
326
    {
327 1
        $this->failOnError = $failOnError;
328 1
    }
329
330
    public function setTarget(string $target)
331
    {
332
        $this->subTarget = $target;
333
    }
334
335
    /**
336
     * Enable/ disable verbose log messages showing when each sub-build path is entered/ exited.
337
     * The default value is "false".
338
     * @param bool $on true to enable verbose mode, false otherwise (default).
339
     */
340 1
    public function setVerbose(bool $on)
341
    {
342 1
        $this->verbose = $on;
343 1
    }
344
345
    /**
346
     * Corresponds to <code>&lt;ant&gt;</code>'s
347
     * <code>output</code> attribute.
348
     *
349
     * @param string $s the filename to write the output to.
350
     */
351
    public function setOutput(string $s)
352
    {
353
        $this->output = $s;
354
    }
355
356
    /**
357
     * Corresponds to <code>&lt;ant&gt;</code>'s
358
     * <code>inheritall</code> attribute.
359
     *
360
     * @param b the new value for this boolean flag.
361
     */
362 1
    public function setInheritall(bool $b)
363
    {
364 1
        $this->inheritAll = $b;
365 1
    }
366
367
    /**
368
     * Corresponds to <code>&lt;ant&gt;</code>'s
369
     * <code>inheritrefs</code> attribute.
370
     *
371
     * @param b the new value for this boolean flag.
372
     */
373 1
    public function setInheritrefs(bool $b)
374
    {
375 1
        $this->inheritRefs = $b;
376 1
    }
377
378
    /**
379
     * Property to pass to the new project.
380
     * The property is passed as a 'user property'
381
     */
382
    public function createProperty()
383
    {
384
        $p = new PropertyTask();
385
        $p->setUserProperty(true);
386
        $p->setTaskName('property');
387
        $this->properties[] = $p;
388
389
        return $p;
390
    }
391
392
    /**
393
     * Corresponds to <code>&lt;ant&gt;</code>'s
394
     * nested <code>&lt;reference&gt;</code> element.
395
     *
396
     * @param PhingReference $r the reference to pass on explicitly to the sub-build.
397
     */
398
    public function addReference(PhingReference $r)
399
    {
400
        $this->references[] = $r;
401
    }
402
403
    /**
404
     * Set the buildpath to be used to find sub-projects.
405
     *
406
     * @param Path $s an Ant Path object containing the buildpath.
407
     */
408
    public function setBuildpath(Path $s)
409
    {
410
        $this->getBuildpath()->append($s);
411
    }
412
413
    /**
414
     * Creates a nested build path, and add it to the implicit build path.
415
     *
416
     * @return Path the newly created nested build path.
417
     */
418
    public function createBuildpath(): Path
419
    {
420
        return $this->getBuildpath()->createPath();
421
    }
422
423
    /**
424
     * Gets the implicit build path, creating it if <code>null</code>.
425
     *
426
     * @return Path the implicit build path.
427
     */
428
    private function getBuildpath(): Path
429
    {
430
        if ($this->buildpath === null) {
431
            $this->buildpath = new Path($this->getProject());
432
        }
433
        return $this->buildpath;
434
    }
435
436
    /**
437
     * Creates a nested <code>&lt;buildpathelement&gt;</code>,
438
     * and add it to the implicit build path.
439
     *
440
     * @return PathElement the newly created nested build path element.
441
     */
442
    public function createBuildpathElement()
443
    {
444
        return $this->getBuildpath()->createPathElement();
445
    }
446
447
    /**
448
     * Buildpath to use, by reference.
449
     *
450
     * @param Reference $r a reference to an Ant Path object containing the buildpath.
451
     */
452
    public function setBuildpathRef(Reference $r)
453
    {
454
        $this->createBuildpath()->setRefid($r);
455
    }
456
}
457