TableDataDumpTrait::getDataRowKeys()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 4
nop 2
dl 0
loc 19
rs 9.8666
c 0
b 0
f 0
1
<?php
2
3
namespace Lagdo\DbAdmin\Db\Facades\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_map;
15
16
trait TableDataDumpTrait
17
{
18
    /**
19
     * The queries generated by the dump
20
     *
21
     * @var array
22
     */
23
    private $queries = [];
24
25
    /**
26
     * The dump options
27
     *
28
     * @var array
29
     */
30
    private $options;
31
32
    // Temp vars for data dumps
33
    private $insert = '';
34
    private $buffer = '';
35
    private $suffix = '';
36
37
    /**
38
     * Print CSV row
39
     *
40
     * @param array  $row
41
     *
42
     * @return void
43
     */
44
    private function dumpCsv(array $row)
45
    {
46
        // From functions.inc.php
47
        foreach ($row as $key => $val) {
48
            if (preg_match('~["\n,;\t]|^0|\.\d*0$~', $val) || $val === '') {
49
                $row[$key] = '"' . str_replace('"', '""', $val) . '"';
50
            }
51
        }
52
        $separator = $this->options['format'] === 'csv' ? ',' :
53
            ($this->options['format'] === 'tsv' ? "\t" : ';');
54
        $this->queries[] = implode($separator, $row);
55
    }
56
57
    /**
58
     * Convert a value to string
59
     *
60
     * @param mixed  $value
61
     * @param TableFieldEntity $field
62
     *
63
     * @return string
64
     */
65
    private function convertToString($value, TableFieldEntity $field): string
66
    {
67
        // From functions.inc.php
68
        if ($value === null) {
69
            return 'NULL';
70
        }
71
        if (!preg_match($this->driver->numberRegex(), $field->type) ||
72
            preg_match('~\[~', $field->fullType) && is_numeric($value)) {
73
            $value = $this->driver->quote(($value === false ? 0 : $value));
74
        }
75
        return $this->driver->unconvertField($field, $value);
76
    }
77
78
    /**
79
     * @param array $row
80
     * @param StatementInterface $statement
81
     *
82
     * @return array
83
     */
84
    private function getDataRowKeys(array $row, StatementInterface $statement): array
85
    {
86
        $values = [];
87
        $keys = [];
88
        // For is preferred to foreach because the values are not used.
89
        // foreach ($row as $val) {
90
        // }
91
        $rowCount = count($row);
92
        for ($i = 0; $i < $rowCount; $i++) {
93
            $field = $statement->fetchField();
94
            $keys[] = $field->name();
95
            $key = $this->driver->escapeId($field->name());
96
            $values[] = "$key = VALUES($key)";
97
        }
98
        $this->suffix = ";\n";
99
        if ($this->options['data_style'] === 'INSERT+UPDATE') {
100
            $this->suffix = "\nON DUPLICATE KEY UPDATE " . implode(', ', $values) . ";\n";
101
        }
102
        return $keys;
103
    }
104
105
    /**
106
     * @param array $row
107
     *
108
     * @return void
109
     */
110
    private function saveRowInBuffer(array $row)
111
    {
112
        $max_packet = ($this->driver->jush() === 'sqlite' ? 0 : 1048576); // default, minimum is 1024
113
        $s = ($max_packet ? "\n" : ' ') . '(' . implode(",\t", $row) . ')';
114
        if (!$this->buffer) {
115
            $this->buffer = $this->insert . $s;
116
            return;
117
        }
118
        if (strlen($this->buffer) + 4 + strlen($s) + strlen($this->suffix) < $max_packet) { // 4 - length specification
119
            $this->buffer .= ",$s";
120
            return;
121
        }
122
        $this->queries[] = $this->buffer . $this->suffix;
123
        $this->buffer = $this->insert . $s;
124
    }
125
126
    /**
127
     * @param string $table
128
     * @param array $fields
129
     * @param array $row
130
     * @param array $keys
131
     *
132
     * @return void
133
     */
134
    private function dumpRow(string $table, array $fields, array $row, array $keys)
135
    {
136
        if ($this->options['format'] !== 'sql') {
137
            if ($this->options['data_style'] === 'table') {
138
                $this->dumpCsv($keys);
139
                $this->options['data_style'] = 'INSERT';
140
            }
141
            $this->dumpCsv($row);
142
            return;
143
        }
144
        if (!$this->insert) {
145
            $this->insert = 'INSERT INTO ' . $this->driver->escapeTableName($table) . ' (' .
146
                implode(', ', array_map(function ($key) {
147
                    return $this->driver->escapeId($key);
148
                }, $keys)) . ') VALUES';
149
        }
150
        foreach ($row as $key => $val) {
151
            $field = $fields[$key];
152
            $row[$key] = $this->convertToString($val, $field);
153
        }
154
        $this->saveRowInBuffer($row);
155
    }
156
157
    /**
158
     * @param string $table
159
     *
160
     * @return void
161
     */
162
    private function dumpTruncateQuery(string $table)
163
    {
164
        if ($this->options['format'] === 'sql' &&
165
            $this->options['data_style'] === 'TRUNCATE+INSERT') {
166
            $this->queries[] = $this->driver->getTruncateTableQuery($table) . ";\n";
167
        }
168
    }
169
170
    /**
171
     * @param string $table
172
     * @param StatementInterface $statement
173
     *
174
     * @return void
175
     */
176
    private function dumpRows(string $table, StatementInterface $statement)
177
    {
178
        $fields = $this->options['format'] !== 'sql' ? [] : $this->driver->fields($table);
179
        $keys = [];
180
        $fetch_function = ($table !== '' ? 'fetchAssoc' : 'fetchRow');
181
        while ($row = $statement->$fetch_function()) {
182
            if (empty($keys)) {
183
                $keys = $this->getDataRowKeys($row, $statement);
184
            }
185
            $this->dumpRow($table, $fields, $row, $keys);
186
        }
187
    }
188
189
    /** Export table data
190
     *
191
     * @param string $table
192
     *
193
     * @return void
194
     */
195
    private function dumpTableData(string $table)
196
    {
197
        if (!$this->options['data_style']) {
198
            return;
199
        }
200
        $fields = $this->driver->fields($table);
201
        $query = 'SELECT *' . $this->driver->convertFields($fields, $fields) . ' FROM ' . $this->driver->escapeTableName($table);
202
        $statement = $this->driver->query($query); // 1 - MYSQLI_USE_RESULT //! enum and set as numbers
203
        if (!$statement) {
204
            if ($this->options['format'] === 'sql') {
205
                $this->queries[] = '-- ' . str_replace("\n", ' ', $this->driver->error()) . "\n";
206
            }
207
            return;
208
        }
209
210
        $this->insert = '';
211
        $this->buffer = '';
212
        $this->suffix = '';
213
        $this->dumpTruncateQuery($table);
214
        $this->dumpRows($table, $statement);
215
        if (!empty($this->buffer)) {
216
            $this->queries[] = $this->buffer . $this->suffix;
217
        }
218
    }
219
}
220