Passed
Push — master ( 91accf...e7194d )
by Siad
06:49
created

CopyTask::copyToSingleDestination()   A

Complexity

Conditions 3
Paths 9

Size

Total Lines 41
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 3.0813

Importance

Changes 0
Metric Value
cc 3
eloc 25
nc 9
nop 8
dl 0
loc 41
ccs 19
cts 24
cp 0.7917
crap 3.0813
rs 9.52
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the LGPL. For more information please see
17
 * <http://phing.info>.
18
 */
19
20
/**
21
 * A phing copy task.  Copies a file or directory to a new file
22
 * or directory.  Files are only copied if the source file is newer
23
 * than the destination file, or when the destination file does not
24
 * exist. It is possible to explicitly overwrite existing files.
25
 *
26
 * @author Andreas Aderhold, [email protected]
27
 *
28
 * @package phing.tasks.system
29
 */
30
class CopyTask extends Task
31
{
32
    use ResourceAware;
33
    use FilterChainAware;
34
35
    /**
36
     * @var PhingFile
37
     */
38
    protected $file = null; // the source file (from xml attribute)
39
40
    /**
41
     * @var PhingFile
42
     */
43
    protected $destFile = null; // the destiantion file (from xml attribute)
44
45
    /**
46
     * @var PhingFile
47
     */
48
    protected $destDir = null; // the destination dir (from xml attribute)
49
50
    protected $overwrite = false; // overwrite destination (from xml attribute)
51
    protected $preserveLMT = false; // sync timestamps (from xml attribute)
52
    protected $preservePermissions = true; // sync permissions (from xml attribute)
53
    protected $includeEmpty = true; // include empty dirs? (from XML)
54
    protected $flatten = false; // apply the FlattenMapper right way (from XML)
55
56
    /**
57
     * @var Mapper
58
     */
59
    protected $mapperElement = null;
60
61
    protected $fileCopyMap = []; // asoc array containing mapped file names
62
    protected $dirCopyMap = []; // asoc array containing mapped file names
63
    protected $completeDirMap = []; // asoc array containing complete dir names
64
65
    /**
66
     * @var FileUtils
67
     */
68
    protected $fileUtils = null; // a instance of fileutils
69
70
    protected $verbosity = Project::MSG_VERBOSE;
71
72
    /**
73
     * @var int $mode
74
     */
75
    protected $mode = 0; // mode to create directories with
76
77
    /**
78
     * @var bool $haltonerror
79
     */
80
    protected $haltonerror = true; // stop build on errors
81
82
    protected $enableMultipleMappings = false;
83
84
    /** @var int $granularity */
85
    protected $granularity = 0;
86
87
    /**
88
     * Sets up this object internal stuff.
89
     * i.e. the Fileutils instance and default mode.
90
     */
91 58
    public function __construct()
92
    {
93 58
        parent::__construct();
94 58
        $this->fileUtils = new FileUtils();
95 58
        $this->mode = 0777 - umask();
96 58
    }
97
98
    /**
99
     * Set the number of seconds leeway to give before deciding a
100
     * target is out of date.
101
     *
102
     * @param int $granularity the granularity used to decide if a target is out of date.
103
     */
104 2
    public function setGranularity(int $granularity): void
105
    {
106 2
        $this->granularity = $granularity;
107 2
    }
108
109
    /**
110
     * Set the overwrite flag. IntrospectionHelper takes care of
111
     * booleans in set* methods so we can assume that the right
112
     * value (boolean primitive) is coming in here.
113
     *
114
     * @param boolean $bool Overwrite the destination file(s) if it/they already exist
115
     *
116
     * @return void
117
     */
118 9
    public function setOverwrite($bool)
119
    {
120 9
        $this->overwrite = (bool) $bool;
121 9
    }
122
123
    /**
124
     * Set whether files copied from directory trees will be "flattened"
125
     * into a single directory.  If there are multiple files with
126
     * the same name in the source directory tree, only the first
127
     * file will be copied into the "flattened" directory, unless
128
     * the forceoverwrite attribute is true.
129
     *
130
     * @param bool $flatten if true flatten the destination directory. Default
131
     *                is false.
132
     */
133
    public function setFlatten($flatten)
134
    {
135
        $this->flatten = $flatten;
136
    }
137
138
    /**
139
     * Used to force listing of all names of copied files.
140
     *
141
     * @param boolean $verbosity
142
     */
143 1
    public function setVerbose($verbosity)
144
    {
145 1
        if ($verbosity) {
146 1
            $this->verbosity = Project::MSG_INFO;
147
        } else {
148
            $this->verbosity = Project::MSG_VERBOSE;
149
        }
150 1
    }
151
152
    /**
153
     * @see CopyTask::setPreserveLastModified
154
     * @param $bool
155
     */
156
    public function setTstamp($bool)
157
    {
158
        $this->setPreserveLastModified($bool);
159
    }
160
161
    /**
162
     * Set the preserve timestamp flag. IntrospectionHelper takes care of
163
     * booleans in set* methods so we can assume that the right
164
     * value (boolean primitive) is coming in here.
165
     *
166
     * @param  boolean $bool Preserve the timestamp on the destination file
167
     * @return void
168
     */
169 1
    public function setPreserveLastModified($bool)
170
    {
171 1
        $this->preserveLMT = (bool) $bool;
172 1
    }
173
174
    /**
175
     * Set the preserve permissions flag. IntrospectionHelper takes care of
176
     * booleans in set* methods so we can assume that the right
177
     * value (boolean primitive) is coming in here.
178
     *
179
     * @param  boolean $bool Preserve the timestamp on the destination file
180
     * @return void
181
     */
182
    public function setPreservepermissions($bool)
183
    {
184
        $this->preservePermissions = (bool) $bool;
185
    }
186
187
    /**
188
     * @param $bool
189
     */
190
    public function setPreservemode($bool)
191
    {
192
        $this->setPreservepermissions($bool);
193
    }
194
195
    /**
196
     * Set the include empty dirs flag. IntrospectionHelper takes care of
197
     * booleans in set* methods so we can assume that the right
198
     * value (boolean primitive) is coming in here.
199
     *
200
     * @param  boolean $bool Flag if empty dirs should be cpoied too
201
     * @return void
202
     */
203
    public function setIncludeEmptyDirs($bool)
204
    {
205
        $this->includeEmpty = (bool) $bool;
206
    }
207
208
    /**
209
     * Set the file. We have to manually take care of the
210
     * type that is coming due to limited type support in php
211
     * in and convert it manually if necessary.
212
     *
213
     * @param PhingFile $file The source file. Either a string or an PhingFile object
214
     *
215
     * @return void
216
     */
217 16
    public function setFile(PhingFile $file)
218
    {
219 16
        $this->file = $file;
220 16
    }
221
222
    /**
223
     * Set the toFile. We have to manually take care of the
224
     * type that is coming due to limited type support in php
225
     * in and convert it manually if necessary.
226
     *
227
     * @param PhingFile $file The dest file. Either a string or an PhingFile object
228
     *
229
     * @return void
230
     */
231 9
    public function setTofile(PhingFile $file)
232
    {
233 9
        $this->destFile = $file;
234 9
    }
235
236
    /**
237
     * Sets the mode to create destination directories with (ignored on Windows).
238
     * Default mode is taken from umask()
239
     *
240
     * @param integer $mode Octal mode
241
     *
242
     * @return void
243
     */
244
    public function setMode($mode)
245
    {
246
        $this->mode = (int) base_convert($mode, 8, 10);
247
    }
248
249
    /**
250
     * Set the toDir. We have to manually take care of the
251
     * type that is coming due to limited type support in php
252
     * in and convert it manually if necessary.
253
     *
254
     * @param PhingFile $dir The directory, either a string or an PhingFile object
255
     *
256
     * @return void
257
     */
258 49
    public function setTodir(PhingFile $dir)
259
    {
260 49
        $this->destDir = $dir;
261 49
    }
262
263 2
    public function setEnableMultipleMappings($enableMultipleMappings)
264
    {
265 2
        $this->enableMultipleMappings = (bool) $enableMultipleMappings;
266 2
    }
267
268
    public function isEnabledMultipleMappings()
269
    {
270
        return $this->enableMultipleMappings;
271
    }
272
273
    /**
274
     * Set the haltonerror attribute - when true, will
275
     * make the build fail when errors are detected.
276
     *
277
     * @param boolean $haltonerror Flag if the build should be stopped on errors
278
     *
279
     * @return void
280
     */
281 1
    public function setHaltonerror($haltonerror)
282
    {
283 1
        $this->haltonerror = (bool) $haltonerror;
284 1
    }
285
286
    /**
287
     * Nested creator, creates one Mapper for this task
288
     *
289
     * @return Mapper         The created Mapper type object
290
     * @throws BuildException
291
     */
292 3
    public function createMapper()
293
    {
294 3
        if ($this->mapperElement !== null) {
295
            throw new BuildException("Cannot define more than one mapper", $this->getLocation());
296
        }
297 3
        $this->mapperElement = new Mapper($this->project);
298
299 3
        return $this->mapperElement;
300
    }
301
302
    /**
303
     * The main entry point where everything gets in motion.
304
     *
305
     * @return true           on success
306
     * @throws BuildException
307
     */
308 57
    public function main()
309
    {
310 57
        $this->validateAttributes();
311
312 57
        if ($this->file !== null) {
313 15
            if ($this->file->exists()) {
314 14
                if ($this->destFile === null) {
315 7
                    $this->destFile = new PhingFile($this->destDir, (string) $this->file->getName());
316
                }
317
                if (
318 14
                    $this->overwrite === true
319 14
                    || ($this->file->lastModified() - $this->granularity > $this->destFile->lastModified())
320
                ) {
321 12
                    $this->fileCopyMap[$this->file->getAbsolutePath()] = $this->destFile->getAbsolutePath();
322
                } else {
323 14
                    $this->log($this->file->getName() . " omitted, " . $this->destFile->getName() . " is up to date");
324
                }
325
            } else {
326
                // terminate build
327 1
                $this->logError("Could not find file " . $this->file->__toString() . " to copy.");
328
            }
329
        }
330
331 57
        $project = $this->getProject();
332
333
        // process filelists
334 57
        foreach ($this->filelists as $fl) {
335 2
            $fromDir = $fl->getDir($project);
336 2
            $srcFiles = $fl->getFiles($project);
337 2
            $srcDirs = [$fl->getDir($project)];
338
339 2
            if (!$this->flatten && $this->mapperElement === null) {
340 2
                $this->completeDirMap[$fromDir->getAbsolutePath()] = $this->destDir->getAbsolutePath();
341
            }
342
343 2
            $this->_scan($fromDir, $this->destDir, $srcFiles, $srcDirs);
344
        }
345
346 57
        foreach ($this->dirsets as $dirset) {
347
            try {
348 1
                $ds = $dirset->getDirectoryScanner($project);
349 1
                $fromDir = $dirset->getDir($project);
350 1
                $srcDirs = $ds->getIncludedDirectories();
351
352 1
                $srcFiles = [];
353 1
                foreach ($srcDirs as $srcDir) {
354 1
                    $srcFiles[] = $srcDir;
355
                }
356
357
                if (
358 1
                    !$this->flatten &&
359 1
                    $this->mapperElement === null &&
360 1
                    $ds->isEverythingIncluded()
361
                ) {
362
                    $this->completeDirMap[$fromDir->getAbsolutePath()] = $this->destDir->getAbsolutePath();
363
                }
364
365 1
                $this->_scan($fromDir, $this->destDir, $srcFiles, $srcDirs);
366
            } catch (BuildException $e) {
367
                if ($this->haltonerror === true) {
368
                    throw $e;
369
                }
370
371
                $this->logError($e->getMessage());
372
            }
373
        }
374
375
        // process filesets
376 57
        foreach ($this->filesets as $fs) {
377
            try {
378 42
                $ds = $fs->getDirectoryScanner($project);
379 38
                $fromDir = $fs->getDir($project);
380 38
                $srcFiles = $ds->getIncludedFiles();
381 38
                $srcDirs = $ds->getIncludedDirectories();
382
383
                if (
384 38
                    !$this->flatten
385 38
                    && $this->mapperElement === null
386 38
                    && $ds->isEverythingIncluded()
387
                ) {
388 18
                    $this->completeDirMap[$fromDir->getAbsolutePath()] = $this->destDir->getAbsolutePath();
389
                }
390
391 38
                $this->_scan($fromDir, $this->destDir, $srcFiles, $srcDirs);
392 4
            } catch (BuildException $e) {
393 4
                if ($this->haltonerror == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
394 4
                    throw $e;
395
                }
396
397
                $this->logError($e->getMessage());
398
            }
399
        }
400
401
        // go and copy the stuff
402 53
        $this->doWork();
403
404 53
        if ($this->destFile !== null) {
405 16
            $this->destDir = null;
406
        }
407 53
    }
408
409
    /**
410
     * Validates attributes coming in from XML
411
     *
412
     * @return void
413
     * @throws BuildException
414
     */
415 56
    protected function validateAttributes()
416
    {
417 56
        if ($this->file === null && count($this->dirsets) === 0 && count($this->filesets) === 0 && count($this->filelists) === 0) {
418
            throw new BuildException("CopyTask. Specify at least one source - a file, fileset or filelist.");
419
        }
420
421 56
        if ($this->destFile !== null && $this->destDir !== null) {
422
            throw new BuildException("Only one of destfile and destdir may be set.");
423
        }
424
425 56
        if ($this->destFile === null && $this->destDir === null) {
426
            throw new BuildException("One of destfile or destdir must be set.");
427
        }
428
429 56
        if ($this->file !== null && $this->file->exists() && $this->file->isDirectory()) {
430
            throw new BuildException("Use a fileset to copy directories.");
431
        }
432
433 56
        if ($this->destFile !== null && (count($this->filesets) > 0 || count($this->dirsets) > 0)) {
434
            throw new BuildException("Cannot concatenate multiple files into a single file.");
435
        }
436
437 56
        if ($this->destFile !== null) {
438 8
            $this->destDir = new PhingFile($this->destFile->getParent());
439
        }
440 56
    }
441
442
    /**
443
     * Compares source files to destination files to see if they
444
     * should be copied.
445
     *
446
     * @param $fromDir
447
     * @param $toDir
448
     * @param $files
449
     * @param $dirs
450
     *
451
     * @return void
452
     */
453 41
    private function _scan(&$fromDir, &$toDir, &$files, &$dirs)
454
    {
455
        /* mappers should be generic, so we get the mappers here and
456
        pass them on to builMap. This method is not redundan like it seems */
457 41
        $mapper = $this->getMapper();
458
459 41
        $this->buildMap($fromDir, $toDir, $files, $mapper, $this->fileCopyMap);
460
461 41
        if ($this->includeEmpty) {
462 41
            $this->buildMap($fromDir, $toDir, $dirs, $mapper, $this->dirCopyMap);
463
        }
464 41
    }
465
466 41
    private function getMapper()
467
    {
468 41
        $mapper = null;
469 41
        if ($this->mapperElement !== null) {
470 3
            $mapper = $this->mapperElement->getImplementation();
471 38
        } elseif ($this->flatten) {
472
            $mapper = new FlattenMapper();
473
        } else {
474 38
            $mapper = new IdentityMapper();
475
        }
476 41
        return $mapper;
477
    }
478
479
    /**
480
     * Builds a map of filenames (from->to) that should be copied
481
     *
482
     * @param $fromDir
483
     * @param $toDir
484
     * @param $names
485
     * @param FileNameMapper $mapper
486
     * @param $map
487
     *
488
     * @return void
489
     */
490 41
    private function buildMap(&$fromDir, &$toDir, &$names, &$mapper, &$map)
491
    {
492 41
        $toCopy = null;
493 41
        if ($this->overwrite) {
494 1
            $v = [];
495 1
            foreach ($names as $name) {
496 1
                $result = $mapper->main($name);
497 1
                if ($result !== null) {
498 1
                    $v[] = $name;
499
                }
500
            }
501 1
            $toCopy = $v;
502
        } else {
503 40
            $ds = new SourceFileScanner($this);
504 40
            $toCopy = $ds->restrict($names, $fromDir, $toDir, $mapper);
505
        }
506
507 41
        for ($i = 0, $_i = count($toCopy); $i < $_i; $i++) {
508 38
            $src = new PhingFile($fromDir, $toCopy[$i]);
509 38
            $mapped = $mapper->main($toCopy[$i]);
510 38
            if (!$this->enableMultipleMappings) {
511 36
                $dest = new PhingFile($toDir, $mapped[0]);
512 36
                $map[$src->getAbsolutePath()] = $dest->getAbsolutePath();
513
            } else {
514 2
                $mappedFiles = [];
515
516 2
                foreach ($mapped as $mappedFile) {
517 2
                    if ($mappedFile === null) {
518
                        continue;
519
                    }
520 2
                    $dest = new PhingFile($toDir, $mappedFile);
521 2
                    $mappedFiles[] = $dest->getAbsolutePath();
522
                }
523 2
                $map[$src->getAbsolutePath()] = $mappedFiles;
524
            }
525
        }
526 41
    }
527
528
    /**
529
     * Actually copies the files
530
     *
531
     * @return void
532
     * @throws BuildException
533
     */
534 47
    protected function doWork()
535
    {
536
537
        // These "slots" allow filters to retrieve information about the currently-being-process files
538 47
        $fromSlot = $this->getRegisterSlot("currentFromFile");
539 47
        $fromBasenameSlot = $this->getRegisterSlot("currentFromFile.basename");
540
541 47
        $toSlot = $this->getRegisterSlot("currentToFile");
542 47
        $toBasenameSlot = $this->getRegisterSlot("currentToFile.basename");
543
544 47
        $mapSize = count($this->fileCopyMap);
545 47
        $total = $mapSize;
546
547
        // handle empty dirs if appropriate
548 47
        if ($this->includeEmpty) {
549 47
            $count = 0;
550 47
            foreach ($this->dirCopyMap as $srcdir => $destdir) {
551 4
                $s = new PhingFile((string) $srcdir);
552 4
                $d = new PhingFile((string) $destdir);
553 4
                if (!$d->exists()) {
554
                    // Setting source directory permissions to target
555
                    // (On permissions preservation, the target directory permissions
556
                    // will be inherited from the source directory, otherwise the 'mode'
557
                    // will be used)
558 4
                    $dirMode = ($this->preservePermissions ? $s->getMode() : $this->mode);
559
560
                    // Directory creation with specific permission mode
561 4
                    if (!$d->mkdirs($dirMode)) {
562
                        $this->logError("Unable to create directory " . $d->__toString());
563
                    } else {
564 4
                        if ($this->preserveLMT) {
565
                            $d->setLastModified($s->lastModified());
566
                        }
567
568 4
                        $count++;
569
                    }
570
                }
571
            }
572 47
            if ($count > 0) {
573 4
                $this->log(
574 4
                    "Created " . $count . " empty director" . ($count == 1 ? "y" : "ies") . " in " . $this->destDir->getAbsolutePath()
575
                );
576
            }
577
        }
578
579 47
        if ($mapSize == 0) {
580 4
            return;
581
        }
582
583 43
        $this->log(
584 43
            "Copying " . $mapSize . " file" . (($mapSize) === 1 ? '' : 's') . " to " . $this->destDir->getAbsolutePath()
585
        );
586
        // walks the map and actually copies the files
587 43
        $count = 0;
588 43
        foreach ($this->fileCopyMap as $from => $toFiles) {
589 43
            if (is_array($toFiles)) {
590 2
                foreach ($toFiles as $to) {
591 2
                    $this->copyToSingleDestination(
592 2
                        $from,
593
                        $to,
594
                        $fromSlot,
595
                        $fromBasenameSlot,
596
                        $toSlot,
597
                        $toBasenameSlot,
598
                        $count,
599
                        $total
600
                    );
601
                }
602
            } else {
603 41
                $this->copyToSingleDestination(
604 41
                    $from,
605
                    $toFiles,
606
                    $fromSlot,
607
                    $fromBasenameSlot,
608
                    $toSlot,
609
                    $toBasenameSlot,
610
                    $count,
611
                    $total
612
                );
613
            }
614
        }
615 43
    }
616
617
    /**
618
     * @param $from
619
     * @param $to
620
     * @param RegisterSlot $fromSlot
621
     * @param RegisterSlot $fromBasenameSlot
622
     * @param RegisterSlot $toSlot
623
     * @param RegisterSlot $toBasenameSlot
624
     * @param $count
625
     * @param $total
626
     */
627 43
    private function copyToSingleDestination(
628
        $from,
629
        $to,
630
        $fromSlot,
631
        $fromBasenameSlot,
632
        $toSlot,
633
        $toBasenameSlot,
634
        &$count,
635
        &$total
636
    ) {
637 43
        if ($from === $to) {
638
            $this->log("Skipping self-copy of " . $from, $this->verbosity);
639
            $total--;
640
            return;
641
        }
642 43
        $this->log("From " . $from . " to " . $to, $this->verbosity);
643
        try { // try to copy file
644 43
            $fromFile = new PhingFile($from);
645 43
            $toFile = new PhingFile($to);
646
647 43
            $fromSlot->setValue($fromFile->getPath());
648 43
            $fromBasenameSlot->setValue($fromFile->getName());
649
650 43
            $toSlot->setValue($toFile->getPath());
651 43
            $toBasenameSlot->setValue($toFile->getName());
652
653 43
            $this->fileUtils->copyFile(
654 43
                $fromFile,
655
                $toFile,
656 43
                $this->getProject(),
657 43
                $this->overwrite,
658 43
                $this->preserveLMT,
659 43
                $this->filterChains,
660 43
                $this->mode,
661 43
                $this->preservePermissions,
662 43
                $this->granularity
663
            );
664
665 43
            $count++;
666
        } catch (IOException $ioe) {
667
            $this->logError("Failed to copy " . $from . " to " . $to . ": " . $ioe->getMessage());
668
        }
669 43
    }
670
671
    /**
672
     * @param string $message
673
     * @param null $location
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $location is correct as it would always require null to be passed?
Loading history...
674
     *
675
     * @throws BuildException
676
     */
677 1
    protected function logError($message, $location = null)
678
    {
679 1
        if ($this->haltonerror) {
680
            throw new BuildException($message, $location);
681
        }
682
683 1
        $this->log($message, Project::MSG_ERR);
684 1
    }
685
}
686