Passed
Push — main ( b31323...d7d864 )
by Thierry
02:22
created

TableDumpTrait::dumpTableOrView()   B

Complexity

Conditions 9
Paths 10

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 17
c 0
b 0
f 0
nc 10
nop 3
dl 0
loc 27
rs 8.0555
1
<?php
2
3
namespace Lagdo\DbAdmin\DbAdmin\Traits;
4
5
use Lagdo\DbAdmin\Driver\Db\StatementInterface;
6
use Lagdo\DbAdmin\Driver\Entity\TableFieldEntity;
7
8
use function count;
9
use function strlen;
10
use function preg_match;
11
use function str_replace;
12
use function implode;
13
use function is_numeric;
14
use function array_keys;
15
use function array_map;
16
17
trait TableDumpTrait
18
{
19
    /**
20
     * The dump options
21
     *
22
     * @var array
23
     */
24
    private $options;
25
26
    /**
27
     * The queries generated by the dump
28
     *
29
     * @var array
30
     */
31
    private $queries = [];
32
33
    // Temp vars for data dumps
34
    private $insert = '';
35
    private $buffer = '';
36
    private $suffix = '';
37
    private $views = [];
38
    private $fkeys = [];
39
40
    /**
41
     * Print CSV row
42
     *
43
     * @param array  $row
44
     *
45
     * @return void
46
     */
47
    private function dumpCsv(array $row)
48
    {
49
        // From functions.inc.php
50
        foreach ($row as $key => $val) {
51
            if (preg_match('~["\n,;\t]|^0|\.\d*0$~', $val) || $val === '') {
52
                $row[$key] = '"' . str_replace('"', '""', $val) . '"';
53
            }
54
        }
55
        $separator = $this->options['format'] === 'csv' ? ',' :
56
            ($this->options['format'] === 'tsv' ? "\t" : ';');
57
        $this->queries[] = implode($separator, $row);
58
    }
59
60
    /**
61
     * Convert a value to string
62
     *
63
     * @param mixed  $value
64
     * @param TableFieldEntity $field
65
     *
66
     * @return string
67
     */
68
    private function convertToString($value, TableFieldEntity $field): string
69
    {
70
        // From functions.inc.php
71
        if ($value === null) {
72
            return 'NULL';
73
        }
74
        if (!preg_match($this->driver->numberRegex(), $field->type) ||
75
            preg_match('~\[~', $field->fullType) && is_numeric($value)) {
76
            $value = $this->driver->quote(($value === false ? 0 : $value));
77
        }
78
        return $this->driver->unconvertField($field, $value);
79
    }
80
81
    /**
82
     * @param string $table
83
     * @param string $style
84
     * @param int $tableType
85
     *
86
     * @return string
87
     */
88
    private function getCreateTableQuery(string $table, string $style, int $tableType): string
89
    {
90
        if ($tableType !== 2) {
91
            return $this->driver->sqlForCreateTable($table, $this->options['autoIncrement'], $style);
92
        }
93
        $fields = [];
94
        foreach ($this->driver->fields($table) as $name => $field) {
95
            $fields[] = $this->driver->escapeId($name) . ' ' . $field->fullType;
96
        }
97
        return 'CREATE TABLE ' . $this->driver->table($table) . ' (' . implode(', ', $fields) . ')';
98
    }
99
100
    /**
101
     * Export table structure
102
     *
103
     * @param string $table
104
     * @param string $style
105
     * @param int    $tableType       0 table, 1 view, 2 temporary view table
106
     *
107
     * @return void
108
     */
109
    private function dumpTableOrView(string $table, string $style, int $tableType = 0)
110
    {
111
        // From adminer.inc.php
112
        if ($this->options['format'] !== 'sql') {
113
            $this->queries[] = "\xef\xbb\xbf"; // UTF-8 byte order mark
114
            if ($style) {
115
                $this->dumpCsv(array_keys($this->driver->fields($table)));
116
            }
117
            return;
118
        }
119
        if (!$style) {
120
            return;
121
        }
122
123
        $create = $this->getCreateTableQuery($table, $style, $tableType);
124
        $this->driver->setUtf8mb4($create);
125
        if (!$create) {
126
            return;
127
        }
128
        if ($style === 'DROP+CREATE' || $tableType === 1) {
129
            $this->queries[] = 'DROP ' . ($tableType === 2 ? 'VIEW' : 'TABLE') .
130
                ' IF EXISTS ' . $this->driver->table($table) . ';';
131
        }
132
        if ($tableType === 1) {
133
            $create = $this->admin->removeDefiner($create);
134
        }
135
        $this->queries[] = $create . ';';
136
    }
137
138
    /**
139
     * @param array $row
140
     * @param StatementInterface $statement
141
     *
142
     * @return array
143
     */
144
    private function getDataRowKeys(array $row, StatementInterface $statement): array
145
    {
146
        $values = [];
147
        $keys = [];
148
        // For is preferred to foreach because the values are not used.
149
        // foreach ($row as $val) {
150
        // }
151
        for ($i = 0; $i < count($row); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
152
            $field = $statement->fetchField();
153
            $keys[] = $field->name();
154
            $key = $this->driver->escapeId($field->name());
155
            $values[] = "$key = VALUES($key)";
156
        }
157
        $this->suffix = ";\n";
158
        if ($this->options['data_style'] === 'INSERT+UPDATE') {
159
            $this->suffix = "\nON DUPLICATE KEY UPDATE " . implode(', ', $values) . ";\n";
160
        }
161
        return $keys;
162
    }
163
164
    /**
165
     * @param array $row
166
     *
167
     * @return void
168
     */
169
    private function saveRowInBuffer(array $row)
170
    {
171
        $max_packet = ($this->driver->jush() === 'sqlite' ? 0 : 1048576); // default, minimum is 1024
172
        $s = ($max_packet ? "\n" : ' ') . '(' . implode(",\t", $row) . ')';
173
        if (!$this->buffer) {
174
            $this->buffer = $this->insert . $s;
175
            return;
176
        }
177
        if (strlen($this->buffer) + 4 + strlen($s) + strlen($this->suffix) < $max_packet) { // 4 - length specification
178
            $this->buffer .= ",$s";
179
            return;
180
        }
181
        $this->queries[] = $this->buffer . $this->suffix;
182
        $this->buffer = $this->insert . $s;
183
    }
184
185
    /**
186
     * @param string $table
187
     * @param array $fields
188
     * @param array $row
189
     * @param array $keys
190
     *
191
     * @return void
192
     */
193
    private function dumpRow(string $table, array $fields, array $row, array $keys)
194
    {
195
        if ($this->options['format'] !== 'sql') {
196
            if ($this->options['data_style'] === 'table') {
197
                $this->dumpCsv($keys);
198
                $this->options['data_style'] = 'INSERT';
199
            }
200
            $this->dumpCsv($row);
201
            return;
202
        }
203
        if (!$this->insert) {
204
            $this->insert = 'INSERT INTO ' . $this->driver->table($table) . ' (' .
205
                implode(', ', array_map(function ($key) {
206
                    return $this->driver->escapeId($key);
207
                }, $keys)) . ') VALUES';
208
        }
209
        foreach ($row as $key => $val) {
210
            $field = $fields[$key];
211
            $row[$key] = $this->convertToString($val, $field);
212
        }
213
        $this->saveRowInBuffer($row);
214
    }
215
216
    /** Export table data
217
     *
218
     * @param string $table
219
     * @param string $query
220
     *
221
     * @return void
222
     */
223
    private function dumpData(string $table, string $query)
224
    {
225
        if (!$this->options['data_style']) {
226
            return;
227
        }
228
        $fields = [];
229
        if ($this->options['format'] === 'sql') {
230
            if ($this->options['data_style'] === 'TRUNCATE+INSERT') {
231
                $this->queries[] = $this->driver->sqlForTruncateTable($table) . ";\n";
232
            }
233
            $fields = $this->driver->fields($table);
234
        }
235
        $statement = $this->driver->query($query); // 1 - MYSQLI_USE_RESULT //! enum and set as numbers
236
        if (!$statement) {
237
            if ($this->options['format'] === 'sql') {
238
                $this->queries[] = '-- ' . str_replace("\n", ' ', $this->driver->error()) . "\n";
239
            }
240
            return;
241
        }
242
        $this->insert = '';
243
        $this->buffer = '';
244
        $this->suffix = '';
245
        $keys = [];
246
        $fetch_function = ($table !== '' ? 'fetchAssoc' : 'fetchRow');
247
        while ($row = $statement->$fetch_function()) {
248
            if (empty($keys)) {
249
                $keys = $this->getDataRowKeys($row, $statement);
250
            }
251
            $this->dumpRow($table, $fields, $row, $keys);
252
        }
253
        if (($this->buffer)) {
254
            $this->queries[] = $this->buffer . $this->suffix;
255
        }
256
    }
257
}
258