Passed
Push — master ( 08a73c...a7d833 )
by Sebastian
01:49 queued 12s
created

Mysqldump::restore()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 18
cts 18
cp 1
rs 9.488
c 0
b 0
f 0
cc 2
nc 2
nop 2
crap 2
1
<?php
2
namespace phpbu\App\Backup\Source;
3
4
use phpbu\App\Backup\Restore\Plan;
5
use phpbu\App\Backup\Target;
6
use phpbu\App\Cli\Executable;
7
use phpbu\App\Exception;
8
use phpbu\App\Result;
9
use phpbu\App\Util;
10
11
/**
12
 * Mysqldump source class.
13
 *
14
 * @package    phpbu
15
 * @subpackage Backup
16
 * @author     Sebastian Feldmann <[email protected]>
17
 * @copyright  Sebastian Feldmann <[email protected]>
18
 * @license    https://opensource.org/licenses/MIT The MIT License (MIT)
19
 * @link       http://phpbu.de/
20
 * @since      Class available since Release 1.0.0
21
 */
22
class Mysqldump extends SimulatorExecutable implements Simulator, Restorable
23
{
24
    /**
25
     * Path to mysql executable.
26
     *
27
     * @var string
28
     */
29
    private $pathToMysql;
30
31
    /**
32
     * Path to mysqldump executable.
33
     *
34
     * @var string
35
     */
36
    private $pathToMysqldump;
37
38
    /**
39
     * Path to mysqlimport executable.
40
     *
41
     * @var string
42
     */
43
    private $pathToMysqlimport;
44
45
    /**
46
     * Host to connect to
47
     * --host <hostname>
48
     *
49
     * @var string
50
     */
51
    private $host;
52
53
    /**
54
     * Port to connect to
55
     * --port <port>
56
     *
57
     * @var int
58
     */
59
    private $port;
60
61
    /**
62
     * Port to connect to
63
     * --protocol <TCP|SOCKET|PIPE|MEMORY>
64
     *
65
     * @var string
66
     */
67
    private $protocol;
68
69
    /**
70
     * User to connect with
71
     * --user <username>
72
     *
73
     * @var string
74
     */
75
    private $user;
76
77
    /**
78
     * Password to authenticate with
79
     * --password <password>
80
     *
81
     * @var string
82
     */
83
    private $password;
84
85
    /**
86
     * List of tables to backup
87
     * --tables array of strings
88
     *
89
     * @var array
90
     */
91
    private $tables;
92
93
    /**
94
     * List of databases to backup
95
     * --databases array of strings
96
     *
97
     * @var array
98
     */
99
    private $databases;
100
101
    /**
102
     * List of tables to ignore
103
     *
104
     * @var array
105
     */
106
    private $ignoreTables;
107
108
    /**
109
     * List of tables where only the table structure is stored
110
     *
111
     * @var array
112
     */
113
    private $structureOnly;
114
115
    /**
116
     * Table separated data files
117
     * --tab
118
     *
119
     * @var boolean
120
     */
121
    private $filePerTable;
122
123
    /**
124
     * Use mysqldump quick mode
125
     * -q
126
     *
127
     * @var boolean
128
     */
129
    private $quick;
130
131
    /**
132
     * Lock tables option
133
     * --lock-tables
134
     *
135
     * @var bool
136
     */
137
    private $lockTables;
138
139
    /**
140
     * Single Transaction option
141
     * --single-transaction
142
     *
143
     * @var bool
144
     */
145
    private $singleTransaction;
146
147
    /**
148
     * Use mysqldump with compression
149
     * -C
150
     *
151
     * @var boolean
152
     */
153
    private $compress;
154
155
    /**
156
     * Use mysqldump with extended insert
157
     * -e
158
     *
159
     * @var boolean
160
     */
161
    private $extendedInsert;
162
163
    /**
164
     * Dump blob fields as hex.
165
     * --hex-blob
166
     *
167
     * @var boolean
168
     */
169
    private $hexBlob;
170
171
    /**
172
     * Dump only table structures
173
     * --no-data
174
     *
175
     * @var boolean
176
     */
177
    private $noData;
178
179
    /**
180
     * Add general transaction id statement.
181
     * --set-gids-purged=['ON', 'OFF', 'AUTO']
182
     *
183
     * @var string
184
     */
185
    private $gtidPurged;
186
187
    /**
188
     * Dump procedures and functions.
189
     * --routines
190
     *
191
     * @var bool
192
     */
193
    private $routines;
194
195
    /**
196
     * Setup.
197
     *
198
     * @see    \phpbu\App\Backup\Source
199
     * @param  array $conf
200
     * @throws \phpbu\App\Exception
201
     */
202 17
    public function setup(array $conf = [])
203
    {
204 17
        $this->setupSourceData($conf);
205
206 17
        $this->pathToMysql       = Util\Arr::getValue($conf, 'pathToMysql', '');
207 17
        $this->pathToMysqldump   = Util\Arr::getValue($conf, 'pathToMysqldump', '');
208 17
        $this->pathToMysqlimport = Util\Arr::getValue($conf, 'pathToMysqlimport', '');
209 17
        $this->host              = Util\Arr::getValue($conf, 'host', '');
210 17
        $this->port              = Util\Arr::getValue($conf, 'port', 0);
211 17
        $this->protocol          = Util\Arr::getValue($conf, 'protocol', '');
212 17
        $this->user              = Util\Arr::getValue($conf, 'user', '');
213 17
        $this->password          = Util\Arr::getValue($conf, 'password', '');
214 17
        $this->gtidPurged        = Util\Arr::getValue($conf, 'gtidPurged', '');
215 17
        $this->hexBlob           = Util\Str::toBoolean(Util\Arr::getValue($conf, 'hexBlob', ''), false);
216 17
        $this->quick             = Util\Str::toBoolean(Util\Arr::getValue($conf, 'quick', ''), false);
217 17
        $this->lockTables        = Util\Str::toBoolean(Util\Arr::getValue($conf, 'lockTables', ''), false);
218 17
        $this->singleTransaction = Util\Str::toBoolean(Util\Arr::getValue($conf, 'singleTransaction', ''), false);
219 17
        $this->compress          = Util\Str::toBoolean(Util\Arr::getValue($conf, 'compress', ''), false);
220 17
        $this->extendedInsert    = Util\Str::toBoolean(Util\Arr::getValue($conf, 'extendedInsert', ''), false);
221 17
        $this->noData            = Util\Str::toBoolean(Util\Arr::getValue($conf, 'noData', ''), false);
222 17
        $this->filePerTable      = Util\Str::toBoolean(Util\Arr::getValue($conf, 'filePerTable', ''), false);
223 17
        $this->routines          = Util\Str::toBoolean(Util\Arr::getValue($conf, 'routines', ''), false);
224
225
        // this doesn't fail, but it doesn't work, so throw an exception so the user understands
226 17
        if ($this->filePerTable && count($this->structureOnly)) {
227 1
            throw new Exception('\'structureOnly\' can not be used with the \'filePerTable\' option');
228
        }
229 16
    }
230
231
    /**
232
     * Get tables and databases to backup.
233
     *
234
     * @param array $conf
235
     */
236 17 View Code Duplication
    protected function setupSourceData(array $conf)
237
    {
238 17
        $this->tables        = Util\Str::toList(Util\Arr::getValue($conf, 'tables', ''));
239 17
        $this->databases     = Util\Str::toList(Util\Arr::getValue($conf, 'databases', ''));
240 17
        $this->ignoreTables  = Util\Str::toList(Util\Arr::getValue($conf, 'ignoreTables', ''));
241 17
        $this->structureOnly = Util\Str::toList(Util\Arr::getValue($conf, 'structureOnly', ''));
242 17
    }
243
244
    /**
245
     * Execute the backup.
246
     *
247
     * @see    \phpbu\App\Backup\Source
248
     * @param  \phpbu\App\Backup\Target $target
249
     * @param  \phpbu\App\Result        $result
250
     * @return \phpbu\App\Backup\Source\Status
251
     * @throws \phpbu\App\Exception
252
     */
253 4
    public function backup(Target $target, Result $result) : Status
254
    {
255
        // create the writable dump directory for tables files
256 4
        if ($this->filePerTable && !is_dir($this->getDumpTarget($target))) {
257 1
            $old = umask(0);
258 1
            mkdir($this->getDumpTarget($target), 0777, true);
259 1
            umask($old);
260
        }
261
262 4
        $mysqldump = $this->execute($target);
263
264 4
        $result->debug($this->getExecutable($target)->getCommandPrintable());
265
266 4
        if (!$mysqldump->isSuccessful()) {
267 1
            throw new Exception('mysqldump failed:' . $mysqldump->getStdErr());
268
        }
269
270 3
        return $this->createStatus($target);
271
    }
272
273
    /**
274
     * Restore the backup
275
     *
276
     * @param \phpbu\App\Backup\Target       $target
277
     * @param \phpbu\App\Backup\Restore\Plan $plan
278
     * @return \phpbu\App\Backup\Source\Status
279
     * @throws \phpbu\App\Exception
280
     */
281 2
    public function restore(Target $target, Plan $plan): Status
282
    {
283 2
        $executable = $this->createMysqlExecutable();
284
285 2
        if ($this->filePerTable) {
286 1
            $database    = $this->databases[0];
287 1
            $sourceTar   = $target->getPathname(true) . '.tar';
288 1
            $mysqlimport = $this->createMysqlimportExecutable('<table-file>', $database);
289
290 1
            $executable->useDatabase($database);
291 1
            $executable->useSourceFile('<table-file>');
292
293 1
            $mysqlCommand  = $executable->getCommandPrintable();
294 1
            $importCommand = $mysqlimport->getCommandPrintable();
295 1
            $mysqlComment  = 'Restore the structure, execute this for every table file';
296 1
            $importComment = 'Restore the data, execute this for every table file';
297
298 1
            $plan->addRestoreCommand('tar -xvf ' . $sourceTar, 'Extract the table files');
299 1
            $plan->addRestoreCommand($mysqlCommand, $mysqlComment);
300 1
            $plan->addRestoreCommand($importCommand, $importComment);
301
        } else {
302 1
            $executable->useSourceFile($target->getFilename(true));
303 1
            $plan->addRestoreCommand($executable->getCommandPrintable());
304
        }
305
306 2
        return Status::create()->uncompressedFile($target->getPathname());
307
    }
308
309
    /**
310
     * Create the Executable to run the mysqldump command.
311
     *
312
     * @param  \phpbu\App\Backup\Target $target
313
     * @return \phpbu\App\Cli\Executable
314
     */
315 14
    protected function createExecutable(Target $target) : Executable
316
    {
317 14
        $executable = new Executable\Mysqldump($this->pathToMysqldump);
318 14
        $executable->credentials($this->user, $this->password)
319 14
                   ->useHost($this->host)
320 14
                   ->usePort($this->port)
321 14
                   ->useProtocol($this->protocol)
322 14
                   ->useQuickMode($this->quick)
323 14
                   ->lockTables($this->lockTables)
324 14
                   ->dumpBlobsHexadecimal($this->hexBlob)
325 14
                   ->addGTIDStatement($this->gtidPurged)
326 14
                   ->useCompression($this->compress)
327 14
                   ->useExtendedInsert($this->extendedInsert)
328 14
                   ->dumpTables($this->tables)
329 14
                   ->singleTransaction($this->singleTransaction)
330 14
                   ->dumpDatabases($this->databases)
331 14
                   ->ignoreTables($this->ignoreTables)
332 14
                   ->produceFilePerTable($this->filePerTable)
333 14
                   ->dumpNoData($this->noData)
334 14
                   ->dumpRoutines($this->routines)
335 14
                   ->dumpStructureOnly($this->structureOnly)
336 14
                   ->dumpTo($this->getDumpTarget($target));
337
        // if compression is active and commands can be piped
338 14
        if ($this->isHandlingCompression($target)) {
339 2
            $executable->compressOutput($target->getCompression());
340
        }
341 14
        return $executable;
342
    }
343
344
    /**
345
     * Create backup status.
346
     *
347
     * @param  \phpbu\App\Backup\Target $target
348
     * @return \phpbu\App\Backup\Source\Status
349
     */
350 4
    protected function createStatus(Target $target) : Status
351
    {
352
        // file_per_table creates a directory with all the files
353 4
        if ($this->filePerTable) {
354 1
            return Status::create()->uncompressedDirectory($this->getDumpTarget($target));
355
        }
356
357
        // if compression is active and commands can be piped
358
        // compression is handled via pipe
359 3
        if ($this->isHandlingCompression($target)) {
360 1
            return Status::create();
361
        }
362
363
        // default create uncompressed dump file
364 2
        return Status::create()->uncompressedFile($this->getDumpTarget($target));
365
    }
366
367
    /**
368
     * Can compression be handled via pipe operator.
369
     *
370
     * @param  \phpbu\App\Backup\Target $target
371
     * @return bool
372
     */
373 14
    private function isHandlingCompression(Target $target) : bool
374
    {
375 14
        return $target->shouldBeCompressed() && Util\Cli::canPipe() && $target->getCompression()->isPipeable();
376
    }
377
378
    /**
379
     * Return dump target path.
380
     *
381
     * @param  \phpbu\App\Backup\Target $target
382
     * @return string
383
     */
384 14
    private function getDumpTarget(Target $target) : string
385
    {
386 14
        return $target->getPathnamePlain() . ($this->filePerTable ? '.dump' : '');
387
    }
388
389
    /**
390
     * Create the Executable to run the mysql command.
391
     *
392
     * @return \phpbu\App\Cli\Executable\Mysql
393
     */
394 2 View Code Duplication
    private function createMysqlExecutable(): Executable\Mysql
395
    {
396 2
        $executable = new Executable\Mysql($this->pathToMysql);
397 2
        $executable->credentials($this->user, $this->password)
398 2
            ->useHost($this->host)
399 2
            ->usePort($this->port)
400 2
            ->useProtocol($this->protocol)
401 2
            ->useQuickMode($this->quick)
402 2
            ->useCompression($this->compress);
403
404 2
        return $executable;
405
    }
406
407
    /**
408
     * Create the Executable to run the mysqlimport command.
409
     *
410
     * @param string $sourceFilename
411
     * @param string $targetDatabase
412
     *
413
     * @return \phpbu\App\Cli\Executable\Mysqlimport
414
     */
415 1 View Code Duplication
    private function createMysqlimportExecutable(string $sourceFilename, string $targetDatabase): Executable\Mysqlimport
416
    {
417 1
        $executable = new Executable\Mysqlimport($this->pathToMysqlimport);
418 1
        $executable->setSourceAndTarget($sourceFilename, $targetDatabase)
419 1
            ->credentials($this->user, $this->password)
420 1
            ->useHost($this->host)
421 1
            ->usePort($this->port)
422 1
            ->useProtocol($this->protocol)
423 1
            ->useCompression($this->compress);
424
425 1
        return $executable;
426
    }
427
}
428