Completed
Push — master ( 35413c...f854d5 )
by Sebastian
21:09
created

Tar::archiveTo()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
crap 1
1
<?php
2
namespace phpbu\App\Cli\Executable;
3
4
use phpbu\App\Cli\Executable;
5
use phpbu\App\Exception;
6
use SebastianFeldmann\Cli\CommandLine;
7
use SebastianFeldmann\Cli\Command\Executable as Cmd;
8
9
/**
10
 * Tar Executable class.
11
 *
12
 * @package    phpbu
13
 * @subpackage Backup
14
 * @author     Sebastian Feldmann <[email protected]>
15
 * @copyright  Sebastian Feldmann <[email protected]>
16
 * @license    https://opensource.org/licenses/MIT The MIT License (MIT)
17
 * @link       http://phpbu.de/
18
 * @since      Class available since Release 1.0.0
19
 */
20
class Tar extends Abstraction implements Executable
21
{
22
    /**
23
     * Path to compress
24
     *
25
     * @var string
26
     */
27
    private $path;
28
29
    /**
30
     * Compression to use
31
     *
32
     * @var string
33
     */
34
    private $compression;
35
36
    /**
37
     * Compress program to use.
38
     * --use-compress-program
39
     *
40
     * @var string
41
     */
42
    private $compressProgram;
43
44
    /**
45
     * Path to dump file
46
     *
47
     * @var string
48
     */
49
    private $tarPathname;
50
51
    /**
52
     * List of excluded path.
53
     * --exclude='foo'
54
     *
55
     * @var array
56
     */
57
    private $excludes = [];
58
59
    /**
60
     * Force local file resolution
61
     * --force-local
62
     *
63
     * @var bool
64
     */
65
    private $local = false;
66
67
    /**
68
     * Ignore failed reads
69
     * --ignore-failed-read
70
     *
71
     * @var bool
72
     */
73
    private $ignoreFailedRead;
74
75
    /**
76
     * Should the source directory be removed.
77
     *
78
     * @var boolean
79
     */
80
    private $removeSourceDir = false;
81
82
    /**
83
     * Limit data throughput
84
     * | pv -L ${limit}
85
     *
86
     * @var string
87
     */
88
    private $pvLimit = '';
89
90
    /**
91
     * List of available compressors
92
     *
93
     * @var array
94
     */
95
    private static $availableCompressions = [
96
        'bzip2' => 'j',
97
        'gzip'  => 'z',
98
        'xz'    => 'J'
99
    ];
100
101
    /**
102
     * Constructor.
103
     *
104
     * @param string $path
105
     */
106 33
    public function __construct(string $path = '')
107
    {
108 33
        $this->setup('tar', $path);
109 33
    }
110
111
    /**
112
     * Return 'tar' compressor option e.g. 'j' for bzip2.
113
     *
114
     * @param  string $compressor
115
     * @return string
116
     */
117 12
    protected function getCompressionOption(string $compressor) : string
118
    {
119 12
        return $this->isCompressionValid($compressor) ? self::$availableCompressions[$compressor] : '';
120
    }
121
122
    /**
123
     * Compress tar.
124
     *
125
     * @param  string $compression
126
     * @return \phpbu\App\Cli\Executable\Tar
127
     */
128 23
    public function useCompression(string $compression) : Tar
129
    {
130 23
        if ($this->isCompressionValid($compression)) {
131 12
            $this->compression = $this->getCompressionOption($compression);
132
        }
133 23
        return $this;
134
    }
135
136
    /**
137
     * Set compress program.
138
     *
139
     * @param  string $program
140
     * @return \phpbu\App\Cli\Executable\Tar
141
     */
142 16
    public function useCompressProgram(string $program) : Tar
143
    {
144 16
        $this->compressProgram = $program;
145 16
        return $this;
146
    }
147
148
    /**
149
     * Add an path to exclude.
150
     *
151
     * @param  string $path
152
     * @return \phpbu\App\Cli\Executable\Tar
153
     */
154 2
    public function addExclude(string $path) : Tar
155
    {
156 2
        $this->excludes[] = $path;
157 2
        return $this;
158
    }
159
160
    /**
161
     * Force local file resolution.
162
     *
163
     * @param  bool $bool
164
     * @return \phpbu\App\Cli\Executable\Tar
165
     */
166 16
    public function forceLocal(bool $bool) : Tar
167
    {
168 16
        $this->local = $bool;
169 16
        return $this;
170
    }
171
172
    /**
173
     * Ignore failed reads setter.
174
     *
175
     * @param  bool $bool
176
     * @return \phpbu\App\Cli\Executable\Tar
177
     */
178 16
    public function ignoreFailedRead(bool $bool) : Tar
179
    {
180 16
        $this->ignoreFailedRead = $bool;
181 16
        return $this;
182
    }
183
184
    /**
185
     * Limit the data throughput.
186
     *
187
     * @param string $limit
188
     * @return \phpbu\App\Cli\Executable\Tar
189
     */
190 17
    public function throttle(string $limit) : Tar
191
    {
192 17
        $this->pvLimit = $limit;
193 17
        return $this;
194
    }
195
196
    /**
197
     * Does the tar handle the compression.
198
     *
199
     * @return bool
200
     */
201 4
    public function handlesCompression() : bool
202
    {
203 4
        return !empty($this->compression);
204
    }
205
206
    /**
207
     * Set folder to compress.
208
     *
209
     * @param  string $path
210
     * @return \phpbu\App\Cli\Executable\Tar
211
     * @throws \phpbu\App\Exception
212
     */
213 31
    public function archiveDirectory(string $path) : Tar
214
    {
215 31
        $this->validateDirectory($path);
216 30
        $this->path = $path;
217 30
        return $this;
218
    }
219
220
    /**
221
     * Set target filename.
222
     *
223
     * @param  string $path
224
     * @return \phpbu\App\Cli\Executable\Tar
225
     */
226 29
    public function archiveTo(string $path) : Tar
227
    {
228 29
        $this->tarPathname = $path;
229 29
        return $this;
230
    }
231
232
    /**
233
     * Delete the source directory.
234
     *
235
     * @param  boolean $bool
236
     * @return \phpbu\App\Cli\Executable\Tar
237
     */
238 20
    public function removeSourceDirectory(bool $bool) : Tar
239
    {
240 20
        $this->removeSourceDir = $bool;
241 20
        return $this;
242
    }
243
244
    /**
245
     * Tar CommandLine generator.
246
     *
247
     * @return \SebastianFeldmann\Cli\CommandLine
248
     */
249 31
    protected function createCommandLine() : CommandLine
250
    {
251 31
        $this->validateSetup();
252
253 29
        $process = new CommandLine();
254 29
        $tar     = new Cmd($this->binary);
255 29
        $create  = $this->isThrottled() ? 'c' : 'cf';
256 29
        $process->addCommand($tar);
257
258 29
        $this->setExcludeOptions($tar);
259 29
        $this->handleWarnings($tar);
260
261 29
        $tar->addOptionIfNotEmpty('--force-local', $this->local, false);
262 29
        $tar->addOptionIfNotEmpty('--use-compress-program', $this->compressProgram);
263 29
        $tar->addOption('-' . (empty($this->compressProgram) ? $this->compression : '') . $create);
264
265 29
        if ($this->isThrottled()) {
266 3
            $pv = new Cmd('pv');
267 3
            $pv->addOption('-qL', $this->pvLimit, ' ');
268 3
            $process->pipeOutputTo($pv);
269 3
            $process->redirectOutputTo($this->tarPathname);
270
        } else {
271 26
            $tar->addArgument($this->tarPathname);
272
        }
273
274 29
        $tar->addOption('-C', dirname($this->path), ' ');
275 29
        $tar->addArgument(basename($this->path));
276
277
        // delete the source data if requested
278 29
        $this->addRemoveCommand($process);
279
280 29
        return $process;
281
    }
282
283
    /**
284
     * Adds necessary exclude options to tat command.
285
     *
286
     * @param \SebastianFeldmann\Cli\Command\Executable $tar
287
     */
288 29
    protected function setExcludeOptions(Cmd $tar)
289
    {
290 29
        foreach ($this->excludes as $path) {
291 2
            $tar->addOption('--exclude', $path);
292
        }
293 29
    }
294
295
    /**
296
     * Configure warning handling.
297
     * With the 'ignoreFailedRead' option set, exit code '1' is also accepted since it only indicates a warning.
298
     *
299
     * @param \SebastianFeldmann\Cli\Command\Executable $tar
300
     */
301 29
    protected function handleWarnings(Cmd $tar)
302
    {
303 29
        if ($this->ignoreFailedRead) {
304 3
            $tar->addOption('--ignore-failed-read');
305 3
            $this->acceptableExitCodes = [0, 1];
306
        }
307 29
    }
308
309
    /**
310
     * Add a remove command if requested.
311
     *
312
     * @param \SebastianFeldmann\Cli\CommandLine $process
313
     */
314 29
    protected function addRemoveCommand(CommandLine $process)
315
    {
316 29
        if ($this->removeSourceDir) {
317 6
            $process->addCommand($this->getRmCommand());
318
        }
319 29
    }
320
321
    /**
322
     * Return 'rm' command.
323
     *
324
     * @return \SebastianFeldmann\Cli\Command\Executable
325
     */
326 6
    protected function getRmCommand() : Cmd
327
    {
328 6
        $rm = new Cmd('rm');
329 6
        $rm->addOption('-rf', $this->path, ' ');
330 6
        return $rm;
331
    }
332
333
    /**
334
     * Check directory to compress.
335
     *
336
     * @param  string $path
337
     * @throws \phpbu\App\Exception
338
     */
339 31
    private function validateDirectory(string $path)
340
    {
341 31
        if ($path === '.') {
342 1
            throw new Exception('unable to tar current working directory');
343
        }
344 30
    }
345
346
    /**
347
     * Check if source and target values are set.
348
     *
349
     * @throws \phpbu\App\Exception
350
     */
351 31
    private function validateSetup()
352
    {
353 31
        if (empty($this->path)) {
354 1
            throw new Exception('no directory to compress');
355
        }
356 30
        if (empty($this->tarPathname)) {
357 1
            throw new Exception('no target filename set');
358
        }
359 29
    }
360
361
    /**
362
     * Return true if a given compression is valid false otherwise.
363
     *
364
     * @param  string $compression
365
     * @return bool
366
     */
367 25
    public static function isCompressionValid(string  $compression) : bool
368
    {
369 25
        return isset(self::$availableCompressions[$compression]);
370
    }
371
372
    /**
373
     * Should output be throttled through pv.
374
     *
375
     * @return bool
376
     */
377 29
    public function isThrottled() : bool
378
    {
379 29
        return !empty($this->pvLimit);
380
    }
381
}
382