Completed
Push — master ( a51f45...c47d23 )
by Sebastian
03:08
created

Tar::dereference()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

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 10
c 0
b 0
f 0
cc 1
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
     * Instead of archiving symbolic links, archive the files they link to
103
     *
104
     * @var bool
105
     */
106
    private $dereference = false;
107
108
    /**
109
     * Constructor.
110
     *
111
     * @param string $path
112
     */
113 36
    public function __construct(string $path = '')
114
    {
115 36
        $this->setup('tar', $path);
116 36
    }
117
118
    /**
119
     * Return 'tar' compressor option e.g. 'j' for bzip2.
120
     *
121
     * @param  string $compressor
122
     * @return string
123
     */
124 13
    protected function getCompressionOption(string $compressor) : string
125
    {
126 13
        return $this->isCompressionValid($compressor) ? self::$availableCompressions[$compressor] : '';
127
    }
128
129
    /**
130
     * Compress tar.
131
     *
132
     * @param  string $compression
133
     * @return \phpbu\App\Cli\Executable\Tar
134
     */
135 25
    public function useCompression(string $compression) : Tar
136
    {
137 25
        if ($this->isCompressionValid($compression)) {
138 13
            $this->compression = $this->getCompressionOption($compression);
139
        }
140 25
        return $this;
141
    }
142
143
    /**
144
     * Set compress program.
145
     *
146
     * @param  string $program
147
     * @return \phpbu\App\Cli\Executable\Tar
148
     */
149 17
    public function useCompressProgram(string $program) : Tar
150
    {
151 17
        $this->compressProgram = $program;
152 17
        return $this;
153
    }
154
155
    /**
156
     * Add an path to exclude.
157
     *
158
     * @param  string $path
159
     * @return \phpbu\App\Cli\Executable\Tar
160
     */
161 2
    public function addExclude(string $path) : Tar
162
    {
163 2
        $this->excludes[] = $path;
164 2
        return $this;
165
    }
166
167
    /**
168
     * Force local file resolution.
169
     *
170
     * @param  bool $bool
171
     * @return \phpbu\App\Cli\Executable\Tar
172
     */
173 17
    public function forceLocal(bool $bool) : Tar
174
    {
175 17
        $this->local = $bool;
176 17
        return $this;
177
    }
178
179
    /**
180
     * Ignore failed reads setter.
181
     *
182
     * @param  bool $bool
183
     * @return \phpbu\App\Cli\Executable\Tar
184
     */
185 17
    public function ignoreFailedRead(bool $bool) : Tar
186
    {
187 17
        $this->ignoreFailedRead = $bool;
188 17
        return $this;
189
    }
190
191
    /**
192
     * Limit the data throughput.
193
     *
194
     * @param string $limit
195
     * @return \phpbu\App\Cli\Executable\Tar
196
     */
197 18
    public function throttle(string $limit) : Tar
198
    {
199 18
        $this->pvLimit = $limit;
200 18
        return $this;
201
    }
202
203
    /**
204
     * Does the tar handle the compression.
205
     *
206
     * @return bool
207
     */
208 4
    public function handlesCompression() : bool
209
    {
210 4
        return !empty($this->compression);
211
    }
212
213
    /**
214
     * Set folder to compress.
215
     *
216
     * @param  string $path
217
     * @return \phpbu\App\Cli\Executable\Tar
218
     * @throws \phpbu\App\Exception
219
     */
220 34
    public function archiveDirectory(string $path) : Tar
221
    {
222 34
        $this->validateDirectory($path);
223 33
        $this->path = $path;
224 33
        return $this;
225
    }
226
227
    /**
228
     * Set target filename.
229
     *
230
     * @param  string $path
231
     * @return \phpbu\App\Cli\Executable\Tar
232
     */
233 32
    public function archiveTo(string $path) : Tar
234
    {
235 32
        $this->tarPathname = $path;
236 32
        return $this;
237
    }
238
239
    /**
240
     * Delete the source directory.
241
     *
242
     * @param  boolean $bool
243
     * @return \phpbu\App\Cli\Executable\Tar
244
     */
245 22
    public function removeSourceDirectory(bool $bool) : Tar
246
    {
247 22
        $this->removeSourceDir = $bool;
248 22
        return $this;
249
    }
250
251
    /**
252
     * Instead of archiving symbolic links, archive the files they link
253
     *
254
     * @param bool $bool
255
     * @return \phpbu\App\Cli\Executable\Tar
256
     */
257 17
    public function dereference(bool $bool) : Tar
258
    {
259 17
        $this->dereference = $bool;
260 17
        return $this;
261
    }
262
263
    /**
264
     * Tar CommandLine generator.
265
     *
266
     * @return \SebastianFeldmann\Cli\CommandLine
267
     * @throws \phpbu\App\Exception
268
     */
269 34
    protected function createCommandLine() : CommandLine
270
    {
271 34
        $this->validateSetup();
272
273 32
        $process = new CommandLine();
274 32
        $tar     = new Cmd($this->binary);
275 32
        $create  = $this->isThrottled() ? 'c' : 'cf';
276 32
        $process->addCommand($tar);
277
278 32
        $this->setExcludeOptions($tar);
279 32
        $this->handleWarnings($tar);
280
281 32
        $tar->addOptionIfNotEmpty('-h', $this->dereference, false);
282 32
        $tar->addOptionIfNotEmpty('--force-local', $this->local, false);
283 32
        $tar->addOptionIfNotEmpty('--use-compress-program', $this->compressProgram);
284 32
        $tar->addOption('-' . (empty($this->compressProgram) ? $this->compression : '') . $create);
285
286 32
        if ($this->isThrottled()) {
287 3
            $pv = new Cmd('pv');
288 3
            $pv->addOption('-qL', $this->pvLimit, ' ');
289 3
            $process->pipeOutputTo($pv);
290 3
            $process->redirectOutputTo($this->tarPathname);
291
        } else {
292 29
            $tar->addArgument($this->tarPathname);
293
        }
294
295 32
        $tar->addOption('-C', dirname($this->path), ' ');
296 32
        $tar->addArgument(basename($this->path));
297
298
        // delete the source data if requested
299 32
        $this->addRemoveCommand($process);
300
301 32
        return $process;
302
    }
303
304
    /**
305
     * Adds necessary exclude options to tat command.
306
     *
307
     * @param \SebastianFeldmann\Cli\Command\Executable $tar
308
     */
309 32
    protected function setExcludeOptions(Cmd $tar)
310
    {
311 32
        foreach ($this->excludes as $path) {
312 2
            $tar->addOption('--exclude', $path);
313
        }
314 32
    }
315
316
    /**
317
     * Configure warning handling.
318
     * With the 'ignoreFailedRead' option set, exit code '1' is also accepted since it only indicates a warning.
319
     *
320
     * @param \SebastianFeldmann\Cli\Command\Executable $tar
321
     */
322 32
    protected function handleWarnings(Cmd $tar)
323
    {
324 32
        if ($this->ignoreFailedRead) {
325 3
            $tar->addOption('--ignore-failed-read');
326 3
            $this->acceptableExitCodes = [0, 1];
327
        }
328 32
    }
329
330
    /**
331
     * Add a remove command if requested.
332
     *
333
     * @param \SebastianFeldmann\Cli\CommandLine $process
334
     */
335 32
    protected function addRemoveCommand(CommandLine $process)
336
    {
337 32
        if ($this->removeSourceDir) {
338 7
            $process->addCommand($this->getRmCommand());
339
        }
340 32
    }
341
342
    /**
343
     * Return 'rm' command.
344
     *
345
     * @return \SebastianFeldmann\Cli\Command\Executable
346
     */
347 7
    protected function getRmCommand() : Cmd
348
    {
349 7
        $rm = new Cmd('rm');
350 7
        $rm->addOption('-rf', $this->path, ' ');
351 7
        return $rm;
352
    }
353
354
    /**
355
     * Check directory to compress.
356
     *
357
     * @param  string $path
358
     * @throws \phpbu\App\Exception
359
     */
360 34
    private function validateDirectory(string $path)
361
    {
362 34
        if ($path === '.') {
363 1
            throw new Exception('unable to tar current working directory');
364
        }
365 33
    }
366
367
    /**
368
     * Check if source and target values are set.
369
     *
370
     * @throws \phpbu\App\Exception
371
     */
372 34
    private function validateSetup()
373
    {
374 34
        if (empty($this->path)) {
375 1
            throw new Exception('no directory to compress');
376
        }
377 33
        if (empty($this->tarPathname)) {
378 1
            throw new Exception('no target filename set');
379
        }
380 32
    }
381
382
    /**
383
     * Return true if a given compression is valid false otherwise.
384
     *
385
     * @param  string $compression
386
     * @return bool
387
     */
388 27
    public static function isCompressionValid(string  $compression) : bool
389
    {
390 27
        return isset(self::$availableCompressions[$compression]);
391
    }
392
393
    /**
394
     * Should output be throttled through pv.
395
     *
396
     * @return bool
397
     */
398 32
    public function isThrottled() : bool
399
    {
400 32
        return !empty($this->pvLimit);
401
    }
402
}
403