1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Lagdo\DbAdmin\Driver\Db; |
4
|
|
|
|
5
|
|
|
use Lagdo\DbAdmin\Driver\Entity\TableFieldEntity; |
6
|
|
|
use Lagdo\DbAdmin\Driver\Entity\TableEntity; |
7
|
|
|
|
8
|
|
|
use Lagdo\DbAdmin\Driver\DriverInterface; |
9
|
|
|
use Lagdo\DbAdmin\Driver\UtilInterface; |
10
|
|
|
use Lagdo\DbAdmin\Driver\TranslatorInterface; |
11
|
|
|
use Lagdo\DbAdmin\Driver\Db\ConnectionInterface; |
12
|
|
|
|
13
|
|
|
abstract class Query implements QueryInterface |
14
|
|
|
{ |
15
|
|
|
/** |
16
|
|
|
* @var DriverInterface |
17
|
|
|
*/ |
18
|
|
|
protected $driver; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* @var UtilInterface |
22
|
|
|
*/ |
23
|
|
|
protected $util; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @var TranslatorInterface |
27
|
|
|
*/ |
28
|
|
|
protected $trans; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @var ConnectionInterface |
32
|
|
|
*/ |
33
|
|
|
protected $connection; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* The last error code |
37
|
|
|
* |
38
|
|
|
* @var int |
39
|
|
|
*/ |
40
|
|
|
protected $errno = 0; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* The last error message |
44
|
|
|
* |
45
|
|
|
* @var string |
46
|
|
|
*/ |
47
|
|
|
protected $error = ''; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* The number of rows affected by the last query |
51
|
|
|
* |
52
|
|
|
* @var int |
53
|
|
|
*/ |
54
|
|
|
protected $affectedRows; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* Executed queries |
58
|
|
|
* |
59
|
|
|
* @var array |
60
|
|
|
*/ |
61
|
|
|
protected $queries = []; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Query start timestamp |
65
|
|
|
* |
66
|
|
|
* @var int |
67
|
|
|
*/ |
68
|
|
|
protected $start = 0; |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* The constructor |
72
|
|
|
* |
73
|
|
|
* @param DriverInterface $driver |
74
|
|
|
* @param UtilInterface $util |
75
|
|
|
* @param TranslatorInterface $trans |
76
|
|
|
* @param ConnectionInterface $connection |
77
|
|
|
*/ |
78
|
|
|
public function __construct(DriverInterface $driver, UtilInterface $util, |
79
|
|
|
TranslatorInterface $trans, ConnectionInterface $connection) |
80
|
|
|
{ |
81
|
|
|
$this->driver = $driver; |
82
|
|
|
$this->util = $util; |
83
|
|
|
$this->trans = $trans; |
84
|
|
|
$this->connection = $connection; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* @inheritDoc |
89
|
|
|
*/ |
90
|
|
|
public function schema() |
91
|
|
|
{ |
92
|
|
|
return ""; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* @inheritDoc |
97
|
|
|
*/ |
98
|
|
|
public function select(string $table, array $select, array $where, |
99
|
|
|
array $group, array $order = [], int $limit = 1, int $page = 0) |
100
|
|
|
{ |
101
|
|
|
$query = $this->driver->buildSelectQuery($table, $select, $where, $group, $order, $limit, $page); |
102
|
|
|
// $start = microtime(true); |
103
|
|
|
return $this->connection->query($query); |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* @inheritDoc |
108
|
|
|
*/ |
109
|
|
|
public function insert(string $table, array $values) |
110
|
|
|
{ |
111
|
|
|
$table = $this->driver->table($table); |
112
|
|
|
if (!empty($values)) { |
113
|
|
|
return $this->execute("INSERT INTO $table DEFAULT VALUES"); |
114
|
|
|
} |
115
|
|
|
return $this->execute("INSERT INTO $table (" . |
116
|
|
|
implode(", ", array_keys($values)) . ") VALUES (" . implode(", ", $values) . ")"); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* @inheritDoc |
121
|
|
|
*/ |
122
|
|
|
public function update(string $table, array $values, string $queryWhere, int $limit = 0, string $separator = "\n") |
123
|
|
|
{ |
124
|
|
|
$assignments = []; |
125
|
|
|
foreach ($values as $name => $value) { |
126
|
|
|
$assignments[] = "$name = $value"; |
127
|
|
|
} |
128
|
|
|
$query = $this->driver->table($table) . " SET$separator" . implode(",$separator", $assignments); |
129
|
|
|
if (!$limit) { |
130
|
|
|
return $this->execute('UPDATE ' . $query . $queryWhere); |
131
|
|
|
} |
132
|
|
|
return $this->execute('UPDATE' . $this->driver->limitToOne($table, $query, $queryWhere, $separator)); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* @inheritDoc |
137
|
|
|
*/ |
138
|
|
|
public function delete(string $table, string $queryWhere, int $limit = 0) |
139
|
|
|
{ |
140
|
|
|
$query = 'FROM ' . $this->driver->table($table); |
141
|
|
|
if (!$limit) { |
142
|
|
|
return $this->execute("DELETE $query $queryWhere"); |
143
|
|
|
} |
144
|
|
|
return $this->execute('DELETE' . $this->driver->limitToOne($table, $query, $queryWhere)); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* @inheritDoc |
149
|
|
|
*/ |
150
|
|
|
public function explain(ConnectionInterface $connection, string $query) |
151
|
|
|
{ |
152
|
|
|
return null; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* @inheritDoc |
157
|
|
|
*/ |
158
|
|
|
public function slowQuery(string $query, int $timeout) |
159
|
|
|
{ |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* @inheritDoc |
164
|
|
|
*/ |
165
|
|
|
public function setError(string $error = '') |
166
|
|
|
{ |
167
|
|
|
$this->error = $error; |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* @inheritDoc |
172
|
|
|
*/ |
173
|
|
|
public function error() |
174
|
|
|
{ |
175
|
|
|
return $this->error; |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* @inheritDoc |
180
|
|
|
*/ |
181
|
|
|
public function hasError() |
182
|
|
|
{ |
183
|
|
|
return $this->error !== ''; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* @inheritDoc |
188
|
|
|
*/ |
189
|
|
|
public function setErrno($errno) |
190
|
|
|
{ |
191
|
|
|
$this->errno = $errno; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* @inheritDoc |
196
|
|
|
*/ |
197
|
|
|
public function errno() |
198
|
|
|
{ |
199
|
|
|
return $this->errno; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* @inheritDoc |
204
|
|
|
*/ |
205
|
|
|
public function hasErrno() |
206
|
|
|
{ |
207
|
|
|
return $this->errno !== 0; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* @inheritDoc |
212
|
|
|
*/ |
213
|
|
|
public function setAffectedRows($affectedRows) |
214
|
|
|
{ |
215
|
|
|
$this->affectedRows = $affectedRows; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* @inheritDoc |
220
|
|
|
*/ |
221
|
|
|
public function affectedRows() |
222
|
|
|
{ |
223
|
|
|
return $this->affectedRows; |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
/** |
227
|
|
|
* @inheritDoc |
228
|
|
|
*/ |
229
|
|
|
public function execute(string $query) |
230
|
|
|
{ |
231
|
|
|
if (!$this->start) { |
232
|
|
|
$this->start = microtime(true); |
|
|
|
|
233
|
|
|
} |
234
|
|
|
$this->queries[] = (preg_match('~;$~', $query) ? "DELIMITER ;;\n$query;\nDELIMITER " : $query) . ";"; |
235
|
|
|
return $this->connection->query($query); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* @inheritDoc |
240
|
|
|
*/ |
241
|
|
|
public function queries() |
242
|
|
|
{ |
243
|
|
|
return [implode("\n", $this->queries), $this->trans->formatTime($this->start)]; |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* @inheritDoc |
248
|
|
|
*/ |
249
|
|
|
public function applyQueries(string $query, array $tables, $escape = null) |
250
|
|
|
{ |
251
|
|
|
if (!$escape) { |
252
|
|
|
$escape = function ($table) { |
253
|
|
|
return $this->driver->table($table); |
254
|
|
|
}; |
255
|
|
|
} |
256
|
|
|
foreach ($tables as $table) { |
257
|
|
|
if (!$this->execute("$query " . $escape($table))) { |
258
|
|
|
return false; |
259
|
|
|
} |
260
|
|
|
} |
261
|
|
|
return true; |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
/** |
265
|
|
|
* @inheritDoc |
266
|
|
|
*/ |
267
|
|
|
public function values(string $query, $column = 0) |
268
|
|
|
{ |
269
|
|
|
$values = []; |
270
|
|
|
$statement = $this->connection->query($query); |
271
|
|
|
if (is_object($statement)) { |
272
|
|
|
while ($row = $statement->fetchRow()) { |
273
|
|
|
$values[] = $row[$column]; |
274
|
|
|
} |
275
|
|
|
} |
276
|
|
|
return $values; |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
/** |
280
|
|
|
* @inheritDoc |
281
|
|
|
*/ |
282
|
|
|
public function keyValues(string $query, ConnectionInterface $connection = null, bool $setKeys = true) |
283
|
|
|
{ |
284
|
|
|
if (!is_object($connection)) { |
285
|
|
|
$connection = $this->connection; |
286
|
|
|
} |
287
|
|
|
$values = []; |
288
|
|
|
$statement = $connection->query($query); |
289
|
|
|
if (is_object($statement)) { |
290
|
|
|
while ($row = $statement->fetchRow()) { |
291
|
|
|
if ($setKeys) { |
292
|
|
|
$values[$row[0]] = $row[1]; |
293
|
|
|
} else { |
294
|
|
|
$values[] = $row[0]; |
295
|
|
|
} |
296
|
|
|
} |
297
|
|
|
} |
298
|
|
|
return $values; |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
/** |
302
|
|
|
* @inheritDoc |
303
|
|
|
*/ |
304
|
|
|
public function rows(string $query, ConnectionInterface $connection = null) |
305
|
|
|
{ |
306
|
|
|
if (!$connection) { |
307
|
|
|
$connection = $this->connection; |
308
|
|
|
} |
309
|
|
|
$statement = $connection->query($query); |
310
|
|
|
if (!is_object($statement)) { // can return true |
311
|
|
|
return []; |
312
|
|
|
} |
313
|
|
|
$rows = []; |
314
|
|
|
while ($row = $statement->fetchAssoc()) { |
315
|
|
|
$rows[] = $row; |
316
|
|
|
} |
317
|
|
|
return $rows; |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* @inheritDoc |
322
|
|
|
*/ |
323
|
|
|
public function removeDefiner(string $query) |
324
|
|
|
{ |
325
|
|
|
return preg_replace('~^([A-Z =]+) DEFINER=`' . |
326
|
|
|
preg_replace('~@(.*)~', '`@`(%|\1)', $this->user()) . |
327
|
|
|
'`~', '\1', $query); //! proper escaping of user |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
/** |
331
|
|
|
* @inheritDoc |
332
|
|
|
*/ |
333
|
|
|
public function begin() |
334
|
|
|
{ |
335
|
|
|
return $this->connection->query("BEGIN"); |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
/** |
339
|
|
|
* @inheritDoc |
340
|
|
|
*/ |
341
|
|
|
public function commit() |
342
|
|
|
{ |
343
|
|
|
return $this->connection->query("COMMIT"); |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
/** |
347
|
|
|
* @inheritDoc |
348
|
|
|
*/ |
349
|
|
|
public function rollback() |
350
|
|
|
{ |
351
|
|
|
return $this->connection->query("ROLLBACK"); |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
/** |
355
|
|
|
* @inheritDoc |
356
|
|
|
*/ |
357
|
|
|
public function countRows(TableEntity $tableStatus, array $where) |
358
|
|
|
{ |
359
|
|
|
return null; |
360
|
|
|
} |
361
|
|
|
|
362
|
|
|
/** |
363
|
|
|
* @inheritDoc |
364
|
|
|
*/ |
365
|
|
|
public function convertSearch(string $idf, array $val, TableFieldEntity $field) |
366
|
|
|
{ |
367
|
|
|
return $idf; |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
/** |
371
|
|
|
* @inheritDoc |
372
|
|
|
*/ |
373
|
|
|
public function view(string $name) |
374
|
|
|
{ |
375
|
|
|
return []; |
376
|
|
|
} |
377
|
|
|
} |
378
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.