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