Passed
Push — master ( 0123ab...cb5cd1 )
by Siad
11:09
created

PhingTask::processFile()   F

Complexity

Conditions 18
Paths 3424

Size

Total Lines 109
Code Lines 58

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 54
CRAP Score 18.0147

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 18
eloc 58
c 1
b 0
f 0
nc 3424
nop 0
dl 0
loc 109
ccs 54
cts 56
cp 0.9643
crap 18.0147
rs 0.7

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
 * Task that invokes phing on another build file.
22
 *
23
 * Use this task, for example, if you have nested buildfiles in your project. Unlike
24
 * AntTask, PhingTask can even support filesets:
25
 *
26
 * <pre>
27
 *   <phing>
28
 *    <fileset dir="${srcdir}">
29
 *      <include name="** /build.xml" /> <!-- space added after ** is there because of PHP comment syntax -->
30
 *      <exclude name="build.xml" />
31
 *    </fileset>
32
 *   </phing>
33
 * </pre>
34
 *
35
 * @author  Hans Lellelid <[email protected]>
36
 * @package phing.tasks.system
37
 */
38
class PhingTask extends Task
39
{
40
    use FileSetAware;
41
42
    /**
43
     * the basedir where is executed the build file
44
     */
45
    private $dir;
46
47
    /**
48
     * build.xml (can be absolute) in this case dir will be ignored
49
     */
50
    private $phingFile;
51
52
    /**
53
     * the target to call if any
54
     */
55
    protected $newTarget;
56
57
    /**
58
     * should we inherit properties from the parent ?
59
     */
60
    private $inheritAll = true;
61
62
    /**
63
     * should we inherit references from the parent ?
64
     */
65
    private $inheritRefs = false;
66
67
    /**
68
     * the properties to pass to the new project
69
     */
70
    private $properties = [];
71
72
    /**
73
     * the references to pass to the new project
74
     */
75
    private $references = [];
76
77
    /**
78
     * The temporary project created to run the build file
79
     *
80
     * @var Project
81
     */
82
    private $newProject;
83
84
    /**
85
     * Fail the build process when the called build fails?
86
     */
87
    private $haltOnFailure = false;
88
89
    /**
90
     * @var OutputStream
91
     */
92
    private $out;
93
94
    /** @var string */
95
    private $output;
96
97
    /**
98
     *  If true, abort the build process if there is a problem with or in the target build file.
99
     *  Defaults to false.
100
     *
101
     * @param boolean $hof new value
102
     */
103 14
    public function setHaltOnFailure($hof)
104
    {
105 14
        $this->haltOnFailure = (bool) $hof;
106 14
    }
107
108
    /**
109
     * Creates a Project instance for the project to call.
110
     *
111
     * @return void
112
     */
113 27
    public function init()
114
    {
115 27
        $this->newProject = new Project();
116 27
        $tdf = $this->project->getTaskDefinitions();
117 27
        $this->newProject->addTaskDefinition("property", $tdf["property"]);
118 27
    }
119
120
    /**
121
     * Called in execute or createProperty if newProject is null.
122
     *
123
     * <p>This can happen if the same instance of this task is run
124
     * twice as newProject is set to null at the end of execute (to
125
     * save memory and help the GC).</p>
126
     *
127
     * <p>Sets all properties that have been defined as nested
128
     * property elements.</p>
129
     */
130 6
    private function reinit()
131
    {
132 6
        $this->init();
133 6
        $count = count($this->properties);
134 6
        for ($i = 0; $i < $count; $i++) {
135
            /**
136
             * @var PropertyTask $p
137
             */
138 6
            $p = $this->properties[$i];
139
            /**
140
             * @var PropertyTask $newP
141
             */
142 6
            $newP = $this->newProject->createTask("property");
143 6
            $newP->setName($p->getName());
144 6
            if ($p->getValue() !== null) {
145 6
                $newP->setValue($p->getValue());
146
            }
147 6
            if ($p->getFile() !== null) {
148
                $newP->setFile($p->getFile());
149
            }
150 6
            if ($p->getPrefix() !== null) {
151
                $newP->setPrefix($p->getPrefix());
152
            }
153 6
            if ($p->getRefid() !== null) {
154
                $newP->setRefid($p->getRefid());
155
            }
156 6
            if ($p->getEnvironment() !== null) {
157
                $newP->setEnvironment($p->getEnvironment());
158
            }
159 6
            if ($p->getUserProperty() !== null) {
160 6
                $newP->setUserProperty($p->getUserProperty());
161
            }
162 6
            $newP->setOverride($p->getOverride());
163 6
            $newP->setLogoutput($p->getLogoutput());
164 6
            $newP->setQuiet($p->getQuiet());
165
166 6
            $this->properties[$i] = $newP;
167
        }
168 6
    }
169
170
    /**
171
     * Main entry point for the task.
172
     *
173
     * @return void
174
     */
175 24
    public function main()
176
    {
177
178
        // Call Phing on the file set with the attribute "phingfile"
179 24
        if ($this->phingFile !== null or $this->dir !== null) {
180 24
            $this->processFile();
181
        }
182
183
        // if no filesets are given stop here; else process filesets
184 21
        if (!empty($this->filesets)) {
185
            // preserve old settings
186
            $savedDir = $this->dir;
187
            $savedPhingFile = $this->phingFile;
188
            $savedTarget = $this->newTarget;
189
190
            // set no specific target for files in filesets
191
            // [HL] I'm commenting this out; I don't know why this should not be supported!
192
            // $this->newTarget = null;
193
194
            foreach ($this->filesets as $fs) {
195
                $ds = $fs->getDirectoryScanner($this->project);
196
197
                $fromDir = $fs->getDir($this->project);
0 ignored issues
show
Unused Code introduced by
The assignment to $fromDir is dead and can be removed.
Loading history...
198
                $srcFiles = $ds->getIncludedFiles();
199
200
                foreach ($srcFiles as $fname) {
201
                    $f = new PhingFile($ds->getbasedir(), $fname);
202
                    $f = $f->getAbsoluteFile();
203
                    $this->phingFile = $f->getAbsolutePath();
204
                    $this->dir = $f->getParentFile();
205
                    $this->processFile(); // run Phing!
206
                }
207
            }
208
209
            // side effect free programming ;-)
210
            $this->dir = $savedDir;
211
            $this->phingFile = $savedPhingFile;
212
            $this->newTarget = $savedTarget;
213
214
            // [HL] change back to correct dir
215
            if ($this->dir !== null) {
216
                chdir($this->dir->getAbsolutePath());
217
            }
218
        }
219
220
        // Remove any dangling references to help the GC
221 21
        foreach ($this->properties as $property) {
222 11
            $property->setFallback(null);
223
        }
224 21
    }
225
226
    /**
227
     * Execute phing file.
228
     *
229
     * @throws BuildException
230
     * @return void
231
     */
232 24
    private function processFile()
233
    {
234 24
        $buildFailed = false;
235 24
        $savedDir = $this->dir;
236 24
        $savedPhingFile = $this->phingFile;
237 24
        $savedTarget = $this->newTarget;
238
239 24
        $savedBasedirAbsPath = null; // this is used to save the basedir *if* we change it
240
241
        try {
242 24
            $this->getNewProject();
243
244 24
            $this->initializeProject();
245
246 24
            if ($this->dir !== null) {
247 4
                $dirAbsPath = $this->dir->getAbsolutePath();
248
249
                // BE CAREFUL! -- when the basedir is changed for a project,
250
                // all calls to getAbsolutePath() on a relative-path dir will
251
                // be made relative to the project's basedir!  This means
252
                // that subsequent calls to $this->dir->getAbsolutePath() will be WRONG!
253
254
                // We need to save the current project's basedir first.
255 4
                $savedBasedirAbsPath = $this->getProject()->getBasedir()->getAbsolutePath();
256
257 4
                $this->newProject->setBasedir($this->dir);
258
259
                // Now we must reset $this->dir so that it continues to resolve to the same
260
                // path.
261 4
                $this->dir = new PhingFile($dirAbsPath);
262
263 4
                if ($savedDir !== null) { // has been set explicitly
264 4
                    $this->newProject->setInheritedProperty("project.basedir", $this->dir->getAbsolutePath());
265
                }
266
            } else {
267
                // Since we're not changing the basedir here (for file resolution),
268
                // we don't need to worry about any side-effects in this scanrio.
269 21
                $this->dir = $this->getProject()->getBasedir();
270
            }
271
272 24
            $this->overrideProperties();
273 24
            if ($this->phingFile === null) {
274
                $this->phingFile = "build.xml";
275
            }
276
277 24
            $fu = new FileUtils();
278 24
            $file = $fu->resolveFile($this->dir, $this->phingFile);
279 24
            $this->phingFile = $file->getAbsolutePath();
280
281 24
            $this->log(
282 24
                "Calling Buildfile '" . $this->phingFile . "' with target '" . $this->newTarget . "'",
283 24
                Project::MSG_VERBOSE
284
            );
285
286 24
            $this->newProject->setUserProperty("phing.file", $this->phingFile);
287
288 24
            ProjectConfigurator::configureProject($this->newProject, new PhingFile($this->phingFile));
289
290 23
            if ($this->newTarget === null) {
291
                $this->newTarget = $this->newProject->getDefaultTarget();
292
            }
293
294
            // Are we trying to call the target in which we are defined?
295
            if (
296 23
                $this->newProject->getBaseDir() == $this->project->getBaseDir()
297 19
                && $this->newProject->getProperty("phing.file") == $this->project->getProperty("phing.file")
298 16
                && $this->getOwningTarget() !== null
299 16
                && $this->newTarget == $this->getOwningTarget()->getName()
300
            ) {
301 2
                throw new BuildException("phing task calling its own parent target");
302
            }
303
304 22
            $this->addReferences();
305 22
            $this->newProject->executeTarget($this->newTarget);
306 4
        } catch (Exception $e) {
307 4
            $buildFailed = true;
308 4
            $this->log($e->getMessage(), Project::MSG_ERR);
309 4
            if (Phing::getMsgOutputLevel() <= Project::MSG_DEBUG) {
310 4
                $lines = explode("\n", $e->getTraceAsString());
311 4
                foreach ($lines as $line) {
312 4
                    $this->log($line, Project::MSG_DEBUG);
313
                }
314
            }
315
            // important!!! continue on to perform cleanup tasks.
316
        }
317
318
        // reset environment values to prevent side-effects.
319
320 24
        $this->newProject = null;
321 24
        $pkeys = array_keys($this->properties);
322 24
        foreach ($pkeys as $k) {
323 11
            $this->properties[$k]->setProject(null);
324
        }
325
326 24
        if ($this->output !== null && $this->out !== null) {
327 1
            $this->out->close();
328
        }
329
330 24
        $this->dir = $savedDir;
331 24
        $this->phingFile = $savedPhingFile;
332 24
        $this->newTarget = $savedTarget;
333
334
        // If the basedir for any project was changed, we need to set that back here.
335 24
        if ($savedBasedirAbsPath !== null) {
336 4
            chdir($savedBasedirAbsPath);
337
        }
338
339 24
        if ($this->haltOnFailure && $buildFailed) {
340 3
            throw new BuildException("Execution of the target buildfile failed. Aborting.");
341
        }
342 21
    }
343
344
    /**
345
     * Get the (sub)-Project instance currently in use.
346
     *
347
     * @return Project
348
     */
349 24
    protected function getNewProject(): \Project
350
    {
351 24
        if ($this->newProject === null) {
352 6
            $this->reinit();
353
        }
354 24
        return $this->newProject;
355
    }
356
357
    /**
358
     * Configure the Project, i.e. make intance, attach build listeners
359
     * (copy from father project), add Task and Datatype definitions,
360
     * copy properties and references from old project if these options
361
     * are set via the attributes of the XML tag.
362
     *
363
     * Developer note:
364
     * This function replaces the old methods "init", "_reinit" and
365
     * "_initializeProject".
366
     */
367 24
    private function initializeProject()
368
    {
369 24
        $this->newProject->setInputHandler($this->project->getInputHandler());
370
371 24
        foreach ($this->project->getBuildListeners() as $listener) {
372 24
            $this->newProject->addBuildListener($listener);
373
        }
374
375
        /* Copy things from old project. Datatypes and Tasks are always
376
         * copied, properties and references only if specified so/not
377
         * specified otherwise in the XML definition.
378
         */
379
        // Add Datatype definitions
380 24
        foreach ($this->project->getDataTypeDefinitions() as $typeName => $typeClass) {
381 24
            $this->newProject->addDataTypeDefinition($typeName, $typeClass);
382
        }
383
384
        // Add Task definitions
385 24
        foreach ($this->project->getTaskDefinitions() as $taskName => $taskClass) {
386 24
            if ($taskClass == "propertytask") {
387
                // we have already added this taskdef in init()
388
                continue;
389
            }
390 24
            $this->newProject->addTaskDefinition($taskName, $taskClass);
391
        }
392
393 24
        if ($this->output !== null) {
394
            try {
395 1
                if ($this->dir !== null) {
396 1
                    $outfile = (new FileUtils())->resolveFile($this->dir, $this->output);
397
                } else {
398 1
                    $outfile = $this->getProject()->resolveFile($this->output);
399
                }
400 1
                $this->out = new FileOutputStream($outfile);
401 1
                $logger = new DefaultLogger();
402 1
                $logger->setMessageOutputLevel(Project::MSG_INFO);
403 1
                $logger->setOutputStream($this->out);
404 1
                $logger->setErrorStream($this->out);
405 1
                $this->newProject->addBuildListener($logger);
406
            } catch (Exception $ex) {
407
                $this->log("Phing: Can't set output to " . $this->output);
408
            }
409
        }
410
411
        // set user-defined properties
412 24
        $this->project->copyUserProperties($this->newProject);
413
414 24
        if (!$this->inheritAll) {
415
            // set System built-in properties separately,
416
            // b/c we won't inherit them.
417 13
            $this->newProject->setSystemProperties();
418
        } else {
419
            // set all properties from calling project
420 12
            $properties = $this->project->getProperties();
421 12
            foreach ($properties as $name => $value) {
422 12
                if ($name == "basedir" || $name == "phing.file" || $name == "phing.version") {
423
                    // basedir and phing.file get special treatment in main()
424 12
                    continue;
425
                }
426
                // don't re-set user properties, avoid the warning message
427 12
                if ($this->newProject->getProperty($name) === null) {
428
                    // no user property
429 12
                    $this->newProject->setNewProperty($name, $value);
430
                }
431
            }
432
        }
433 24
    }
434
435
    /**
436
     * Override the properties in the new project with the one
437
     * explicitly defined as nested elements here.
438
     *
439
     * @return void
440
     * @throws BuildException
441
     */
442 24
    private function overrideProperties()
443
    {
444 24
        foreach (array_keys($this->properties) as $i) {
445 11
            $p = $this->properties[$i];
446 11
            $p->setProject($this->newProject);
447 11
            $p->main();
448
        }
449 24
        $this->project->copyInheritedProperties($this->newProject);
450 24
    }
451
452
    /**
453
     * Add the references explicitly defined as nested elements to the
454
     * new project.  Also copy over all references that don't override
455
     * existing references in the new project if inheritrefs has been
456
     * requested.
457
     *
458
     * @return void
459
     * @throws BuildException
460
     */
461 22
    private function addReferences()
462
    {
463
464
        // parent project references
465 22
        $projReferences = $this->project->getReferences();
466
467 22
        $newReferences = $this->newProject->getReferences();
468
469 22
        $subprojRefKeys = [];
470
471 22
        if (count($this->references) > 0) {
472
            for ($i = 0, $count = count($this->references); $i < $count; $i++) {
473
                $ref = $this->references[$i];
474
                $refid = $ref->getRefId();
475
476
                if ($refid === null) {
477
                    throw new BuildException(
478
                        "the refid attribute is required"
479
                        . " for reference elements"
480
                    );
481
                }
482
                if (!isset($projReferences[$refid])) {
483
                    $this->log(
484
                        "Parent project doesn't contain any reference '"
485
                        . $refid . "'",
486
                        Project::MSG_WARN
487
                    );
488
                    continue;
489
                }
490
491
                $subprojRefKeys[] = $refid;
492
                //thisReferences.remove(refid);
493
                $toRefid = $ref->getToRefid();
494
                if ($toRefid === null) {
495
                    $toRefid = $refid;
496
                }
497
                $this->copyReference($refid, $toRefid);
498
            }
499
        }
500
501
        // Now add all references that are not defined in the
502
        // subproject, if inheritRefs is true
503 22
        if ($this->inheritRefs) {
504
            // get the keys that are were not used by the subproject
505 2
            $unusedRefKeys = array_diff(array_keys($projReferences), $subprojRefKeys);
506
507 2
            foreach ($unusedRefKeys as $key) {
508 2
                if (isset($newReferences[$key])) {
509 2
                    continue;
510
                }
511 2
                $this->copyReference($key, $key);
512
            }
513
        }
514 22
    }
515
516
    /**
517
     * Try to clone and reconfigure the object referenced by oldkey in
518
     * the parent project and add it to the new project with the key
519
     * newkey.
520
     *
521
     * <p>If we cannot clone it, copy the referenced object itself and
522
     * keep our fingers crossed.</p>
523
     *
524
     * @param  string $oldKey
525
     * @param  string $newKey
526
     * @throws BuildException
527
     * @return void
528
     */
529 2
    private function copyReference($oldKey, $newKey)
530
    {
531 2
        $orig = $this->project->getReference($oldKey);
532 2
        if ($orig === null) {
533
            $this->log(
534
                "No object referenced by " . $oldKey . ". Can't copy to "
535
                . $newKey,
536
                Project::MSG_WARN
537
            );
538
539
            return;
540
        }
541
542 2
        $copy = clone $orig;
543
544 2
        if ($copy instanceof ProjectComponent) {
545 2
            $copy->setProject($this->newProject);
546 2
        } elseif (in_array('setProject', get_class_methods(get_class($copy)))) {
547
            $copy->setProject($this->newProject);
548 2
        } elseif (!($copy instanceof Project)) {
549
            // don't copy the old "Project" itself
550
            $msg = "Error setting new project instance for "
551
                . "reference with id " . $oldKey;
552
            throw new BuildException($msg);
553
        }
554
555 2
        $this->newProject->addReference($newKey, $copy);
556 2
    }
557
558
    /**
559
     * If true, pass all properties to the new phing project.
560
     * Defaults to true.
561
     *
562
     * @param $value
563
     */
564 20
    public function setInheritAll($value)
565
    {
566 20
        $this->inheritAll = (bool) $value;
567 20
    }
568
569
    /**
570
     * If true, pass all references to the new phing project.
571
     * Defaults to false.
572
     *
573
     * @param $value
574
     */
575 12
    public function setInheritRefs($value)
576
    {
577 12
        $this->inheritRefs = (bool) $value;
578 12
    }
579
580
    /**
581
     * The directory to use as a base directory for the new phing project.
582
     * Defaults to the current project's basedir, unless inheritall
583
     * has been set to false, in which case it doesn't have a default
584
     * value. This will override the basedir setting of the called project.
585
     *
586
     * @param $d
587
     */
588 4
    public function setDir($d)
589
    {
590 4
        if (is_string($d)) {
591 4
            $this->dir = new PhingFile($d);
592
        } else {
593
            $this->dir = $d;
594
        }
595 4
    }
596
597
    /**
598
     * The build file to use.
599
     * Defaults to "build.xml". This file is expected to be a filename relative
600
     * to the dir attribute given.
601
     *
602
     * @param $s
603
     */
604 24
    public function setPhingFile($s)
605
    {
606
        // it is a string and not a file to handle relative/absolute
607
        // otherwise a relative file will be resolved based on the current
608
        // basedir.
609 24
        $this->phingFile = $s;
610 24
    }
611
612
    /**
613
     * Alias function for setPhingfile
614
     *
615
     * @param $s
616
     */
617
    public function setBuildfile($s)
618
    {
619
        $this->setPhingFile($s);
620
    }
621
622
    /**
623
     * The target of the new Phing project to execute.
624
     * Defaults to the new project's default target.
625
     *
626
     * @param string $s
627
     */
628 25
    public function setTarget(string $s)
629
    {
630 25
        if ('' === $s) {
631 1
            throw new BuildException("target attribute must not be empty");
632
        }
633
634 24
        $this->newTarget = $s;
635 24
    }
636
637
    /**
638
     * Set the filename to write the output to. This is relative to the value
639
     * of the dir attribute if it has been set or to the base directory of the
640
     * current project otherwise.
641
     * @param string $outputFile the name of the file to which the output should go.
642
     */
643 1
    public function setOutput(string $outputFile): void
644
    {
645 1
        $this->output = $outputFile;
646 1
    }
647
648
    /**
649
     * Property to pass to the new project.
650
     * The property is passed as a 'user property'
651
     */
652 11
    public function createProperty()
653
    {
654 11
        $p = new PropertyTask();
655 11
        $p->setFallback($this->getNewProject());
656 11
        $p->setUserProperty(true);
657 11
        $this->properties[] = $p;
658
659 11
        return $p;
660
    }
661
662
    /**
663
     * Reference element identifying a data type to carry
664
     * over to the new project.
665
     *
666
     * @param PhingReference $ref
667
     */
668 1
    public function addReference(PhingReference $ref)
669
    {
670 1
        $this->references[] = $ref;
671 1
    }
672
}
673