Passed
Push — master ( b9bc63...f1e2bc )
by Michiel
06:23
created

ApplyTask::restrict()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

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

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