Completed
Pull Request — master (#216)
by
unknown
03:06
created

Mysqldump::restore()   B

Complexity

Conditions 8
Paths 128

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 8

Importance

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