MergeFiles   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 102
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 14
lcom 1
cbo 9
dl 0
loc 102
ccs 39
cts 39
cp 1
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
B canContract() 0 16 6
B contract() 0 24 2
B runCommand() 0 29 6
1
<?php
2
/**
3
 * This file is part of graze/data-file
4
 *
5
 * Copyright (c) 2016 Nature Delivered Ltd. <https://www.graze.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @license https://github.com/graze/data-file/blob/master/LICENSE.md
11
 * @link    https://github.com/graze/data-file
12
 */
13
14
namespace Graze\DataFile\Modify\Contract;
15
16
use DirectoryIterator;
17
use Graze\DataFile\Helper\Builder\BuilderAwareInterface;
18
use Graze\DataFile\Helper\GetOptionTrait;
19
use Graze\DataFile\Helper\OptionalLoggerTrait;
20
use Graze\DataFile\Helper\Process\ProcessTrait;
21
use Graze\DataFile\Modify\Compress\CompressionFactory;
22
use Graze\DataFile\Modify\MakeDirectory;
23
use Graze\DataFile\Node\FileNode;
24
use Graze\DataFile\Node\FileNodeCollectionInterface;
25
use Graze\DataFile\Node\FileNodeInterface;
26
use Graze\DataFile\Node\LocalFile;
27
use Psr\Log\LoggerAwareInterface;
28
use Psr\Log\LogLevel;
29
use Symfony\Component\Process\Exception\ProcessFailedException;
30
31
class MergeFiles implements FileContractorInterface, LoggerAwareInterface, BuilderAwareInterface
32
{
33
    use GetOptionTrait;
34
    use OptionalLoggerTrait;
35
    use ProcessTrait;
36
37
    /**
38
     * @param FileNodeCollectionInterface $files
39
     * @param FileNodeInterface           $target
40
     *
41
     * @return bool
42
     */
43 11
    public function canContract(FileNodeCollectionInterface $files, FileNodeInterface $target)
44
    {
45 11
        if (!($target instanceof LocalFile)) {
46 1
            return false;
47
        }
48
49 10
        foreach ($files->getIterator() as $file) {
50 9
            if (!($file instanceof LocalFile) ||
51 9
                !($file->exists()) ||
52 9
                ($file->getCompression() != CompressionFactory::TYPE_NONE)
53
            ) {
54 9
                return false;
55
            }
56
        }
57 9
        return true;
58
    }
59
60
    /**
61
     * Do the expansion and return a collection
62
     *
63
     * @param FileNodeCollectionInterface $files
64
     * @param FileNodeInterface           $target
65
     * @param array                       $options :keepOldFiles <bool> (Default: true) Keep the old files after merging
66
     *
67
     * @return FileNodeInterface
68
     */
69 7
    public function contract(
70
        FileNodeCollectionInterface $files,
71
        FileNodeInterface $target,
72
        array $options = []
73
    ) {
74 7
        $this->options = $options;
75 7
        if (!$this->canContract($files, $target)) {
76 2
            throw new \InvalidArgumentException("The supplied files are not valid");
77
        }
78
79 5
        $this->log(LogLevel::INFO, "Merging files in collection $files into: {$target}");
80
81
        $filePaths = $files->map(function (LocalFile $item) {
82 5
            return $item->getPath();
83 5
        });
84
85 5
        $cmd = sprintf(
86 5
            'cat %s > %s',
87 5
            implode(' ', $filePaths),
88 5
            $target->getPath()
89
        );
90
91 5
        return $this->runCommand($files, $target, $cmd, $this->getOption('keepOldFiles', true));
92
    }
93
94
    /**
95
     * @param FileNodeCollectionInterface $files
96
     * @param FileNodeInterface           $target
97
     * @param string                      $cmd
98
     * @param bool                        $keepOld
99
     *
100
     * @return FileNodeInterface
101
     * @throws \Graze\DataFile\Modify\Exception\MakeDirectoryFailedException
102
     */
103 5
    private function runCommand(FileNodeCollectionInterface $files, FileNodeInterface $target, $cmd, $keepOld = true)
104
    {
105 5
        if ($target instanceof FileNode) {
106 5
            $maker = new MakeDirectory();
107 5
            $maker->makeDirectory($target);
108
        }
109
110 5
        $process = $this->getProcess($cmd);
111 5
        $process->run();
112
113 5
        if (!$process->isSuccessful()) {
114 1
            throw new ProcessFailedException($process);
115
        }
116
117 4
        if (!$keepOld) {
118 2
            $this->log(LogLevel::DEBUG, "Deleting old files in collection $files");
119 2
            $files->map(function (LocalFile $item) {
120 2
                if ($item->exists()) {
121 2
                    $item->delete();
122
                }
123 2
                $count = iterator_count(new DirectoryIterator($item->getDirectory()));
124 2
                if ($count == 2) {
125 2
                    rmdir($item->getDirectory());
126
                }
127 2
            });
128
        }
129
130 4
        return $target;
131
    }
132
}
133