Passed
Push — develop ( d3c53a...e28085 )
by nguereza
14:20
created

DatabaseDump   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 232
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 65
c 1
b 0
f 0
dl 0
loc 232
rs 10
wmc 22

10 Methods

Rating   Name   Duplication   Size   Complexity  
A backup() 0 29 6
A __construct() 0 6 1
A createDriver() 0 10 1
A setOnProgress() 0 4 1
A isCompress() 0 3 1
A setTables() 0 8 2
A dumpTable() 0 8 2
A restore() 0 20 4
A getOnProgress() 0 3 1
A setCompress() 0 12 3
1
<?php
2
3
/**
4
 * Platine Framework
5
 *
6
 * Platine Framework is a lightweight, high-performance, simple and elegant
7
 * PHP Web framework
8
 *
9
 * This content is released under the MIT License (MIT)
10
 *
11
 * Copyright (c) 2020 Platine Framework
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
declare(strict_types=1);
33
34
namespace Platine\Framework\Tool\Database;
35
36
use Platine\Database\Connection;
37
use Platine\Filesystem\Filesystem;
38
use Platine\Logger\LoggerInterface;
39
use RuntimeException;
40
41
/**
42
 * @class DatabaseDump
43
 * @package Platine\Framework\Tool\Database
44
 */
45
class DatabaseDump
46
{
47
    /**
48
     * Do not do backup of anything
49
     */
50
    public const NONE = 0;
51
52
    /**
53
     * Add "DROP" statement for database, tables, etc.
54
     */
55
    public const DROP = 1;
56
57
    /**
58
     * Add statement for database, table, etc. creation
59
     */
60
    public const CREATE = 2;
61
62
    /**
63
     * Do backup of data
64
     */
65
    public const DATA = 4;
66
67
    /**
68
     * Do backup of triggers
69
     */
70
    public const TRIGGER = 8; // not used currently
71
72
    /**
73
     * Default value, process for all
74
     */
75
    public const ALL = 15;
76
77
    /**
78
     * The maximum SQL size
79
     */
80
    public const MAX_SQL_SIZE = 1e6;
81
82
    /**
83
     * The table list to process
84
     * @var array<string, int>
85
     */
86
    protected array $tables = ['*' => self::ALL];
87
88
    /**
89
     * Whether to compress backup file
90
     * @var bool
91
     */
92
    protected bool $compress = false;
93
94
    /**
95
     * Callback to track the progress
96
     * @var callable|null
97
     */
98
    protected $onProgress = null;
99
100
    /**
101
     * The dump driver
102
     * @var DumpDriverInterface
103
     */
104
    protected DumpDriverInterface $driver;
105
106
    /**
107
    * Create new instance
108
    * @param Connection $connection
109
    * @param LoggerInterface $logger
110
    * @param Filesystem $filesystem
111
    */
112
    public function __construct(
113
        protected Connection $connection,
114
        protected LoggerInterface $logger,
115
        protected Filesystem $filesystem
116
    ) {
117
        $this->createDriver();
118
    }
119
120
    /**
121
     * Save the database data into the given file
122
     * @param string $filename
123
     * @return void
124
     */
125
    public function backup(string $filename): void
126
    {
127
        $file = $this->filesystem->file($filename);
128
        if ($file->exists() && $file->isWritable() === false) {
129
            throw new RuntimeException(sprintf('The file [%s] is not writable', $filename));
130
        }
131
132
        $schema = $this->connection->getSchema();
133
        $tables = array_keys($schema->getTables());
134
        $views = array_keys($schema->getViews());
135
        $dbName = $schema->getDatabaseName();
136
137
        $content = $this->driver->startBackup($dbName, $tables, $views);
138
139
        foreach ($tables as $name) {
140
            $content .= $this->dumpTable($name, false);
141
        }
142
143
        foreach ($views as $name) {
144
            $content .= $this->dumpTable($name, true);
145
        }
146
147
        $content .= $this->driver->endBackup($dbName);
148
149
        if ($this->compress) {
150
            $content = (string) gzencode($content);
151
        }
152
153
        $file->write($content);
154
    }
155
156
    /**
157
     * Restore the database data using the given file
158
     * @param string $filename
159
     * @return void
160
     */
161
    public function restore(string $filename): void
162
    {
163
        $file = $this->filesystem->file($filename);
164
        if ($file->exists() === false || $file->isReadable() === false) {
165
            throw new RuntimeException(sprintf(
166
                'The file [%s] does not exist or is not readable',
167
                $filename
168
            ));
169
        }
170
171
        $content = $file->read();
172
        if ($this->compress) {
173
            $content = (string) gzdecode($content);
174
        }
175
176
        $this->driver->restore(
177
            $filename,
178
            $content,
179
            $this->onProgress,
180
            $this->compress
181
        );
182
    }
183
184
    /**
185
     * Whether compression is used or not
186
     * @return bool
187
     */
188
    public function isCompress(): bool
189
    {
190
        return $this->compress;
191
    }
192
193
    /**
194
     * Return the on progress handler
195
     * @return callable|null
196
     */
197
    public function getOnProgress(): ?callable
198
    {
199
        return $this->onProgress;
200
    }
201
202
    /**
203
     * Set the tables
204
     * @param array<string, int> $tables
205
     * @return $this
206
     */
207
    public function setTables(array $tables)
208
    {
209
        if (!isset($tables['*'])) {
210
            $tables['*'] = self::ALL;
211
        }
212
213
        $this->tables = $tables;
214
        return $this;
215
    }
216
217
    /**
218
     * Set the compression feature
219
     * @param bool $compress
220
     * @return $this
221
     */
222
    public function setCompress(bool $compress): self
223
    {
224
        if ($compress && function_exists('gzopen') === false) {
225
            throw new RuntimeException(sprintf(
226
                '"%s" function does not exist can not use compression',
227
                'gzopen'
228
            ));
229
        }
230
231
        $this->compress = $compress;
232
233
        return $this;
234
    }
235
236
    /**
237
     * Set on progress handler
238
     * @param callable|null $onProgress
239
     * @return $this
240
     */
241
    public function setOnProgress(?callable $onProgress): self
242
    {
243
        $this->onProgress = $onProgress;
244
        return $this;
245
    }
246
247
    /**
248
     * Dump the given table or view
249
     * @param string $name
250
     * @param bool $isView
251
     * @return string
252
     */
253
    protected function dumpTable(string $name, bool $isView = false): string
254
    {
255
        $mode = $this->tables[$name] ?? $this->tables['*'];
256
        if ($mode === self::NONE) {
257
            return '';
258
        }
259
260
        return $this->driver->dumpTable($name, $mode, $isView);
261
    }
262
263
    /**
264
     * Create the dump driver
265
     * @return void
266
     */
267
    protected function createDriver(): void
268
    {
269
        $maps = [
270
          'mysql'  => MySQLDump::class,
271
        ];
272
273
        $driver = $this->connection->getConfig()->getDriverName();
274
        $className = $maps[$driver] ?? NullDumpDriver::class;
275
276
        $this->driver = new $className($this->connection);
277
    }
278
}
279