Completed
Push — master ( 20b0ec...0fa80a )
by Siad
15:26
created

CopyTask::copyToSingleDestination()   A

Complexity

Conditions 3
Paths 9

Size

Total Lines 40
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 3.0813

Importance

Changes 0
Metric Value
cc 3
eloc 24
nc 9
nop 8
dl 0
loc 40
ccs 19
cts 24
cp 0.7917
crap 3.0813
rs 9.536
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
    /**
85
     * Sets up this object internal stuff.
86
     * i.e. the Fileutils instance and default mode.
87
     */
88 38
    public function __construct()
89
    {
90 38
        parent::__construct();
91 38
        $this->fileUtils = new FileUtils();
92 38
        $this->mode = 0777 - umask();
93 38
    }
94
95
    /**
96
     * Set the overwrite flag. IntrospectionHelper takes care of
97
     * booleans in set* methods so we can assume that the right
98
     * value (boolean primitive) is coming in here.
99
     *
100
     * @param boolean $bool Overwrite the destination file(s) if it/they already exist
101
     *
102
     * @return void
103
     */
104 7
    public function setOverwrite($bool)
105
    {
106 7
        $this->overwrite = (bool) $bool;
107 7
    }
108
109
    /**
110
     * Set whether files copied from directory trees will be "flattened"
111
     * into a single directory.  If there are multiple files with
112
     * the same name in the source directory tree, only the first
113
     * file will be copied into the "flattened" directory, unless
114
     * the forceoverwrite attribute is true.
115
     *
116
     * @param bool $flatten if true flatten the destination directory. Default
117
     *                is false.
118
     */
119
    public function setFlatten($flatten)
120
    {
121
        $this->flatten = $flatten;
122
    }
123
124
    /**
125
     * Used to force listing of all names of copied files.
126
     *
127
     * @param boolean $verbosity
128
     */
129 1
    public function setVerbose($verbosity)
130
    {
131 1
        if ($verbosity) {
132 1
            $this->verbosity = Project::MSG_INFO;
133
        } else {
134
            $this->verbosity = Project::MSG_VERBOSE;
135
        }
136 1
    }
137
138
    /**
139
     * @see CopyTask::setPreserveLastModified
140
     * @param $bool
141
     */
142
    public function setTstamp($bool)
143
    {
144
        $this->setPreserveLastModified($bool);
145
    }
146
147
    /**
148
     * Set the preserve timestamp flag. IntrospectionHelper takes care of
149
     * booleans in set* methods so we can assume that the right
150
     * value (boolean primitive) is coming in here.
151
     *
152
     * @param  boolean $bool Preserve the timestamp on the destination file
153
     * @return void
154
     */
155 1
    public function setPreserveLastModified($bool)
156
    {
157 1
        $this->preserveLMT = (bool) $bool;
158 1
    }
159
160
    /**
161
     * Set the preserve permissions flag. IntrospectionHelper takes care of
162
     * booleans in set* methods so we can assume that the right
163
     * value (boolean primitive) is coming in here.
164
     *
165
     * @param  boolean $bool Preserve the timestamp on the destination file
166
     * @return void
167
     */
168
    public function setPreservepermissions($bool)
169
    {
170
        $this->preservePermissions = (bool) $bool;
171
    }
172
173
    /**
174
     * @param $bool
175
     */
176
    public function setPreservemode($bool)
177
    {
178
        $this->setPreservepermissions($bool);
179
    }
180
181
    /**
182
     * Set the include empty dirs flag. IntrospectionHelper takes care of
183
     * booleans in set* methods so we can assume that the right
184
     * value (boolean primitive) is coming in here.
185
     *
186
     * @param  boolean $bool Flag if empty dirs should be cpoied too
187
     * @return void
188
     */
189
    public function setIncludeEmptyDirs($bool)
190
    {
191
        $this->includeEmpty = (bool) $bool;
192
    }
193
194
    /**
195
     * Set the file. We have to manually take care of the
196
     * type that is coming due to limited type support in php
197
     * in and convert it manually if necessary.
198
     *
199
     * @param PhingFile $file The source file. Either a string or an PhingFile object
200
     *
201
     * @return void
202
     */
203 14
    public function setFile(PhingFile $file)
204
    {
205 14
        $this->file = $file;
206 14
    }
207
208
    /**
209
     * Set the toFile. 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 dest file. Either a string or an PhingFile object
214
     *
215
     * @return void
216
     */
217 9
    public function setTofile(PhingFile $file)
218
    {
219 9
        $this->destFile = $file;
220 9
    }
221
222
    /**
223
     * Sets the mode to create destination directories with (ignored on Windows).
224
     * Default mode is taken from umask()
225
     *
226
     * @param integer $mode Octal mode
227
     *
228
     * @return void
229
     */
230
    public function setMode($mode)
231
    {
232
        $this->mode = (int) base_convert($mode, 8, 10);
233
    }
234
235
    /**
236
     * Set the toDir. We have to manually take care of the
237
     * type that is coming due to limited type support in php
238
     * in and convert it manually if necessary.
239
     *
240
     * @param PhingFile $dir The directory, either a string or an PhingFile object
241
     *
242
     * @return void
243
     */
244 29
    public function setTodir(PhingFile $dir)
245
    {
246 29
        $this->destDir = $dir;
247 29
    }
248
249 2
    public function setEnableMultipleMappings($enableMultipleMappings)
250
    {
251 2
        $this->enableMultipleMappings = (bool) $enableMultipleMappings;
252 2
    }
253
254
    public function isEnabledMultipleMappings()
255
    {
256
        return $this->enableMultipleMappings;
257
    }
258
259
    /**
260
     * Set the haltonerror attribute - when true, will
261
     * make the build fail when errors are detected.
262
     *
263
     * @param boolean $haltonerror Flag if the build should be stopped on errors
264
     *
265
     * @return void
266
     */
267 1
    public function setHaltonerror($haltonerror)
268
    {
269 1
        $this->haltonerror = (bool) $haltonerror;
270 1
    }
271
272
    /**
273
     * Nested creator, creates one Mapper for this task
274
     *
275
     * @return Mapper         The created Mapper type object
276
     * @throws BuildException
277
     */
278 3
    public function createMapper()
279
    {
280 3
        if ($this->mapperElement !== null) {
281
            throw new BuildException("Cannot define more than one mapper", $this->getLocation());
282
        }
283 3
        $this->mapperElement = new Mapper($this->project);
284
285 3
        return $this->mapperElement;
286
    }
287
288
    /**
289
     * The main entry point where everything gets in motion.
290
     *
291
     * @return true           on success
292
     * @throws BuildException
293
     */
294 38
    public function main()
295
    {
296 38
        $this->validateAttributes();
297
298 38
        if ($this->file !== null) {
299 13
            if ($this->file->exists()) {
300 12
                if ($this->destFile === null) {
301 5
                    $this->destFile = new PhingFile($this->destDir, (string) $this->file->getName());
302
                }
303 12
                if ($this->overwrite === true || ($this->file->lastModified() > $this->destFile->lastModified())) {
304 12
                    $this->fileCopyMap[$this->file->getAbsolutePath()] = $this->destFile->getAbsolutePath();
305
                } else {
306
                    $this->log($this->file->getName() . " omitted, " . $this->destFile->getName() . " is up to date");
307
                }
308
            } else {
309
                // terminate build
310 1
                $this->logError("Could not find file " . $this->file->__toString() . " to copy.");
311
            }
312
        }
313
314 38
        $project = $this->getProject();
315
316
        // process filelists
317 38
        foreach ($this->filelists as $fl) {
318 2
            $fromDir = $fl->getDir($project);
319 2
            $srcFiles = $fl->getFiles($project);
320 2
            $srcDirs = [$fl->getDir($project)];
321
322 2
            if (!$this->flatten && $this->mapperElement === null) {
323 2
                $this->completeDirMap[$fromDir->getAbsolutePath()] = $this->destDir->getAbsolutePath();
324
            }
325
326 2
            $this->_scan($fromDir, $this->destDir, $srcFiles, $srcDirs);
327
        }
328
329 38
        foreach ($this->dirsets as $dirset) {
330
            try {
331 1
                $ds = $dirset->getDirectoryScanner($project);
332 1
                $fromDir = $dirset->getDir($project);
333 1
                $srcDirs = $ds->getIncludedDirectories();
334
335 1
                $srcFiles = [];
336 1
                foreach ($srcDirs as $srcDir) {
337 1
                    $srcFiles[] = $srcDir;
338
                }
339
340
                if (
341 1
                    !$this->flatten &&
342 1
                    $this->mapperElement === null &&
343 1
                    $ds->isEverythingIncluded()
344
                ) {
345
                    $this->completeDirMap[$fromDir->getAbsolutePath()] = $this->destDir->getAbsolutePath();
346
                }
347
348 1
                $this->_scan($fromDir, $this->destDir, $srcFiles, $srcDirs);
349
            } catch (BuildException $e) {
350
                if ($this->haltonerror === true) {
351
                    throw $e;
352
                }
353
354
                $this->logError($e->getMessage());
355
            }
356
        }
357
358
        // process filesets
359 38
        foreach ($this->filesets as $fs) {
360
            try {
361 25
                $ds = $fs->getDirectoryScanner($project);
362 25
                $fromDir = $fs->getDir($project);
363 25
                $srcFiles = $ds->getIncludedFiles();
364 25
                $srcDirs = $ds->getIncludedDirectories();
365
366
                if (
367 25
                    !$this->flatten
368 25
                    && $this->mapperElement === null
369 22
                    && $ds->isEverythingIncluded()
370
                ) {
371 17
                    $this->completeDirMap[$fromDir->getAbsolutePath()] = $this->destDir->getAbsolutePath();
372
                }
373
374 25
                $this->_scan($fromDir, $this->destDir, $srcFiles, $srcDirs);
375
            } catch (BuildException $e) {
376
                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...
377
                    throw $e;
378
                }
379
380
                $this->logError($e->getMessage());
381
            }
382
        }
383
384
        // go and copy the stuff
385 38
        $this->doWork();
386
387 38
        if ($this->destFile !== null) {
388 14
            $this->destDir = null;
389
        }
390 38
    }
391
392
    /**
393
     * Validates attributes coming in from XML
394
     *
395
     * @return void
396
     * @throws BuildException
397
     */
398 37
    protected function validateAttributes()
399
    {
400 37
        if ($this->file === null && count($this->dirsets) === 0 && count($this->filesets) === 0 && count($this->filelists) === 0) {
401
            throw new BuildException("CopyTask. Specify at least one source - a file, fileset or filelist.");
402
        }
403
404 37
        if ($this->destFile !== null && $this->destDir !== null) {
405
            throw new BuildException("Only one of destfile and destdir may be set.");
406
        }
407
408 37
        if ($this->destFile === null && $this->destDir === null) {
409
            throw new BuildException("One of destfile or destdir must be set.");
410
        }
411
412 37
        if ($this->file !== null && $this->file->exists() && $this->file->isDirectory()) {
413
            throw new BuildException("Use a fileset to copy directories.");
414
        }
415
416 37
        if ($this->destFile !== null && (count($this->filesets) > 0 || count($this->dirsets) > 0)) {
417
            throw new BuildException("Cannot concatenate multiple files into a single file.");
418
        }
419
420 37
        if ($this->destFile !== null) {
421 8
            $this->destDir = new PhingFile($this->destFile->getParent());
422
        }
423 37
    }
424
425
    /**
426
     * Compares source files to destination files to see if they
427
     * should be copied.
428
     *
429
     * @param $fromDir
430
     * @param $toDir
431
     * @param $files
432
     * @param $dirs
433
     *
434
     * @return void
435
     */
436 28
    private function _scan(&$fromDir, &$toDir, &$files, &$dirs)
437
    {
438
        /* mappers should be generic, so we get the mappers here and
439
        pass them on to builMap. This method is not redundan like it seems */
440 28
        $mapper = $this->getMapper();
441
442 28
        $this->buildMap($fromDir, $toDir, $files, $mapper, $this->fileCopyMap);
443
444 28
        if ($this->includeEmpty) {
445 28
            $this->buildMap($fromDir, $toDir, $dirs, $mapper, $this->dirCopyMap);
446
        }
447 28
    }
448
449 28
    private function getMapper()
450
    {
451 28
        $mapper = null;
452 28
        if ($this->mapperElement !== null) {
453 3
            $mapper = $this->mapperElement->getImplementation();
454 25
        } elseif ($this->flatten) {
455
            $mapper = new FlattenMapper();
456
        } else {
457 25
            $mapper = new IdentityMapper();
458
        }
459 28
        return $mapper;
460
    }
461
462
    /**
463
     * Builds a map of filenames (from->to) that should be copied
464
     *
465
     * @param $fromDir
466
     * @param $toDir
467
     * @param $names
468
     * @param FileNameMapper $mapper
469
     * @param $map
470
     *
471
     * @return void
472
     */
473 28
    private function buildMap(&$fromDir, &$toDir, &$names, &$mapper, &$map)
474
    {
475 28
        $toCopy = null;
476 28
        if ($this->overwrite) {
477
            $v = [];
478
            foreach ($names as $name) {
479
                $result = $mapper->main($name);
480
                if ($result !== null) {
481
                    $v[] = $name;
482
                }
483
            }
484
            $toCopy = $v;
485
        } else {
486 28
            $ds = new SourceFileScanner($this);
487 28
            $toCopy = $ds->restrict($names, $fromDir, $toDir, $mapper);
488
        }
489
490 28
        for ($i = 0, $_i = count($toCopy); $i < $_i; $i++) {
491 25
            $src = new PhingFile($fromDir, $toCopy[$i]);
492 25
            $mapped = $mapper->main($toCopy[$i]);
493 25
            if (!$this->enableMultipleMappings) {
494 23
                $dest = new PhingFile($toDir, $mapped[0]);
495 23
                $map[$src->getAbsolutePath()] = $dest->getAbsolutePath();
496
            } else {
497 2
                $mappedFiles = [];
498
499 2
                foreach ($mapped as $mappedFile) {
500 2
                    if ($mappedFile === null) {
501
                        continue;
502
                    }
503 2
                    $dest = new PhingFile($toDir, $mappedFile);
504 2
                    $mappedFiles[] = $dest->getAbsolutePath();
505
                }
506 2
                $map[$src->getAbsolutePath()] = $mappedFiles;
507
            }
508
        }
509 28
    }
510
511
    /**
512
     * Actually copies the files
513
     *
514
     * @return void
515
     * @throws BuildException
516
     */
517 33
    protected function doWork()
518
    {
519
520
        // These "slots" allow filters to retrieve information about the currently-being-process files
521 33
        $fromSlot = $this->getRegisterSlot("currentFromFile");
522 33
        $fromBasenameSlot = $this->getRegisterSlot("currentFromFile.basename");
523
524 33
        $toSlot = $this->getRegisterSlot("currentToFile");
525 33
        $toBasenameSlot = $this->getRegisterSlot("currentToFile.basename");
526
527 33
        $mapSize = count($this->fileCopyMap);
528 33
        $total = $mapSize;
529
530
        // handle empty dirs if appropriate
531 33
        if ($this->includeEmpty) {
532 33
            $count = 0;
533 33
            foreach ($this->dirCopyMap as $srcdir => $destdir) {
534 4
                $s = new PhingFile((string) $srcdir);
535 4
                $d = new PhingFile((string) $destdir);
536 4
                if (!$d->exists()) {
537
                    // Setting source directory permissions to target
538
                    // (On permissions preservation, the target directory permissions
539
                    // will be inherited from the source directory, otherwise the 'mode'
540
                    // will be used)
541 4
                    $dirMode = ($this->preservePermissions ? $s->getMode() : $this->mode);
542
543
                    // Directory creation with specific permission mode
544 4
                    if (!$d->mkdirs($dirMode)) {
545
                        $this->logError("Unable to create directory " . $d->__toString());
546
                    } else {
547 4
                        if ($this->preserveLMT) {
548
                            $d->setLastModified($s->lastModified());
549
                        }
550
551 4
                        $count++;
552
                    }
553
                }
554
            }
555 33
            if ($count > 0) {
556 4
                $this->log(
557 4
                    "Created " . $count . " empty director" . ($count == 1 ? "y" : "ies") . " in " . $this->destDir->getAbsolutePath()
558
                );
559
            }
560
        }
561
562 33
        if ($mapSize == 0) {
563 3
            return;
564
        }
565
566 30
        $this->log(
567 30
            "Copying " . $mapSize . " file" . (($mapSize) === 1 ? '' : 's') . " to " . $this->destDir->getAbsolutePath()
568
        );
569
        // walks the map and actually copies the files
570 30
        $count = 0;
571 30
        foreach ($this->fileCopyMap as $from => $toFiles) {
572 30
            if (is_array($toFiles)) {
573 2
                foreach ($toFiles as $to) {
574 2
                    $this->copyToSingleDestination(
575 2
                        $from,
576 2
                        $to,
577 2
                        $fromSlot,
578 2
                        $fromBasenameSlot,
579 2
                        $toSlot,
580 2
                        $toBasenameSlot,
581 2
                        $count,
582 2
                        $total
583
                    );
584
                }
585
            } else {
586 28
                $this->copyToSingleDestination(
587 28
                    $from,
588 28
                    $toFiles,
589 28
                    $fromSlot,
590 28
                    $fromBasenameSlot,
591 28
                    $toSlot,
592 28
                    $toBasenameSlot,
593 28
                    $count,
594 28
                    $total
595
                );
596
            }
597
        }
598 30
    }
599
600
    /**
601
     * @param $from
602
     * @param $to
603
     * @param RegisterSlot $fromSlot
604
     * @param RegisterSlot $fromBasenameSlot
605
     * @param RegisterSlot $toSlot
606
     * @param RegisterSlot $toBasenameSlot
607
     * @param $count
608
     * @param $total
609
     */
610 30
    private function copyToSingleDestination(
611
        $from,
612
        $to,
613
        $fromSlot,
614
        $fromBasenameSlot,
615
        $toSlot,
616
        $toBasenameSlot,
617
        &$count,
618
        &$total
619
    ) {
620 30
        if ($from === $to) {
621
            $this->log("Skipping self-copy of " . $from, $this->verbosity);
622
            $total--;
623
            return;
624
        }
625 30
        $this->log("From " . $from . " to " . $to, $this->verbosity);
626
        try { // try to copy file
627 30
            $fromFile = new PhingFile($from);
628 30
            $toFile = new PhingFile($to);
629
630 30
            $fromSlot->setValue($fromFile->getPath());
631 30
            $fromBasenameSlot->setValue($fromFile->getName());
632
633 30
            $toSlot->setValue($toFile->getPath());
634 30
            $toBasenameSlot->setValue($toFile->getName());
635
636 30
            $this->fileUtils->copyFile(
637 30
                $fromFile,
638 30
                $toFile,
639 30
                $this->getProject(),
640 30
                $this->overwrite,
641 30
                $this->preserveLMT,
642 30
                $this->filterChains,
643 30
                $this->mode,
644 30
                $this->preservePermissions
645
            );
646
647 30
            $count++;
648
        } catch (IOException $ioe) {
649
            $this->logError("Failed to copy " . $from . " to " . $to . ": " . $ioe->getMessage());
650
        }
651 30
    }
652
653
    /**
654
     * @param string $message
655
     * @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...
656
     *
657
     * @throws BuildException
658
     */
659 1
    protected function logError($message, $location = null)
660
    {
661 1
        if ($this->haltonerror) {
662
            throw new BuildException($message, $location);
663
        }
664
665 1
        $this->log($message, Project::MSG_ERR);
666 1
    }
667
}
668