Passed
Push — main ( 221f6d...f8c128 )
by Siad
05:28
created

src/Phing/Task/System/ApplyTask.php (1 issue)

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