Passed
Push — master ( eabf33...342b63 )
by Michiel
05:53
created

ApplyTask::logSkippingFileset()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 4
nop 3
dl 0
loc 11
ccs 0
cts 7
cp 0
crap 20
rs 10
c 0
b 0
f 0
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
use Phing\Exception\BuildException;
21
use Phing\Exception\NullPointerException;
22
use Phing\Io\DirectoryScanner;
23
use Phing\Io\FileUtils;
24
use Phing\Io\IOException;
25
use Phing\Io\File;
26
use Phing\Io\SourceFileScanner;
27
use Phing\Phing;
28
use Phing\Project;
29
use Phing\Type\Commandline;
30
use Phing\Type\CommandlineMarker;
31
use Phing\Type\DirSet;
32
use Phing\Type\FileList;
33
use Phing\Type\Mapper;
34
35
/**
36
 * Executes a command on the (filtered) file list/set.
37
 * (Loosely based on the "Ant Apply" task - http://ant.apache.org/manual/Tasks/apply.html)
38
 *
39
 * @author  Utsav Handa <handautsav at hotmail dot com>
40
 * @package phing.tasks.system
41
 *
42
 * @todo Add support for mapper, targetfile expressions
43
 */
44
class ApplyTask extends ExecTask
45
{
46
    use ResourceAware;
47
48
    public const SOURCEFILE_ID = '__SOURCEFILE__';
49
    protected $currentdirectory;
50
51
    /**
52
     * Whether output should be appended to or overwrite an existing file
53
     *
54
     * @var boolean
55
     */
56
    protected $appendoutput = false;
57
58
    /**
59
     * Runs the command only once, appending all files as arguments
60
     * else command will be executed once for every file.
61
     *
62
     * @var boolean
63
     */
64
    protected $parallel = false;
65
66
    /**
67
     * Whether source file name should be added to the end of command automatically
68
     *
69
     * @var boolean
70
     */
71
    protected $addsourcefile = true;
72
73
    /**
74
     * Whether the filenames should be passed on the command line as relative pathnames (relative to the base directory of the corresponding fileset/list)
75
     *
76
     * @var boolean
77
     */
78
    protected $relative = false;
79
80
    protected $currentos;
81
    protected $osvariant;
82
83
    /**
84
     * Logging level for status messages
85
     *
86
     * @var integer
87
     */
88
    protected $loglevel = null;
89
90
    /**
91
     * Whether to use forward-slash as file-separator on the file names
92
     *
93
     * @var boolean
94
     */
95
    protected $forwardslash = false;
96
97
    /**
98
     * Limit the amount of parallelism by passing at most this many sourcefiles at once
99
     * (Set it to <= 0 for unlimited)
100
     *
101
     * @var integer
102
     */
103
    protected $maxparallel = 0;
104
105
    protected static $types = [
106
        'FILE' => 'file',
107
        'DIR' => 'dir',
108
        'BOTH' => 'both',
109
    ];
110
111
    protected $type = 'file';
112
    /**
113
     * @var CommandlineMarker $targetFilePos
114
     */
115
    protected $targetFilePos;
116
    /**
117
     * @var CommandlineMarker $srcFilePos
118
     */
119
    protected $srcFilePos;
120
    protected $srcIsFirst = true;
121
122
    protected $skipEmpty = false;
123
    private $force = false;
124
    private $mapper;
125
    private $destDir;
126
127
    /**
128
     * @var Mapper $mapperElement
129
     */
130
    private $mapperElement;
131
    private $additionalCmds;
132
133
    /**
134
     * Set whether empty filesets will be skipped.  If true and
135
     * no source files have been found or are newer than their
136
     * corresponding target files, the command will not be run.
137
     *
138
     * @param boolean $skip whether to skip empty filesets.
139
     */
140
    public function setSkipEmptyFilesets(bool $skip)
141
    {
142
        $this->skipEmpty = $skip;
143
    }
144
145
    /**
146
     * Specify the directory where target files are to be placed.
147
     *
148
     * @param File $dest the File object representing the destination directory.
149
     */
150 1
    public function setDest(File $dest)
151
    {
152 1
        $this->destDir = $dest;
153 1
    }
154
155
    /**
156
     * File to which output should be written
157
     *
158
     * @param    $append
159
     * @internal param PhingFile $outputfile Output log file
160
     *
161
     * @return void
162
     */
163 3
    public function setAppend(bool $append)
164
    {
165 3
        $this->appendoutput = $append;
166 3
    }
167
168
    /**
169
     * Run the command only once, appending all files as arguments
170
     *
171
     * @param Boolean $parallel Identifier for files as arguments appending
172
     *
173
     * @return void
174
     */
175 15
    public function setParallel(bool $parallel)
176
    {
177 15
        $this->parallel = $parallel;
178 15
    }
179
180
    /**
181
     * To add the source filename at the end of command of automatically
182
     *
183
     * @param Boolean $addsourcefile Identifier for adding source file at the end of command
184
     *
185
     * @return void
186
     */
187 19
    public function setAddsourcefile(bool $addsourcefile)
188
    {
189 19
        $this->addsourcefile = $addsourcefile;
190 19
    }
191
192
    /**
193
     * Whether the filenames should be passed on the command line as relative
194
     * pathnames (relative to the base directory of the corresponding fileset/list)
195
     *
196
     * @param    $relative
197
     * @internal param bool $escape Escape command before execution
198
     *
199
     * @return void
200
     */
201 3
    public function setRelative(bool $relative)
202
    {
203 3
        $this->relative = $relative;
204 3
    }
205
206
    /**
207
     * Fail on command exits with a returncode other than zero
208
     *
209
     * @param boolean $failonerror Indicator to fail on error
210
     *
211
     * @return void
212
     */
213 1
    public function setFailonerror(bool $failonerror)
214
    {
215 1
        $this->checkreturn = $failonerror;
216 1
    }
217
218
    /**
219
     * Whether to use forward-slash as file-separator on the file names
220
     *
221
     * @param boolean $forwardslash Indicator to use forward-slash
222
     *
223
     * @return void
224
     */
225 1
    public function setForwardslash(bool $forwardslash)
226
    {
227 1
        $this->forwardslash = $forwardslash;
228 1
    }
229
230
    /**
231
     * Limit the amount of parallelism by passing at most this many sourcefiles at once
232
     *
233
     * @param    $max
234
     * @internal param bool $forwardslash Indicator to use forward-slash
235
     *
236
     * @return void
237
     */
238 1
    public function setMaxparallel($max)
239
    {
240 1
        $this->maxparallel = (int) $max;
241 1
    }
242
243
    public function setForce(bool $force)
244
    {
245
        $this->force = $force;
246
    }
247
248
    /**
249
     * Set whether the command works only on files, directories or both.
250
     *
251
     * @param string $type a FileDirBoth EnumeratedAttribute.
252
     */
253
    public function setType(string $type)
254
    {
255
        $this->type = $type;
256
    }
257
258
    /**
259
     * Supports embedded <targetfile> element.
260
     *
261
     * @return CommandlineMarker
262
     * @throws \Phing\Exception\BuildException
263
     */
264 1
    public function createTargetfile()
265
    {
266 1
        if ($this->targetFilePos !== null) {
267
            throw new BuildException(
268
                $this->getTaskType() . " doesn\'t support multiple "
269
                . "targetfile elements.",
270
                $this->getLocation()
271
            );
272
        }
273
274 1
        $this->targetFilePos = $this->commandline->createMarker();
275 1
        $this->srcIsFirst = ($this->srcFilePos !== null);
276
277 1
        return $this->targetFilePos;
278
    }
279
280
    /**
281
     * Supports embedded <srcfile> element.
282
     *
283
     * @return CommandlineMarker
284
     * @throws \Phing\Exception\BuildException
285
     */
286 1
    public function createSrcfile()
287
    {
288 1
        if ($this->srcFilePos !== null) {
289
            throw new BuildException(
290
                $this->getTaskType() . " doesn\'t support multiple "
291
                . "srcfile elements.",
292
                $this->getLocation()
293
            );
294
        }
295
296 1
        $this->srcFilePos = $this->commandline->createMarker();
297 1
        return $this->srcFilePos;
298
    }
299
300
    /**
301
     * @return Mapper
302
     * @throws \Phing\Exception\BuildException
303
     */
304 1
    public function createMapper()
305
    {
306 1
        if ($this->mapperElement !== null) {
307
            throw new BuildException(
308
                'Cannot define more than one mapper',
309
                $this->getLocation()
310
            );
311
        }
312 1
        $this->mapperElement = new Mapper($this->getProject());
313 1
        return $this->mapperElement;
314
    }
315
316
    /**********************************************************************************/
317
    /**************************** T A S K  M E T H O D S ******************************/
318
    /**********************************************************************************/
319
320
    /**
321
     * Do work
322
     *
323
     * @throws \Phing\Exception\BuildException
324
     */
325 21
    public function main()
326
    {
327
        try {
328
            // Log
329 21
            $this->log('Started ', $this->loglevel);
330
            // Initialize //
331 21
            $this->prepare();
332 19
            $haveExecuted = false;
333
            // Validate O.S. applicability
334 19
            if ($this->isValidOs()) {
335
                // Build the command //
336 18
                $this->buildCommand();
337
                // Process //
338 18
                $totalFiles = 0;
339 18
                $totalDirs = 0;
340 18
                $fileNames = [];
341
                // - FileSets
342 18
                foreach ($this->filesets as $fs) {
343 18
                    $currentType = $this->type;
344 18
                    if ($fs instanceof DirSet) {
345
                        if ($this->type !== self::$types['DIR']) {
346
                            $this->log(
347
                                "Found a nested dirset but type is $this->type . "
348
                                . "Temporarily switching to type=\"dir\" on the"
349
                                . " assumption that you really did mean"
350
                                . " <dirset> not <fileset>.",
351
                                Project::MSG_DEBUG
352
                            );
353
                            $currentType = 'dir';
354
                        }
355
                    }
356 18
                    $base = $fs->getDir($this->project);
357 18
                    $ds = $fs->getDirectoryScanner($this->project);
358 18
                    if ($currentType !== self::$types['DIR']) {
359 18
                        $s = $this->getFiles($base, $ds);
360 18
                        foreach ($s as $fileName) {
361 18
                            $totalFiles++;
362 18
                            $fileNames[] = $fileName;
363 18
                            $baseDirs[] = $base;
364
                        }
365
                    }
366 18
                    if ($currentType !== self::$types['FILE']) {
367
                        $s = $this->getDirs($base, $ds);
368
                        foreach ($s as $fileName) {
369
                            $totalDirs++;
370
                            $fileNames[] = $fileName;
371
                            $baseDirs[] = $base;
372
                        }
373
                    }
374 18
                    if (count($fileNames) === 0 && $this->skipEmpty) {
375
                        $this->logSkippingFileset($currentType, $ds, $base);
376
                        continue;
377
                    }
378 18
                    $this->process(
379 18
                        $fs->getDirectoryScanner($this->project)->getIncludedFiles(),
380 18
                        $fs->getDir($this->project)
381
                    );
382 17
                    $haveExecuted = true;
383
                }
384 17
                unset($this->filesets);
385
                // - FileLists
386
                /**
387
                 * @var FileList $fl
388
                 */
389 17
                foreach ($this->filelists as $fl) {
390
                    $totalFiles++;
391
                    $this->process($fl->getFiles($this->project), $fl->getDir($this->project));
392
                    $haveExecuted = true;
393
                }
394 17
                unset($this->filelists);
395
            }
396 18
            if ($haveExecuted) {
397 17
                $this->log(
398 17
                    "Applied " . $this->commandline->getExecutable() . " to "
399 17
                    . $totalFiles . " file"
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $totalFiles does not seem to be defined for all execution paths leading up to this point.
Loading history...
400 17
                    . ($totalFiles !== 1 ? "s" : "") . " and "
401 17
                    . $totalDirs . " director"
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $totalDirs does not seem to be defined for all execution paths leading up to this point.
Loading history...
402 17
                    . ($totalDirs !== 1 ? "ies" : "y") . ".",
403 17
                    $this->loglevel
404
                );
405
            }
406
            /// Cleanup //
407 18
            $this->cleanup();
408
            // Log
409 18
            $this->log('End ', $this->loglevel);
410 3
        } catch (IOException | NullPointerException | UnexpectedValueException $e) {
411
            throw new BuildException('Execute failed: ' . $e, $e, $this->getLocation());
412
        }
413 18
    }
414
415
    /**********************************************************************************/
416
    /********************** T A S K  C O R E  M E T H O D S ***************************/
417
    /**********************************************************************************/
418
419 18
    protected function getFiles(File $baseDir, DirectoryScanner $ds)
420
    {
421 18
        return $this->restrict($ds->getIncludedFiles(), $baseDir);
422
    }
423
424
    protected function getDirs(File $baseDir, DirectoryScanner $ds)
425
    {
426
        return $this->restrict($ds->getIncludedDirectories(), $baseDir);
427
    }
428
429 18
    protected function restrict($s, File $baseDir)
430
    {
431 18
        $sfs = new SourceFileScanner($this);
432 18
        return ($this->mapper === null || $this->force)
433 17
            ? $s
434 18
            : $sfs->restrict($s, $baseDir, $this->destDir, $this->mapper);
435
    }
436
437
    private function logSkippingFileset($currentType, DirectoryScanner $ds, File $base)
438
    {
439
        $includedCount = (
440
            ($currentType !== self::$types['DIR']) ? $ds->getIncludedFilesCount() : 0
441
            ) + (
442
            ($currentType !== self::$types['FILES']) ? $ds->getIncludedDirectoriesCount() : 0
443
            );
444
        $this->log(
445
            "Skipping fileset for directory " . $base . ". It is "
446
            . (($includedCount > 0) ? "up to date." : "empty."),
447
            $this->loglevel
448
        );
449
    }
450
451
    /**
452
     * Initializes the task operations, i.e.
453
     * - Required information validation
454
     * - Working directory
455
     *
456
     * @return void
457
     * @throws \Phing\Exception\BuildException
458
     * @throws IOException
459
     */
460 21
    protected function prepare()
461
    {
462
        // Log
463 21
        $this->log('Initializing started ', $this->loglevel);
464
465
        ///// Validating the required parameters /////
466
467 21
        if (!in_array($this->type, self::$types)) {
468
            throw new BuildException('Type must be one of \'file\', \'dir\' or \'both\'.');
469
        }
470
471
        // Executable
472 21
        if ($this->commandline->getExecutable() === null) {
0 ignored issues
show
introduced by
The condition $this->commandline->getExecutable() === null is always false.
Loading history...
473 1
            $this->throwBuildException('Please provide "executable" information');
474
        }
475
476
        // Retrieving the current working directory
477 20
        $this->currentdirectory = getcwd();
478
479
        // Directory (in which the command should be executed)
480 20
        if ($this->dir !== null) {
481
            // Try expanding (any) symbolic links
482 4
            if (!$this->dir->getCanonicalFile()->isDirectory()) {
483 1
                $this->throwBuildException("'" . $this->dir . "' is not a valid directory");
484
            }
485
486
            // Change working directory
487 3
            $dirchangestatus = @chdir($this->dir->getPath());
488
489
            // Log
490 3
            $this->log(
491 3
                'Working directory change ' . ($dirchangestatus ? 'successful' : 'failed') . ' to ' . $this->dir->getPath(),
492 3
                $this->loglevel
493
            );
494
        }
495
496
        ///// Preparing the task environment /////
497
498
        // Getting current operationg system
499 19
        $this->currentos = Phing::getProperty('os.name');
500
501
        // Log
502 19
        $this->log('Operating System identified : ' . $this->currentos, $this->loglevel);
503
504
        // Getting the O.S. type identifier
505
        // Validating the 'filesystem' for determining the OS type [UNIX, WINNT and WIN32]
506
        // (Another usage could be with 'os.name' for determination)
507 19
        if ('WIN' === strtoupper(substr(Phing::getProperty('host.fstype'), 0, 3))) {
508
            $this->osvariant = 'WIN'; // Probable Windows flavour
509
        } else {
510 19
            $this->osvariant = 'LIN'; // Probable GNU/Linux flavour
511
        }
512
513
        // Log
514 19
        $this->log('Operating System variant identified : ' . $this->osvariant, $this->loglevel);
515
516 19
        if (count($this->filesets) === 0 && count($this->filelists) === 0 && count($this->getDirSets()) === 0) {
517
            throw new BuildException(
518
                "no resources specified",
519
                $this->getLocation()
520
            );
521
        }
522 19
        if ($this->targetFilePos !== null && $this->mapperElement === null) {
523
            throw new BuildException(
524
                "targetfile specified without mapper",
525
                $this->getLocation()
526
            );
527
        }
528 19
        if ($this->destDir !== null && $this->mapperElement === null) {
529
            throw new BuildException(
530
                "dest specified without mapper",
531
                $this->getLocation()
532
            );
533
        }
534
535 19
        if ($this->mapperElement !== null) {
536 1
            $this->mapper = $this->mapperElement->getImplementation();
537 1
            $this->log('Mapper identified : ' . get_class($this->mapper), $this->loglevel);
538
        }
539
540 19
        $this->commandline->setEscape($this->escape);
541
542
        // Log
543 19
        $this->log('Initializing completed ', $this->loglevel);
544 19
    }
545
546
    /**
547
     * Builds the full command to execute and stores it in $realCommand.
548
     *
549
     * @return void
550
     *
551
     * @throws \Phing\Exception\BuildException
552
     */
553 18
    protected function buildCommand()
554
    {
555
556
        // Log
557 18
        $this->log('Command building started ', $this->loglevel);
558
559
        // Building the executable
560 18
        $this->realCommand = (string) $this->commandline;
561
562 18
        $this->additionalCmds = '';
563
564
        // Adding the source filename at the end of command, validating the existing
565
        // sourcefile position explicit mentioning
566 18
        if ($this->addsourcefile === true) {
567 1
            $this->realCommand .= ' ' . self::SOURCEFILE_ID;
568
        }
569
570
        // Setting command output redirection with content appending
571 18
        if ($this->output !== null) {
572 3
            $this->additionalCmds .= sprintf(
573 3
                ' 1>%s %s',
574 3
                $this->appendoutput ? '>' : '',
575 3
                escapeshellarg($this->output->getPath())
576
            );
577 15
        } elseif ($this->spawn) { // Validating the 'spawn' configuration, and redirecting the output to 'null'
578 1
            $this->additionalCmds .= sprintf(' %s', 'WIN' === $this->osvariant ? '> NUL' : '1>/dev/null');
579
580 1
            $this->log("For process spawning, setting Output nullification ", $this->loglevel);
581
        }
582
583
        // Setting command error redirection with content appending
584 18
        if ($this->error !== null) {
585 1
            $this->additionalCmds .= sprintf(
586 1
                ' 2>%s %s',
587 1
                $this->appendoutput ? '>' : '',
588 1
                escapeshellarg($this->error->getPath())
589
            );
590
        }
591
592
        // Setting the execution as a background process
593 18
        if ($this->spawn) {
594
            // Validating the O.S. variant
595 1
            if ('WIN' === $this->osvariant) {
596
                $this->additionalCmds = 'start /b ' . $this->additionalCmds; // MS Windows background process forking
597
            } else {
598 1
                $this->additionalCmds .= ' &'; // GNU/Linux background process forking
599
            }
600
        }
601
602 18
        $this->additionalCmds = rtrim($this->additionalCmds);
603
604
        // Log
605 18
        $this->log('Command built : ' . $this->realCommand . $this->additionalCmds, $this->loglevel);
606
607
        // Log
608 18
        $this->log('Command building completed ', $this->loglevel);
609 18
    }
610
611
    /**
612
     * Processes the files list with provided information for execution
613
     *
614
     * @param array $srcFiles File list for processing
615
     * @param string $basedir Base directory of the file list
616
     *
617
     * @return void
618
     * @throws \Phing\Exception\BuildException
619
     * @throws IOException
620
     * @throws NullPointerException
621
     */
622 18
    private function process($srcFiles, $basedir)
623
    {
624
        // Log
625 18
        $this->log("Processing files with base directory ($basedir) ", $this->loglevel);
626 18
        $targets = [];
627 18
        if ($this->targetFilePos !== null) {
628 1
            $addedFiles = [];
629 1
            foreach ($srcFiles as $count => $file) {
630 1
                if ($this->mapper !== null) {
631 1
                    $subTargets = $this->mapper->main($file);
632 1
                    if ($subTargets !== null) {
633 1
                        foreach ($subTargets as $subTarget) {
634 1
                            if ($this->relative) {
635
                                $name = $subTarget;
636
                            } else {
637 1
                                $name = (new File($this->destDir, $subTarget))->getAbsolutePath();
638
                            }
639 1
                            if ($this->forwardslash && FileUtils::getSeparator() !== '/') {
640
                                $name = str_replace(FileUtils::getSeparator(), '/', $name);
641
                            }
642 1
                            if (!isset($addedFiles[$name])) {
643 1
                                $targets[] = $name;
644 1
                                $addedFiles[] = $name;
645
                            }
646
                        }
647
                    }
648
                }
649
            }
650
        }
651 18
        $targetFiles = $targets;
652
653 18
        if (!$this->addsourcefile) {
654 17
            $srcFiles = [];
655
        }
656 18
        $orig = $this->commandline->getCommandline();
657 18
        $result = []; // range(0,count($orig) + count($srcFiles) + count($targetFiles));
658
659 18
        $srcIndex = count($orig);
660 18
        if ($this->srcFilePos !== null) {
661 1
            $srcIndex = $this->srcFilePos->getPosition();
662
        }
663 18
        if ($this->targetFilePos !== null) {
664 1
            $targetIndex = $this->targetFilePos->getPosition();
665
666 1
            if ($srcIndex < $targetIndex || ($srcIndex === $targetIndex && $this->srcIsFirst)) {
667
                // 0 --> srcIndex
668
                $result[] = $orig;
669
670
                // srcIndex --> targetIndex
671
                $result += array_slice($orig, $srcIndex + count($srcFiles), $targetIndex - $srcIndex, true);
672
673
                $result[] = $orig;
674
                $result[] = $targetFiles;
675
                $result = array_merge(... $result);
676
677
                // targetIndex --> end
678
                $result = array_merge(
679
                    array_slice(
680
                        $orig,
681
                        $targetIndex + count($srcFiles) + count($targetFiles),
682
                        count($orig) - $targetIndex,
683
                        true
684
                    ),
685
                    $result
686
                );
687
            } else {
688
                // 0 --> targetIndex
689 1
                $result[] = $orig;
690 1
                $result[] = $targetFiles;
691 1
                $result = array_merge(... $result);
692
693
                // targetIndex --> srcIndex
694 1
                $result = array_merge(
695 1
                    array_slice(
696 1
                        $orig,
697 1
                        $targetIndex + count($targetFiles),
698 1
                        $srcIndex - $targetIndex,
699 1
                        true
700
                    ),
701 1
                    $result
702
                );
703
704
                // srcIndex --> end
705 1
                $result = array_merge(
706 1
                    array_slice(
707 1
                        $orig,
708 1
                        $srcIndex + count($srcFiles) + count($targetFiles),
709 1
                        count($orig) - $srcIndex,
710 1
                        true
711
                    ),
712 1
                    $result
713
                );
714 1
                $srcIndex += count($targetFiles);
715
            }
716
        } else { // no targetFilePos
717
            // 0 --> srcIndex
718 17
            $result = array_merge(array_slice($orig, 0, $srcIndex, true), $result);
719
            // srcIndex --> end
720 17
            $result = array_merge(
721 17
                array_slice($orig, $srcIndex + count($srcFiles), count($orig) - $srcIndex, true),
722 17
                $result
723
            );
724
        }
725
        // fill in source file names
726 18
        foreach ($srcFiles as $i => $file) {
727 1
            if ($this->relative) {
728 1
                $src = $file;
729
            } else {
730
                $src = (new File($basedir, $file))->getAbsolutePath();
731
            }
732 1
            if ($this->forwardslash && FileUtils::getSeparator() !== '/') {
733
                $src = str_replace(FileUtils::getSeparator(), '/', $src);
734
            }
735
            if (
736 1
                $this->srcFilePos !== null
737
                && ($this->srcFilePos->getPrefix() !== ''
738 1
                    || $this->srcFilePos->getSuffix() !== '')
739
            ) {
740
                $src = $this->srcFilePos->getPrefix() . $src . $this->srcFilePos->getSuffix();
741
            }
742 1
            $result[$srcIndex + $i] = $src;
743
        }
744
745 18
        $this->commandline = new Commandline(implode(' ', $result));
746 18
        $this->commandline->setEscape($this->escape);
747 18
        $this->realCommand = (string) $this->commandline . $this->additionalCmds;
748
749 18
        [$returncode, $output] = $this->executeCommand();
750
751 18
        $this->maybeSetReturnPropertyValue($returncode);
752
753
        // Sets the output property
754 18
        if ($this->outputProperty) {
755 2
            $previousValue = $this->project->getProperty($this->outputProperty);
756 2
            if (!empty($previousValue)) {
757
                $previousValue .= "\n";
758
            }
759 2
            $this->project->setProperty($this->outputProperty, $previousValue . implode("\n", $output));
760
        }
761
762
        // Validating the 'return-code'
763 18
        if ($this->checkreturn && ($returncode !== 0)) {
764 1
            $this->throwBuildException("Task exited with code ($returncode)");
765
        }
766 17
    }
767
768
    /**
769
     * Runs cleanup tasks post execution
770
     * - Restore working directory
771
     *
772
     * @return void
773
     */
774 18
    protected function cleanup($return = null, $output = null): void
775
    {
776
        // Restore working directory
777 18
        if ($this->dir !== null) {
778 3
            @chdir($this->currentdirectory);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

778
            /** @scrutinizer ignore-unhandled */ @chdir($this->currentdirectory);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
779
        }
780 18
    }
781
782
    /**
783
     * Prepares the filename per base directory and relative path information
784
     *
785
     * @param array|string $filename
786
     * @param $basedir
787
     * @param $relative
788
     *
789
     * @return mixed processed filenames
790
     *
791
     * @throws IOException
792
     */
793
    public function getFilePath($filename, $basedir, $relative)
794
    {
795
        // Validating the 'file' information
796
        $files = (array) $filename;
797
798
        // Processing the file information
799
        foreach ($files as $index => $file) {
800
            $absolutefilename = (($relative === false) ? ($basedir . FileUtils::getSeparator()) : '');
801
            $absolutefilename .= $file;
802
            if ($relative === false) {
803
                $files[$index] = (new FileUtils())->normalize($absolutefilename);
804
            } else {
805
                $files[$index] = $absolutefilename;
806
            }
807
        }
808
809
        return (is_array($filename) ? $files : $files[0]);
810
    }
811
812
    /**
813
     * Throws the exception with specified information
814
     *
815
     * @param string $information Exception information
816
     *
817
     * @throws BuildException
818
     *
819
     * @return void
820
     */
821 3
    private function throwBuildException($information): void
822
    {
823 3
        throw new BuildException('ApplyTask: ' . (string) $information);
824
    }
825
}
826