Completed
Push — master ( a15851...522585 )
by Harry
02:42
created

MergeFiles::contract()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 24
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 2

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 24
ccs 13
cts 13
cp 1
rs 8.9713
cc 2
eloc 15
nc 2
nop 3
crap 2
1
<?php
2
3
namespace Graze\DataFile\Modify\Contract;
4
5
use DirectoryIterator;
6
use Graze\DataFile\Helper\GetOptionTrait;
7
use Graze\DataFile\Helper\OptionalLoggerTrait;
8
use Graze\DataFile\Helper\Process\ProcessFactoryAwareInterface;
9
use Graze\DataFile\Helper\Process\ProcessTrait;
10
use Graze\DataFile\Modify\Compress\CompressionType;
11
use Graze\DataFile\Modify\MakeDirectory;
12
use Graze\DataFile\Node\FileNode;
13
use Graze\DataFile\Node\FileNodeCollectionInterface;
14
use Graze\DataFile\Node\FileNodeInterface;
15
use Graze\DataFile\Node\LocalFile;
16
use Psr\Log\LoggerAwareInterface;
17
use Psr\Log\LogLevel;
18
use Symfony\Component\Process\Exception\ProcessFailedException;
19
20
class MergeFiles implements FileContractorInterface, LoggerAwareInterface, ProcessFactoryAwareInterface
21
{
22
    use GetOptionTrait;
23
    use OptionalLoggerTrait;
24
    use ProcessTrait;
25
26
    /**
27
     * @param FileNodeCollectionInterface $files
28
     * @param FileNodeInterface           $target
29
     *
30
     * @return bool
31
     */
32 12
    public function canContract(FileNodeCollectionInterface $files, FileNodeInterface $target)
33
    {
34 12
        if (!($target instanceof LocalFile)) {
35 2
            return false;
36
        }
37
38 10
        foreach ($files->getIterator() as $file) {
39 9
            if (!($file instanceof LocalFile) ||
40 9
                !($file->exists()) ||
41 8
                ($file->getCompression() != CompressionType::NONE)
42 9
            ) {
43 4
                return false;
44
            }
45 9
        }
46 9
        return true;
47
    }
48
49
    /**
50
     * Do the expansion and return a collection
51
     *
52
     * @param FileNodeCollectionInterface $files
53
     * @param FileNodeInterface           $target
54
     * @param array                       $options :keepOldFiles <bool> (Default: true) Keep the old files after merging
55
     *
56
     * @return FileNodeInterface
57
     */
58 7
    public function contract(
59
        FileNodeCollectionInterface $files,
60
        FileNodeInterface $target,
61
        array $options = []
62
    ) {
63 7
        $this->options = $options;
64 7
        if (!$this->canContract($files, $target)) {
65 2
            throw new \InvalidArgumentException("The supplied files are not valid");
66
        }
67
68 5
        $this->log(LogLevel::INFO, "Merging files in collection $files into: {$target}");
69
70
        $filePaths = $files->map(function (LocalFile $item) {
71 5
            return $item->getPath();
72 5
        });
73
74 5
        $cmd = sprintf(
75 5
            'cat %s > %s',
76 5
            implode(' ', $filePaths),
77 5
            $target->getPath()
78 5
        );
79
80 5
        return $this->runCommand($files, $target, $cmd, $this->getOption('keepOldFiles', true));
81
    }
82
83
    /**
84
     * @param FileNodeCollectionInterface $files
85
     * @param FileNodeInterface           $target
86
     * @param string                      $cmd
87
     * @param bool                        $keepOld
88
     *
89
     * @return FileNodeInterface
90
     * @throws \Graze\DataFile\Modify\Exception\MakeDirectoryFailedException
91
     */
92 5
    private function runCommand(FileNodeCollectionInterface $files, FileNodeInterface $target, $cmd, $keepOld = true)
93
    {
94 5
        if ($target instanceof FileNode) {
95 5
            $maker = new MakeDirectory();
96 5
            $maker->makeDirectory($target);
97 5
        }
98
99 5
        $process = $this->getProcess($cmd);
100 5
        $process->run();
101
102 5
        if (!$process->isSuccessful()) {
103 1
            throw new ProcessFailedException($process);
104
        }
105
106 4
        if (!$keepOld) {
107 2
            $this->log(LogLevel::DEBUG, "Deleting old files in collection $files");
108 2
            $files->map(function (LocalFile $item) {
109 2
                if ($item->exists()) {
110 2
                    $item->delete();
111 2
                }
112 2
                $count = iterator_count(new DirectoryIterator($item->getDirectory()));
113 2
                if ($count == 2) {
114 2
                    rmdir($item->getDirectory());
115 2
                }
116 2
            });
117 2
        }
118
119 4
        return $target;
120
    }
121
}
122