PhingTask::initializeProject()   F
last analyzed

Complexity

Conditions 10
Paths 816

Size

Total Lines 56
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 10.1228

Importance

Changes 0
Metric Value
eloc 31
dl 0
loc 56
ccs 25
cts 28
cp 0.8929
rs 3.7555
c 0
b 0
f 0
cc 10
nc 816
nop 0
crap 10.1228

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 Exception;
24
use Phing\Exception\BuildException;
25
use Phing\Io\File;
26
use Phing\Io\FileOutputStream;
27
use Phing\Io\FileUtils;
28
use Phing\Io\OutputStream;
29
use Phing\Listener\DefaultLogger;
30
use Phing\Parser\ProjectConfigurator;
31
use Phing\Phing;
32
use Phing\Project;
33
use Phing\ProjectComponent;
34
use Phing\Target;
35
use Phing\Task;
36
use Phing\Type\Element\FileSetAware;
37
use Phing\Type\Reference;
38
39
/**
40
 * Task that invokes phing on another build file.
41
 *
42
 * Use this task, for example, if you have nested buildfiles in your project. Unlike
43
 * AntTask, PhingTask can even support filesets:
44
 *
45
 * <pre>
46
 *   <phing>
47
 *    <fileset dir="${srcdir}">
48
 *      <include name="** /build.xml" /> <!-- space added after ** is there because of PHP comment syntax -->
49
 *      <exclude name="build.xml" />
50
 *    </fileset>
51
 *   </phing>
52
 * </pre>
53
 *
54
 * @author  Hans Lellelid <[email protected]>
55
 */
56
class PhingTask extends Task
57
{
58
    use FileSetAware;
59
60
    /**
61
     * the target to call if any.
62
     *
63
     * @var Target
64
     */
65
    protected $newTarget;
66
67
    /**
68
     * the basedir where is executed the build file.
69
     *
70
     * @var File
71
     */
72
    private $dir;
73
74
    /**
75
     * build.xml (can be absolute) in this case dir will be ignored.
76
     */
77
    private $phingFile;
78
79
    /**
80
     * should we inherit properties from the parent ?
81
     */
82
    private $inheritAll = true;
83
84
    /**
85
     * should we inherit references from the parent ?
86
     */
87
    private $inheritRefs = false;
88
89
    /**
90
     * the properties to pass to the new project.
91
     */
92
    private $properties = [];
93
94
    /**
95
     * the references to pass to the new project.
96
     *
97
     * @var PhingReference[]
98
     */
99
    private $references = [];
100
101
    /**
102
     * The temporary project created to run the build file.
103
     *
104
     * @var Project
105
     */
106
    private $newProject;
107
108
    /**
109
     * Fail the build process when the called build fails?
110
     */
111
    private $haltOnFailure = false;
112
113
    /**
114
     * Whether the basedir of the new project should be the same one
115
     * as it would be when running the build file directly -
116
     * independent of dir and/or inheritAll settings.
117
     */
118
    private $useNativeBasedir = false;
119
120
    /**
121
     * @var OutputStream
122
     */
123
    private $out;
124
125
    /** @var string */
126
    private $output;
127
128
    /**
129
     * @var array
130
     */
131
    private $locals;
132
133 40
    public function __construct(Task $owner = null)
134
    {
135 40
        if (null !== $owner) {
136 14
            $this->bindToOwner($owner);
137
        }
138 40
        parent::__construct();
139
    }
140
141
    /**
142
     *  If true, abort the build process if there is a problem with or in the target build file.
143
     *  Defaults to false.
144
     *
145
     * @param bool $hof new value
146
     */
147 18
    public function setHaltOnFailure($hof)
148
    {
149 18
        $this->haltOnFailure = (bool) $hof;
150
    }
151
152
    /**
153
     * Whether the basedir of the new project should be the same one
154
     * as it would be when running the build file directly -
155
     * independent of dir and/or inheritAll settings.
156
     */
157
    public function setUseNativeBasedir(bool $b)
158
    {
159
        $this->useNativeBasedir = $b;
160
    }
161
162
    /**
163
     * Creates a Project instance for the project to call.
164
     */
165 40
    public function init()
166
    {
167 40
        $this->newProject = $this->getProject()->createSubProject();
168 40
        $tdf = $this->project->getTaskDefinitions();
169 40
        $this->newProject->addTaskDefinition('property', $tdf['property']);
170
    }
171
172
    /**
173
     * Main entry point for the task.
174
     */
175 37
    public function main()
176
    {
177
        // Call Phing on the file set with the attribute "phingfile"
178 37
        if (null !== $this->phingFile || null !== $this->dir) {
179 37
            $this->processFile();
180
        }
181
182
        // if no filesets are given stop here; else process filesets
183 31
        if (!empty($this->filesets)) {
184
            // preserve old settings
185
            $savedDir = $this->dir;
186
            $savedPhingFile = $this->phingFile;
187
            $savedTarget = $this->newTarget;
188
189
            // set no specific target for files in filesets
190
            // [HL] I'm commenting this out; I don't know why this should not be supported!
191
            // $this->newTarget = null;
192
193
            foreach ($this->filesets as $fs) {
194
                $ds = $fs->getDirectoryScanner($this->project);
195
196
                $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...
197
                $srcFiles = $ds->getIncludedFiles();
198
199
                foreach ($srcFiles as $fname) {
200
                    $f = new File($ds->getbasedir(), $fname);
201
                    $f = $f->getAbsoluteFile();
202
                    $this->phingFile = $f->getAbsolutePath();
203
                    $this->dir = $f->getParentFile();
204
                    $this->processFile(); // run Phing!
205
                }
206
            }
207
208
            // side effect free programming ;-)
209
            $this->dir = $savedDir;
210
            $this->phingFile = $savedPhingFile;
211
            $this->newTarget = $savedTarget;
212
213
            // [HL] change back to correct dir
214
            if (null !== $this->dir) {
215
                chdir($this->dir->getAbsolutePath());
216
            }
217
        }
218
219
        // Remove any dangling references to help the GC
220 31
        foreach ($this->properties as $property) {
221 12
            $property->setFallback(null);
222
        }
223
    }
224
225
    /**
226
     * If true, pass all properties to the new phing project.
227
     * Defaults to true.
228
     *
229
     * @param bool $inheritAll
230
     */
231 21
    public function setInheritAll($inheritAll)
232
    {
233 21
        $this->inheritAll = (bool) $inheritAll;
234
    }
235
236
    /**
237
     * If true, pass all references to the new phing project.
238
     * Defaults to false.
239
     *
240
     * @param bool $inheritRefs
241
     */
242 13
    public function setInheritRefs($inheritRefs)
243
    {
244 13
        $this->inheritRefs = (bool) $inheritRefs;
245
    }
246
247
    /**
248
     * The directory to use as a base directory for the new phing project.
249
     * Defaults to the current project's basedir, unless inheritall
250
     * has been set to false, in which case it doesn't have a default
251
     * value. This will override the basedir setting of the called project.
252
     */
253 14
    public function setDir(File $dir): void
254
    {
255 14
        $this->dir = $dir;
256
    }
257
258
    /**
259
     * The build file to use.
260
     * Defaults to "build.xml". This file is expected to be a filename relative
261
     * to the dir attribute given.
262
     */
263 37
    public function setPhingFile(string $file)
264
    {
265
        // it is a string and not a file to handle relative/absolute
266
        // otherwise a relative file will be resolved based on the current
267
        // basedir.
268 37
        $this->phingFile = $file;
269
    }
270
271
    /**
272
     * Alias function for setPhingfile.
273
     *
274
     * @param string $file
275
     */
276 9
    public function setBuildfile($file)
277
    {
278 9
        $this->setPhingFile($file);
279
    }
280
281
    /**
282
     * The target of the new Phing project to execute.
283
     * Defaults to the new project's default target.
284
     */
285 35
    public function setTarget(string $target)
286
    {
287 35
        if ('' === $target) {
288 1
            throw new BuildException('target attribute must not be empty');
289
        }
290
291 34
        $this->newTarget = $target;
0 ignored issues
show
Documentation Bug introduced by
It seems like $target of type string is incompatible with the declared type Phing\Target of property $newTarget.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
292
    }
293
294
    /**
295
     * Set the filename to write the output to. This is relative to the value
296
     * of the dir attribute if it has been set or to the base directory of the
297
     * current project otherwise.
298
     *
299
     * @param string $outputFile the name of the file to which the output should go
300
     */
301 1
    public function setOutput(string $outputFile): void
302
    {
303 1
        $this->output = $outputFile;
304
    }
305
306
    /**
307
     * Property to pass to the new project.
308
     * The property is passed as a 'user property'.
309
     */
310 12
    public function createProperty()
311
    {
312 12
        $p = new PropertyTask();
313 12
        $p->setFallback($this->getNewProject());
314 12
        $p->setUserProperty(true);
315 12
        $p->setTaskName('property');
316 12
        $this->properties[] = $p;
317
318 12
        return $p;
319
    }
320
321
    /**
322
     * Reference element identifying a data type to carry
323
     * over to the new project.
324
     */
325 2
    public function addReference(PhingReference $ref)
326
    {
327 2
        $this->references[] = $ref;
328
    }
329
330
    /**
331
     * Get the (sub)-Project instance currently in use.
332
     */
333 37
    protected function getNewProject(): Project
334
    {
335 37
        if (null === $this->newProject) {
336 6
            $this->reinit();
337
        }
338
339 37
        return $this->newProject;
340
    }
341
342
    /**
343
     * Called in execute or createProperty if newProject is null.
344
     *
345
     * <p>This can happen if the same instance of this task is run
346
     * twice as newProject is set to null at the end of execute (to
347
     * save memory and help the GC).</p>
348
     *
349
     * <p>Sets all properties that have been defined as nested
350
     * property elements.</p>
351
     */
352 6
    private function reinit()
353
    {
354 6
        $this->init();
355
356 6
        $count = count($this->properties);
357 6
        for ($i = 0; $i < $count; ++$i) {
358
            /**
359
             * @var PropertyTask $p
360
             */
361 6
            $p = $this->properties[$i] ?? null;
362 6
            if (null !== $p) {
363
                /** @var PropertyTask $newP */
364 6
                $newP = $this->newProject->createTask('property');
365 6
                $newP->setName($p->getName());
366 6
                if (null !== $p->getValue()) {
367 6
                    $newP->setValue($p->getValue());
368
                }
369 6
                if (null !== $p->getFile()) {
370
                    $newP->setFile($p->getFile());
371
                }
372 6
                if (null !== $p->getPrefix()) {
373
                    $newP->setPrefix($p->getPrefix());
374
                }
375 6
                if (null !== $p->getRefid()) {
376
                    $newP->setRefid($p->getRefid());
377
                }
378 6
                if (null !== $p->getEnvironment()) {
379
                    $newP->setEnvironment($p->getEnvironment());
380
                }
381 6
                if (null !== $p->getUserProperty()) {
382 6
                    $newP->setUserProperty($p->getUserProperty());
383
                }
384 6
                $newP->setOverride($p->getOverride());
385 6
                $newP->setLogoutput($p->getLogoutput());
386 6
                $newP->setQuiet($p->getQuiet());
387
388 6
                $this->properties[$i] = $newP;
389
            }
390
        }
391
    }
392
393
    /**
394
     * Execute phing file.
395
     *
396
     * @throws BuildException
397
     */
398 37
    private function processFile(): void
399
    {
400 37
        $buildFailed = false;
401 37
        $buildFailedCause = null;
402 37
        $savedDir = $this->dir;
403 37
        $savedPhingFile = $this->phingFile;
404 37
        $savedTarget = $this->newTarget;
405
406 37
        $savedBasedirAbsPath = null; // this is used to save the basedir *if* we change it
407
408
        try {
409 37
            $this->getNewProject();
410
411 37
            $this->initializeProject();
412
413 37
            if (null !== $this->dir) {
414 14
                if (!$this->useNativeBasedir) {
415 14
                    $this->newProject->setBasedir($this->dir);
416 14
                    if (null !== $savedDir) { // has been set explicitly
417 14
                        $this->newProject->setInheritedProperty('project.basedir', $this->dir->getAbsolutePath());
418
                    }
419
                }
420
            } else {
421
                // Since we're not changing the basedir here (for file resolution),
422
                // we don't need to worry about any side-effects in this scanrio.
423 24
                $this->dir = $this->getProject()->getBasedir();
424
            }
425
426 37
            $this->overrideProperties();
427 37
            $this->phingFile = $this->phingFile ?? Phing::DEFAULT_BUILD_FILENAME;
428
429 37
            $fu = new FileUtils();
430 37
            $file = $fu->resolveFile($this->dir, $this->phingFile);
431 37
            $this->phingFile = $file->getAbsolutePath();
432 37
            $this->log('calling target(s) '
433 37
                . (empty($this->locals) ? '[default]' : implode(', ', $this->locals))
434 37
                . ' in build file ' . $this->phingFile, Project::MSG_VERBOSE);
435
436 37
            $this->newProject->setUserProperty('phing.file', $this->phingFile);
437
438 37
            if (empty($this->locals)) {
439 37
                $defaultTarget = $this->newProject->getDefaultTarget();
440 37
                if (!empty($defaultTarget)) {
441 37
                    $this->locals[] = $defaultTarget;
442
                }
443
            }
444
445 37
            $thisPhingFile = $this->getProject()->getProperty('phing.file');
446
            // Are we trying to call the target in which we are defined (or
447
            // the build file if this is a top level task)?
448
            if (
449 37
                null !== $thisPhingFile
450 37
                && null !== $this->getOwningTarget()
451 37
                && $thisPhingFile === $file->getPath()
452 37
                && '' === $this->getOwningTarget()->getName()
453
            ) {
454
                if ('phingcall' === $this->getTaskName()) {
455
                    throw new BuildException('phingcall must not be used at the top level.');
456
                }
457
458
                throw new BuildException(
459
                    sprintf(
460
                        '%s task at the top level must not invoke its own build file.',
461
                        $this->getTaskName()
462
                    )
463
                );
464
            }
465
466 37
            ProjectConfigurator::configureProject($this->newProject, new File($this->phingFile));
467
468 33
            if (null === $this->newTarget) {
469 3
                $this->newTarget = $this->newProject->getDefaultTarget();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->newProject->getDefaultTarget() of type string is incompatible with the declared type Phing\Target of property $newTarget.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
470
            }
471
472
            // Are we trying to call the target in which we are defined?
473
            if (
474 33
                $this->newProject->getBasedir()->equals($this->project->getBasedir())
475 33
                && $this->newProject->getProperty('phing.file') === $this->project->getProperty('phing.file')
476 33
                && null !== $this->getOwningTarget()
477
            ) {
478 19
                $owningTargetName = $this->getOwningTarget()->getName();
479 19
                if ($this->newTarget === $owningTargetName) {
480 2
                    throw new BuildException(
481 2
                        sprintf(
482 2
                            '%s task calling its own parent target',
483 2
                            $this->getTaskName()
484 2
                        )
485 2
                    );
486
                }
487
488 17
                $targets = $this->getProject()->getTargets();
489 17
                $taskName = $this->getTaskName();
490
491 17
                foreach ($this->locals as $local) {
492 17
                    if (isset($targets[$local])) {
493 9
                        if ($targets[$local]->dependsOn($owningTargetName)) {
494
                            throw new BuildException(
495
                                sprintf(
496
                                    "%s task calling a target that depends on its parent target '%s'.",
497
                                    $taskName,
498
                                    $owningTargetName
499
                                )
500
                            );
501
                        }
502
                    }
503
                }
504
            }
505
506 31
            $this->addReferences();
507 31
            $this->newProject->executeTarget($this->newTarget);
508 9
        } catch (Exception $e) {
509 9
            $buildFailed = true;
510 9
            $buildFailedCause = $e;
511 9
            $this->log('[' . get_class($e) . '] ' . $e->getMessage(), Project::MSG_ERR);
512 9
            if (Phing::getMsgOutputLevel() <= Project::MSG_DEBUG) {
513 9
                $lines = explode("\n", $e->getTraceAsString());
514 9
                foreach ($lines as $line) {
515 9
                    $this->log($line, Project::MSG_DEBUG);
516
                }
517
            }
518
        } finally {
519
            // reset environment values to prevent side-effects.
520
521 37
            $this->newProject = null;
522 37
            $pkeys = array_keys($this->properties);
523 37
            foreach ($pkeys as $k) {
524 12
                $this->properties[$k]->setProject(null);
525
            }
526
527 37
            if (null !== $this->output && null !== $this->out) {
528 1
                $this->out->close();
529
            }
530
531 37
            $this->dir = $savedDir;
532 37
            $this->phingFile = $savedPhingFile;
533 37
            $this->newTarget = $savedTarget;
534
535
            // If the basedir for any project was changed, we need to set that back here.
536 37
            if (null !== $savedBasedirAbsPath) {
0 ignored issues
show
introduced by
The condition null !== $savedBasedirAbsPath is always false.
Loading history...
537
                chdir($savedBasedirAbsPath);
538
            }
539
540 37
            if ($this->haltOnFailure && $buildFailed) {
541 37
                throw new BuildException('Execution of the target buildfile failed. Aborting.', $buildFailedCause);
542
            }
543
        }
544
    }
545
546
    /**
547
     * Configure the Project, i.e. make intance, attach build listeners
548
     * (copy from father project), add Task and Datatype definitions,
549
     * copy properties and references from old project if these options
550
     * are set via the attributes of the XML tag.
551
     *
552
     * Developer note:
553
     * This function replaces the old methods "init", "_reinit" and
554
     * "_initializeProject".
555
     */
556 37
    private function initializeProject()
557
    {
558 37
        $this->newProject->setInputHandler($this->project->getInputHandler());
559
560 37
        foreach ($this->project->getBuildListeners() as $listener) {
561 37
            $this->newProject->addBuildListener($listener);
562
        }
563
564
        /* Copy things from old project. Datatypes and Tasks are always
565
         * copied, properties and references only if specified so/not
566
         * specified otherwise in the XML definition.
567
         */
568
        // Add Datatype definitions
569 37
        foreach ($this->project->getDataTypeDefinitions() as $typeName => $typeClass) {
570 37
            $this->newProject->addDataTypeDefinition($typeName, $typeClass);
571
        }
572
573
        // Add Task definitions
574 37
        foreach ($this->project->getTaskDefinitions() as $taskName => $taskClass) {
575 37
            if ('Phing\Task\System\PropertyTask' === $taskClass) {
576
                // we have already added this taskdef in init()
577 37
                continue;
578
            }
579 37
            $this->newProject->addTaskDefinition($taskName, $taskClass);
580
        }
581
582 37
        if (null !== $this->output) {
583
            try {
584 1
                if (null !== $this->dir) {
585 1
                    $outfile = (new FileUtils())->resolveFile($this->dir, $this->output);
586
                } else {
587 1
                    $outfile = $this->getProject()->resolveFile($this->output);
588
                }
589 1
                $this->out = new FileOutputStream($outfile);
590 1
                $logger = new DefaultLogger();
591 1
                $logger->setMessageOutputLevel(Project::MSG_INFO);
592 1
                $logger->setOutputStream($this->out);
593 1
                $logger->setErrorStream($this->out);
594 1
                $this->newProject->addBuildListener($logger);
595
            } catch (Exception $ex) {
596
                $this->log("Phing: Can't set output to " . $this->output);
597
            }
598
        }
599
600 37
        if ($this->useNativeBasedir) {
601
            $this->addAlmostAll($this->getProject()->getUserProperties(), 'user');
602
        } else {
603 37
            $this->project->copyUserProperties($this->newProject);
604
        }
605
606 37
        if (!$this->inheritAll) {
607
            // set System built-in properties separately,
608
            // b/c we won't inherit them.
609 13
            $this->newProject->setSystemProperties();
610
        } else {
611 25
            $this->addAlmostAll($this->getProject()->getProperties(), 'plain');
612
        }
613
    }
614
615
    /**
616
     * Copies all properties from the given table to the new project -
617
     * omitting those that have already been set in the new project as
618
     * well as properties named basedir or phing.file.
619
     *
620
     * @param array  $props properties <code>Hashtable</code> to copy to the
621
     *                      new project
622
     * @param string $type  the type of property to set (a plain Phing property, a
623
     *                      user property or an inherited property)
624
     */
625 25
    private function addAlmostAll(array $props, string $type): void
626
    {
627 25
        foreach ($props as $name => $value) {
628 25
            if ('basedir' === $name || 'phing.file' === $name || 'phing.version' === $name) {
629
                // basedir and phing.file get special treatment in main()
630 25
                continue;
631
            }
632 25
            if ('plain' === $type) {
633
                // don't re-set user properties, avoid the warning message
634 25
                if (null === $this->newProject->getProperty($name)) {
635
                    // no user property
636 25
                    $this->newProject->setNewProperty($name, $value);
637
                }
638
            } elseif ('user' === $type) {
639
                $this->newProject->setUserProperty($name, $value);
640
            } elseif ('inherited' === $type) {
641
                $this->newProject->setInheritedProperty($name, $value);
642
            }
643
        }
644
    }
645
646
    /**
647
     * Override the properties in the new project with the one
648
     * explicitly defined as nested elements here.
649
     *
650
     * @throws BuildException
651
     */
652 37
    private function overrideProperties()
653
    {
654
        // remove duplicate properties - last property wins
655 37
        $properties = array_reverse($this->properties);
656 37
        $set = [];
657 37
        foreach ($properties as $i => $p) {
658 12
            if (null !== $p->getName() && '' !== $p->getName()) {
659 12
                if (in_array($p->getName(), $set)) {
660 7
                    unset($this->properties[$i]);
661
                } else {
662 12
                    $set[] = $p->getName();
663
                }
664
            }
665 12
            $p->setProject($this->newProject);
666 12
            $p->main();
667
        }
668 37
        if ($this->useNativeBasedir) {
669
            $this->addAlmostAll($this->getProject()->getInheritedProperties(), 'inherited');
670
        } else {
671 37
            $this->project->copyInheritedProperties($this->newProject);
672
        }
673
    }
674
675
    /**
676
     * Add the references explicitly defined as nested elements to the
677
     * new project.  Also copy over all references that don't override
678
     * existing references in the new project if inheritrefs has been
679
     * requested.
680
     *
681
     * @throws BuildException
682
     */
683 31
    private function addReferences()
684
    {
685
        // parent project references
686 31
        $projReferences = $this->project->getReferences();
687
688 31
        $newReferences = $this->newProject->getReferences();
689
690 31
        $subprojRefKeys = [];
691
692 31
        if (count($this->references) > 0) {
693 1
            for ($i = 0, $count = count($this->references); $i < $count; ++$i) {
694 1
                $ref = $this->references[$i];
695 1
                $refid = $ref->getRefId();
696
697 1
                if (null === $refid) {
698
                    throw new BuildException('the refid attribute is required for reference elements');
699
                }
700 1
                if (!isset($projReferences[$refid])) {
701
                    $this->log("Parent project doesn't contain any reference '" . $refid . "'", Project::MSG_WARN);
702
703
                    continue;
704
                }
705
706 1
                $subprojRefKeys[] = $refid;
707 1
                unset($this->references[$i]); //thisReferences.remove(refid);
708 1
                $toRefid = $ref->getToRefid();
709 1
                if (null === $toRefid) {
710 1
                    $toRefid = $refid;
711
                }
712 1
                $this->copyReference($refid, $toRefid);
713
            }
714
        }
715
716
        // Now add all references that are not defined in the
717
        // subproject, if inheritRefs is true
718 31
        if ($this->inheritRefs) {
719
            // get the keys that are were not used by the subproject
720 2
            $unusedRefKeys = array_diff(array_keys($projReferences), $subprojRefKeys);
721
722 2
            foreach ($unusedRefKeys as $key) {
723 2
                if (isset($newReferences[$key])) {
724 2
                    continue;
725
                }
726 2
                $this->copyReference($key, $key);
727
            }
728
        }
729
    }
730
731
    /**
732
     * Try to clone and reconfigure the object referenced by oldkey in
733
     * the parent project and add it to the new project with the key
734
     * newkey.
735
     *
736
     * <p>If we cannot clone it, copy the referenced object itself and
737
     * keep our fingers crossed.</p>
738
     *
739
     * @param string $oldKey
740
     * @param string $newKey
741
     *
742
     * @throws BuildException
743
     */
744 3
    private function copyReference($oldKey, $newKey)
745
    {
746 3
        $orig = $this->project->getReference($oldKey);
747 3
        if (null === $orig) {
748
            $this->log(
749
                'No object referenced by ' . $oldKey . ". Can't copy to "
750
                . $newKey,
751
                Project::MSG_WARN
752
            );
753
754
            return;
755
        }
756
757 3
        $copy = clone $orig;
758
759 3
        if ($copy instanceof ProjectComponent) {
760 3
            $copy->setProject($this->newProject);
761 2
        } elseif (in_array('setProject', get_class_methods(get_class($copy)))) {
762
            $copy->setProject($this->newProject);
763 2
        } elseif (!($copy instanceof Project)) {
764
            // don't copy the old "Project" itself
765
            $msg = 'Error setting new project instance for '
766
                . 'reference with id ' . $oldKey;
767
768
            throw new BuildException($msg);
769
        }
770
771 3
        $this->newProject->addReference($newKey, $copy);
772
    }
773
}
774