|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
declare(strict_types=1); |
|
4
|
|
|
|
|
5
|
|
|
namespace voku\db; |
|
6
|
|
|
|
|
7
|
|
|
use Doctrine\DBAL\Connection; |
|
8
|
|
|
use voku\cache\Cache; |
|
9
|
|
|
use voku\db\exceptions\DBConnectException; |
|
10
|
|
|
use voku\db\exceptions\DBGoneAwayException; |
|
11
|
|
|
use voku\db\exceptions\QueryException; |
|
12
|
|
|
use voku\helper\UTF8; |
|
13
|
|
|
|
|
14
|
|
|
/** |
|
15
|
|
|
* DB: This class can handle DB queries via MySQLi. |
|
16
|
|
|
*/ |
|
17
|
|
|
final class DB |
|
18
|
|
|
{ |
|
19
|
|
|
/** |
|
20
|
|
|
* @var int |
|
21
|
|
|
*/ |
|
22
|
|
|
public $query_count = 0; |
|
23
|
|
|
|
|
24
|
|
|
/** |
|
25
|
|
|
* @var \mysqli|null |
|
26
|
|
|
*/ |
|
27
|
|
|
private $mysqli_link; |
|
28
|
|
|
|
|
29
|
|
|
/** |
|
30
|
|
|
* @var bool |
|
31
|
|
|
*/ |
|
32
|
|
|
private $connected = false; |
|
33
|
|
|
|
|
34
|
|
|
/** |
|
35
|
|
|
* @var array |
|
36
|
|
|
*/ |
|
37
|
|
|
private $mysqlDefaultTimeFunctions; |
|
38
|
|
|
|
|
39
|
|
|
/** |
|
40
|
|
|
* @var string |
|
41
|
|
|
*/ |
|
42
|
|
|
private $hostname = ''; |
|
43
|
|
|
|
|
44
|
|
|
/** |
|
45
|
|
|
* @var string |
|
46
|
|
|
*/ |
|
47
|
|
|
private $username = ''; |
|
48
|
|
|
|
|
49
|
|
|
/** |
|
50
|
|
|
* @var string |
|
51
|
|
|
*/ |
|
52
|
|
|
private $password = ''; |
|
53
|
|
|
|
|
54
|
|
|
/** |
|
55
|
|
|
* @var string |
|
56
|
|
|
*/ |
|
57
|
|
|
private $database = ''; |
|
58
|
|
|
|
|
59
|
|
|
/** |
|
60
|
|
|
* @var int |
|
61
|
|
|
*/ |
|
62
|
|
|
private $port = 3306; |
|
63
|
|
|
|
|
64
|
|
|
/** |
|
65
|
|
|
* @var string |
|
66
|
|
|
*/ |
|
67
|
|
|
private $charset = 'utf8'; |
|
68
|
|
|
|
|
69
|
|
|
/** |
|
70
|
|
|
* @var string |
|
71
|
|
|
*/ |
|
72
|
|
|
private $socket = ''; |
|
73
|
|
|
|
|
74
|
|
|
/** |
|
75
|
|
|
* @var int|null |
|
76
|
|
|
*/ |
|
77
|
|
|
private $flags; |
|
78
|
|
|
|
|
79
|
|
|
/** |
|
80
|
|
|
* @var bool |
|
81
|
|
|
*/ |
|
82
|
|
|
private $session_to_db = false; |
|
83
|
|
|
|
|
84
|
|
|
/** |
|
85
|
|
|
* @var bool |
|
86
|
|
|
*/ |
|
87
|
|
|
private $in_transaction = false; |
|
88
|
|
|
|
|
89
|
|
|
/** |
|
90
|
|
|
* @var bool |
|
91
|
|
|
*/ |
|
92
|
|
|
private $convert_null_to_empty_string = false; |
|
93
|
|
|
|
|
94
|
|
|
/** |
|
95
|
|
|
* @var bool |
|
96
|
|
|
*/ |
|
97
|
|
|
private $ssl = false; |
|
98
|
|
|
|
|
99
|
|
|
/** |
|
100
|
|
|
* The path name to the key file |
|
101
|
|
|
* |
|
102
|
|
|
* @var string |
|
103
|
|
|
*/ |
|
104
|
|
|
private $clientkey; |
|
105
|
|
|
|
|
106
|
|
|
/** |
|
107
|
|
|
* The path name to the certificate file |
|
108
|
|
|
* |
|
109
|
|
|
* @var string |
|
110
|
|
|
*/ |
|
111
|
|
|
private $clientcert; |
|
112
|
|
|
|
|
113
|
|
|
/** |
|
114
|
|
|
* The path name to the certificate authority file |
|
115
|
|
|
* |
|
116
|
|
|
* @var string |
|
117
|
|
|
*/ |
|
118
|
|
|
private $cacert; |
|
119
|
|
|
|
|
120
|
|
|
/** |
|
121
|
|
|
* @var Debug |
|
122
|
|
|
*/ |
|
123
|
|
|
private $debug; |
|
124
|
|
|
|
|
125
|
|
|
/** |
|
126
|
|
|
* @var \Doctrine\DBAL\Connection|null |
|
127
|
|
|
*/ |
|
128
|
|
|
private $doctrine_connection; |
|
129
|
|
|
|
|
130
|
|
|
/** |
|
131
|
|
|
* @var int |
|
132
|
|
|
*/ |
|
133
|
|
|
private $affected_rows = 0; |
|
134
|
|
|
|
|
135
|
|
|
/** |
|
136
|
|
|
* __construct() |
|
137
|
|
|
* |
|
138
|
|
|
* @param string $hostname |
|
139
|
|
|
* @param string $username |
|
140
|
|
|
* @param string $password |
|
141
|
|
|
* @param string $database |
|
142
|
|
|
* @param int $port |
|
143
|
|
|
* @param string $charset |
|
144
|
|
|
* @param bool $exit_on_error <p>Throw a 'Exception' when a query failed, otherwise it will return |
|
145
|
|
|
* 'false'. Use false to disable it.</p> |
|
146
|
|
|
* @param bool $echo_on_error <p>Echo the error if "checkForDev()" returns true. |
|
147
|
|
|
* Use false to disable it.</p> |
|
148
|
|
|
* @param string $logger_class_name |
|
149
|
|
|
* @param string $logger_level <p>'TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'</p> |
|
150
|
|
|
* @param array $extra_config <p> |
|
151
|
|
|
* 'session_to_db' => bool<br> |
|
152
|
|
|
* 'doctrine' => \Doctrine\DBAL\Connection<br> |
|
153
|
|
|
* 'socket' => string (path)<br> |
|
154
|
|
|
* 'flags' => null|int<br> |
|
155
|
|
|
* 'ssl' => bool<br> |
|
156
|
|
|
* 'clientkey' => string (path)<br> |
|
157
|
|
|
* 'clientcert' => string (path)<br> |
|
158
|
|
|
* 'cacert' => string (path)<br> |
|
159
|
|
|
* </p> |
|
160
|
|
|
*/ |
|
161
|
24 |
|
private function __construct( |
|
162
|
|
|
string $hostname, |
|
163
|
|
|
string $username, |
|
164
|
|
|
string $password, |
|
165
|
|
|
string $database, |
|
166
|
|
|
$port, |
|
167
|
|
|
string $charset, |
|
168
|
|
|
bool $exit_on_error, |
|
169
|
|
|
bool $echo_on_error, |
|
170
|
|
|
string $logger_class_name, |
|
171
|
|
|
string $logger_level, |
|
172
|
|
|
array $extra_config = [] |
|
173
|
|
|
) { |
|
174
|
24 |
|
$this->debug = new Debug($this); |
|
175
|
|
|
|
|
176
|
24 |
|
$this->_loadConfig( |
|
177
|
24 |
|
$hostname, |
|
178
|
24 |
|
$username, |
|
179
|
24 |
|
$password, |
|
180
|
24 |
|
$database, |
|
181
|
24 |
|
$port, |
|
182
|
24 |
|
$charset, |
|
183
|
24 |
|
$exit_on_error, |
|
184
|
24 |
|
$echo_on_error, |
|
185
|
24 |
|
$logger_class_name, |
|
186
|
24 |
|
$logger_level, |
|
187
|
24 |
|
$extra_config |
|
188
|
|
|
); |
|
189
|
|
|
|
|
190
|
15 |
|
$this->connect(); |
|
191
|
|
|
|
|
192
|
6 |
|
$this->mysqlDefaultTimeFunctions = [ |
|
193
|
|
|
// Returns the current date. |
|
194
|
|
|
'CURDATE()', |
|
195
|
|
|
// CURRENT_DATE | Synonyms for CURDATE() |
|
196
|
|
|
'CURRENT_DATE()', |
|
197
|
|
|
// CURRENT_TIME | Synonyms for CURTIME() |
|
198
|
|
|
'CURRENT_TIME()', |
|
199
|
|
|
// CURRENT_TIMESTAMP | Synonyms for NOW() |
|
200
|
|
|
'CURRENT_TIMESTAMP()', |
|
201
|
|
|
// Returns the current time. |
|
202
|
|
|
'CURTIME()', |
|
203
|
|
|
// Synonym for NOW() |
|
204
|
|
|
'LOCALTIME()', |
|
205
|
|
|
// Synonym for NOW() |
|
206
|
|
|
'LOCALTIMESTAMP()', |
|
207
|
|
|
// Returns the current date and time. |
|
208
|
|
|
'NOW()', |
|
209
|
|
|
// Returns the time at which the function executes. |
|
210
|
|
|
'SYSDATE()', |
|
211
|
|
|
// Returns a UNIX timestamp. |
|
212
|
|
|
'UNIX_TIMESTAMP()', |
|
213
|
|
|
// Returns the current UTC date. |
|
214
|
|
|
'UTC_DATE()', |
|
215
|
|
|
// Returns the current UTC time. |
|
216
|
|
|
'UTC_TIME()', |
|
217
|
|
|
// Returns the current UTC date and time. |
|
218
|
|
|
'UTC_TIMESTAMP()', |
|
219
|
|
|
]; |
|
220
|
6 |
|
} |
|
221
|
|
|
|
|
222
|
|
|
/** |
|
223
|
|
|
* Prevent the instance from being cloned. |
|
224
|
|
|
* |
|
225
|
|
|
* @return void |
|
226
|
|
|
*/ |
|
227
|
|
|
private function __clone() |
|
228
|
|
|
{ |
|
229
|
|
|
} |
|
230
|
|
|
|
|
231
|
|
|
/** |
|
232
|
|
|
* __destruct |
|
233
|
|
|
*/ |
|
234
|
|
|
public function __destruct() |
|
235
|
|
|
{ |
|
236
|
|
|
// close the connection only if we don't save PHP-SESSION's in DB |
|
237
|
|
|
if (!$this->session_to_db) { |
|
238
|
|
|
$this->close(); |
|
239
|
|
|
} |
|
240
|
|
|
} |
|
241
|
|
|
|
|
242
|
|
|
/** |
|
243
|
|
|
* @param string|null $sql |
|
244
|
|
|
* @param array $bindings |
|
245
|
|
|
* |
|
246
|
|
|
* @return bool|DB|int|Result|string |
|
247
|
|
|
* <p> |
|
248
|
|
|
* "DB" by "$sql" === null<br /> |
|
249
|
|
|
* "Result" by "<b>SELECT</b>"-queries<br /> |
|
250
|
|
|
* "int|string" (insert_id) by "<b>INSERT / REPLACE</b>"-queries<br /> |
|
251
|
|
|
* "int" (affected_rows) by "<b>UPDATE / DELETE</b>"-queries<br /> |
|
252
|
|
|
* "true" by e.g. "DROP"-queries<br /> |
|
253
|
|
|
* "false" on error |
|
254
|
|
|
* </p> |
|
255
|
|
|
*/ |
|
256
|
4 |
|
public function __invoke(string $sql = null, array $bindings = []) |
|
257
|
|
|
{ |
|
258
|
4 |
|
return $sql !== null ? $this->query($sql, $bindings) : $this; |
|
259
|
|
|
} |
|
260
|
|
|
|
|
261
|
|
|
/** |
|
262
|
|
|
* __wakeup |
|
263
|
|
|
* |
|
264
|
|
|
* @return void |
|
265
|
|
|
*/ |
|
266
|
4 |
|
public function __wakeup() |
|
267
|
|
|
{ |
|
268
|
4 |
|
$this->reconnect(); |
|
269
|
4 |
|
} |
|
270
|
|
|
|
|
271
|
|
|
/** |
|
272
|
|
|
* Load the config from the constructor. |
|
273
|
|
|
* |
|
274
|
|
|
* @param string $hostname |
|
275
|
|
|
* @param string $username |
|
276
|
|
|
* @param string $password |
|
277
|
|
|
* @param string $database |
|
278
|
|
|
* @param int $port <p>default is (int)3306</p> |
|
279
|
|
|
* @param string $charset <p>default is 'utf8' or 'utf8mb4' (if supported)</p> |
|
280
|
|
|
* @param bool $exit_on_error <p>Throw a 'Exception' when a query failed, otherwise it will return |
|
281
|
|
|
* 'false'. Use false to disable it.</p> |
|
282
|
|
|
* @param bool $echo_on_error <p>Echo the error if "checkForDev()" returns true. |
|
283
|
|
|
* Use false to disable it.</p> |
|
284
|
|
|
* @param string $logger_class_name |
|
285
|
|
|
* @param string $logger_level |
|
286
|
|
|
* @param array $extra_config <p> |
|
287
|
|
|
* 'session_to_db' => bool<br> |
|
288
|
|
|
* 'doctrine' => \Doctrine\DBAL\Connection<br> |
|
289
|
|
|
* 'socket' => string (path)<br> |
|
290
|
|
|
* 'flags' => null|int<br> |
|
291
|
|
|
* 'ssl' => bool<br> |
|
292
|
|
|
* 'clientkey' => string (path)<br> |
|
293
|
|
|
* 'clientcert' => string (path)<br> |
|
294
|
|
|
* 'cacert' => string (path)<br> |
|
295
|
|
|
* </p> |
|
296
|
|
|
* |
|
297
|
|
|
* @return bool |
|
298
|
|
|
*/ |
|
299
|
24 |
|
private function _loadConfig( |
|
300
|
|
|
string $hostname, |
|
301
|
|
|
string $username, |
|
302
|
|
|
string $password, |
|
303
|
|
|
string $database, |
|
304
|
|
|
$port, |
|
305
|
|
|
string $charset, |
|
306
|
|
|
bool $exit_on_error, |
|
307
|
|
|
bool $echo_on_error, |
|
308
|
|
|
string $logger_class_name, |
|
309
|
|
|
string $logger_level, |
|
310
|
|
|
array $extra_config = [] |
|
311
|
|
|
): bool { |
|
312
|
24 |
|
$this->hostname = $hostname; |
|
313
|
24 |
|
$this->username = $username; |
|
314
|
24 |
|
$this->password = $password; |
|
315
|
24 |
|
$this->database = $database; |
|
316
|
|
|
|
|
317
|
24 |
|
if ($charset) { |
|
318
|
24 |
|
$this->charset = $charset; |
|
319
|
|
|
} |
|
320
|
|
|
|
|
321
|
24 |
|
if ($port) { |
|
322
|
12 |
|
$this->port = (int) $port; |
|
323
|
|
|
} else { |
|
324
|
|
|
/** @noinspection PhpUsageOfSilenceOperatorInspection */ |
|
325
|
13 |
|
$this->port = (int) @\ini_get('mysqli.default_port'); |
|
326
|
|
|
} |
|
327
|
|
|
|
|
328
|
|
|
// fallback |
|
329
|
24 |
|
if (!$this->port) { |
|
330
|
|
|
$this->port = 3306; |
|
331
|
|
|
} |
|
332
|
|
|
|
|
333
|
|
|
/** @noinspection PhpUsageOfSilenceOperatorInspection */ |
|
334
|
|
|
if ( |
|
335
|
24 |
|
!$this->socket |
|
336
|
|
|
&& |
|
337
|
24 |
|
($defaultSocket = @\ini_get('mysqli.default_socket')) |
|
338
|
|
|
&& |
|
339
|
24 |
|
\is_readable($defaultSocket) |
|
340
|
|
|
) { |
|
341
|
24 |
|
$this->socket = $defaultSocket; |
|
342
|
|
|
} |
|
343
|
|
|
|
|
344
|
24 |
|
$this->debug->setExitOnError($exit_on_error); |
|
345
|
24 |
|
$this->debug->setEchoOnError($echo_on_error); |
|
346
|
|
|
|
|
347
|
24 |
|
$this->debug->setLoggerClassName($logger_class_name); |
|
348
|
24 |
|
$this->debug->setLoggerLevel($logger_level); |
|
349
|
|
|
|
|
350
|
24 |
|
$this->setConfigExtra($extra_config); |
|
351
|
|
|
|
|
352
|
24 |
|
return $this->showConfigError(); |
|
353
|
|
|
} |
|
354
|
|
|
|
|
355
|
|
|
/** |
|
356
|
|
|
* Parses arrays with value pairs and generates SQL to use in queries. |
|
357
|
|
|
* |
|
358
|
|
|
* @param array $arrayPair |
|
359
|
|
|
* @param string $glue <p>This is the separator.</p> |
|
360
|
|
|
* |
|
361
|
|
|
* @return string |
|
362
|
|
|
* |
|
363
|
|
|
* @internal |
|
364
|
|
|
*/ |
|
365
|
72 |
|
public function _parseArrayPair(array $arrayPair, string $glue = ','): string |
|
366
|
|
|
{ |
|
367
|
|
|
// init |
|
368
|
72 |
|
$sql = ''; |
|
369
|
|
|
|
|
370
|
72 |
|
if (\count($arrayPair) === 0) { |
|
371
|
|
|
return ''; |
|
372
|
|
|
} |
|
373
|
|
|
|
|
374
|
72 |
|
$arrayPairCounter = 0; |
|
375
|
72 |
|
foreach ($arrayPair as $_key => $_value) { |
|
376
|
72 |
|
$_connector = '='; |
|
377
|
72 |
|
$_glueHelper = ''; |
|
378
|
72 |
|
$_key_upper = \strtoupper((string) $_key); |
|
379
|
|
|
|
|
380
|
72 |
|
if (\strpos($_key_upper, ' NOT') !== false) { |
|
381
|
6 |
|
$_connector = 'NOT'; |
|
382
|
|
|
} |
|
383
|
|
|
|
|
384
|
72 |
|
if (\strpos($_key_upper, ' IS') !== false) { |
|
385
|
3 |
|
$_connector = 'IS'; |
|
386
|
|
|
} |
|
387
|
|
|
|
|
388
|
72 |
|
if (\strpos($_key_upper, ' IS NOT') !== false) { |
|
389
|
3 |
|
$_connector = 'IS NOT'; |
|
390
|
|
|
} |
|
391
|
|
|
|
|
392
|
72 |
|
if (\strpos($_key_upper, ' IN') !== false) { |
|
393
|
3 |
|
$_connector = 'IN'; |
|
394
|
|
|
} |
|
395
|
|
|
|
|
396
|
72 |
|
if (\strpos($_key_upper, ' NOT IN') !== false) { |
|
397
|
3 |
|
$_connector = 'NOT IN'; |
|
398
|
|
|
} |
|
399
|
|
|
|
|
400
|
72 |
|
if (\strpos($_key_upper, ' BETWEEN') !== false) { |
|
401
|
3 |
|
$_connector = 'BETWEEN'; |
|
402
|
|
|
} |
|
403
|
|
|
|
|
404
|
72 |
|
if (\strpos($_key_upper, ' NOT BETWEEN') !== false) { |
|
405
|
3 |
|
$_connector = 'NOT BETWEEN'; |
|
406
|
|
|
} |
|
407
|
|
|
|
|
408
|
72 |
|
if (\strpos($_key_upper, ' LIKE') !== false) { |
|
409
|
6 |
|
$_connector = 'LIKE'; |
|
410
|
|
|
} |
|
411
|
|
|
|
|
412
|
72 |
|
if (\strpos($_key_upper, ' NOT LIKE') !== false) { |
|
413
|
6 |
|
$_connector = 'NOT LIKE'; |
|
414
|
|
|
} |
|
415
|
|
|
|
|
416
|
72 |
View Code Duplication |
if (\strpos($_key_upper, ' >') !== false && \strpos($_key_upper, ' =') === false) { |
|
417
|
8 |
|
$_connector = '>'; |
|
418
|
|
|
} |
|
419
|
|
|
|
|
420
|
72 |
View Code Duplication |
if (\strpos($_key_upper, ' <') !== false && \strpos($_key_upper, ' =') === false) { |
|
421
|
3 |
|
$_connector = '<'; |
|
422
|
|
|
} |
|
423
|
|
|
|
|
424
|
72 |
|
if (\strpos($_key_upper, ' >=') !== false) { |
|
425
|
8 |
|
$_connector = '>='; |
|
426
|
|
|
} |
|
427
|
|
|
|
|
428
|
72 |
|
if (\strpos($_key_upper, ' <=') !== false) { |
|
429
|
3 |
|
$_connector = '<='; |
|
430
|
|
|
} |
|
431
|
|
|
|
|
432
|
72 |
|
if (\strpos($_key_upper, ' <>') !== false) { |
|
433
|
3 |
|
$_connector = '<>'; |
|
434
|
|
|
} |
|
435
|
|
|
|
|
436
|
72 |
|
if (\strpos($_key_upper, ' OR') !== false) { |
|
437
|
6 |
|
$_glueHelper = 'OR'; |
|
438
|
|
|
} |
|
439
|
|
|
|
|
440
|
72 |
|
if (\strpos($_key_upper, ' AND') !== false) { |
|
441
|
3 |
|
$_glueHelper = 'AND'; |
|
442
|
|
|
} |
|
443
|
|
|
|
|
444
|
72 |
|
if (\is_array($_value)) { |
|
445
|
7 |
|
$firstKey = null; |
|
446
|
7 |
|
$firstValue = null; |
|
447
|
7 |
|
foreach ($_value as $oldKey => $oldValue) { |
|
448
|
7 |
|
$_value[$oldKey] = $this->secure($oldValue); |
|
449
|
|
|
|
|
450
|
7 |
|
if ($firstKey === null) { |
|
451
|
7 |
|
$firstKey = $oldKey; |
|
452
|
|
|
} |
|
453
|
|
|
|
|
454
|
7 |
|
if ($firstValue === null) { |
|
455
|
7 |
|
$firstValue = $_value[$oldKey]; |
|
456
|
|
|
} |
|
457
|
|
|
} |
|
458
|
|
|
|
|
459
|
7 |
|
if ($_connector === 'NOT IN' || $_connector === 'IN') { |
|
460
|
3 |
|
$_value = '(' . \implode(',', $_value) . ')'; |
|
461
|
7 |
|
} elseif ($_connector === 'NOT BETWEEN' || $_connector === 'BETWEEN') { |
|
462
|
3 |
|
$_value = '(' . \implode(' AND ', $_value) . ')'; |
|
463
|
7 |
|
} elseif ($firstKey && $firstValue) { |
|
464
|
1 |
View Code Duplication |
if (\strpos((string) $firstKey, ' +') !== false) { |
|
465
|
1 |
|
$firstKey = \str_replace(' +', '', (string) $firstKey); |
|
466
|
1 |
|
$_value = $firstKey . ' + ' . $firstValue; |
|
467
|
|
|
} |
|
468
|
|
|
|
|
469
|
1 |
View Code Duplication |
if (\strpos((string) $firstKey, ' -') !== false) { |
|
470
|
1 |
|
$firstKey = \str_replace(' -', '', (string) $firstKey); |
|
471
|
7 |
|
$_value = $firstKey . ' - ' . $firstValue; |
|
472
|
|
|
} |
|
473
|
|
|
} |
|
474
|
|
|
} else { |
|
475
|
72 |
|
$_value = $this->secure($_value); |
|
476
|
|
|
} |
|
477
|
|
|
|
|
478
|
72 |
|
$quoteString = $this->quote_string( |
|
479
|
72 |
|
\trim( |
|
480
|
72 |
|
(string) \str_ireplace( |
|
481
|
|
|
[ |
|
482
|
72 |
|
$_connector, |
|
483
|
72 |
|
$_glueHelper, |
|
484
|
|
|
], |
|
485
|
72 |
|
'', |
|
486
|
72 |
|
(string) $_key |
|
487
|
|
|
) |
|
488
|
|
|
) |
|
489
|
|
|
); |
|
490
|
|
|
|
|
491
|
72 |
|
$_value = (array) $_value; |
|
492
|
|
|
|
|
493
|
72 |
|
if (!$_glueHelper) { |
|
494
|
72 |
|
$_glueHelper = $glue; |
|
495
|
|
|
} |
|
496
|
|
|
|
|
497
|
72 |
|
$tmpCounter = 0; |
|
498
|
72 |
|
foreach ($_value as $valueInner) { |
|
499
|
72 |
|
$_glueHelperInner = $_glueHelper; |
|
500
|
|
|
|
|
501
|
72 |
|
if ($arrayPairCounter === 0) { |
|
502
|
72 |
|
if ($tmpCounter === 0 && $_glueHelper === 'OR') { |
|
503
|
3 |
|
$_glueHelperInner = '1 = 1 AND ('; // first "OR"-query glue |
|
504
|
72 |
|
} elseif ($tmpCounter === 0) { |
|
505
|
72 |
|
$_glueHelperInner = ''; // first query glue e.g. for "INSERT"-query -> skip the first "," |
|
506
|
|
|
} |
|
507
|
68 |
|
} elseif ($tmpCounter === 0 && $_glueHelper === 'OR') { |
|
508
|
3 |
|
$_glueHelperInner = 'AND ('; // inner-loop "OR"-query glue |
|
509
|
|
|
} |
|
510
|
|
|
|
|
511
|
72 |
|
if (\is_string($valueInner) && $valueInner === '') { |
|
512
|
|
|
$valueInner = "''"; |
|
513
|
|
|
} |
|
514
|
|
|
|
|
515
|
72 |
|
$sql .= ' ' . $_glueHelperInner . ' ' . $quoteString . ' ' . $_connector . ' ' . $valueInner . " \n"; |
|
516
|
72 |
|
$tmpCounter++; |
|
517
|
|
|
} |
|
518
|
|
|
|
|
519
|
72 |
|
if ($_glueHelper === 'OR') { |
|
520
|
6 |
|
$sql .= ' ) '; |
|
521
|
|
|
} |
|
522
|
|
|
|
|
523
|
72 |
|
$arrayPairCounter++; |
|
524
|
|
|
} |
|
525
|
|
|
|
|
526
|
72 |
|
return $sql; |
|
527
|
|
|
} |
|
528
|
|
|
|
|
529
|
|
|
/** |
|
530
|
|
|
* _parseQueryParams |
|
531
|
|
|
* |
|
532
|
|
|
* @param string $sql |
|
533
|
|
|
* @param array $params |
|
534
|
|
|
* |
|
535
|
|
|
* @return array |
|
536
|
|
|
* <p>with the keys -> 'sql', 'params'</p> |
|
537
|
|
|
*/ |
|
538
|
7 |
|
private function _parseQueryParams(string $sql, array $params = []): array |
|
539
|
|
|
{ |
|
540
|
7 |
|
$offset = \strpos($sql, '?'); |
|
541
|
|
|
|
|
542
|
|
|
// is there anything to parse? |
|
543
|
|
|
if ( |
|
544
|
7 |
|
$offset === false |
|
545
|
|
|
|| |
|
546
|
7 |
|
\count($params) === 0 |
|
547
|
|
|
) { |
|
548
|
3 |
|
return ['sql' => $sql, 'params' => $params]; |
|
549
|
|
|
} |
|
550
|
|
|
|
|
551
|
7 |
|
foreach ($params as $key => $param) { |
|
552
|
|
|
|
|
553
|
|
|
// use this only for not named parameters |
|
554
|
7 |
|
if (!\is_int($key)) { |
|
555
|
3 |
|
continue; |
|
556
|
|
|
} |
|
557
|
|
|
|
|
558
|
7 |
|
if ($offset === false) { |
|
559
|
|
|
continue; |
|
560
|
|
|
} |
|
561
|
|
|
|
|
562
|
7 |
|
$replacement = $this->secure($param); |
|
563
|
|
|
|
|
564
|
7 |
|
unset($params[$key]); |
|
565
|
|
|
|
|
566
|
7 |
|
$sql = \substr_replace($sql, $replacement, $offset, 1); |
|
567
|
7 |
|
$offset = \strpos($sql, '?', $offset + \strlen((string) $replacement)); |
|
568
|
|
|
} |
|
569
|
|
|
|
|
570
|
7 |
|
return ['sql' => $sql, 'params' => $params]; |
|
571
|
|
|
} |
|
572
|
|
|
|
|
573
|
|
|
/** |
|
574
|
|
|
* Returns the SQL by replacing :placeholders with SQL-escaped values. |
|
575
|
|
|
* |
|
576
|
|
|
* @param string $sql <p>The SQL string.</p> |
|
577
|
|
|
* @param array $params <p>An array of key-value bindings.</p> |
|
578
|
|
|
* |
|
579
|
|
|
* @return array |
|
580
|
|
|
* <p>with the keys -> 'sql', 'params'</p> |
|
581
|
|
|
*/ |
|
582
|
10 |
|
private function _parseQueryParamsByName(string $sql, array $params = []): array |
|
583
|
|
|
{ |
|
584
|
|
|
// is there anything to parse? |
|
585
|
|
|
if ( |
|
586
|
10 |
|
\strpos($sql, ':') === false |
|
587
|
|
|
|| |
|
588
|
10 |
|
\count($params) === 0 |
|
589
|
|
|
) { |
|
590
|
7 |
|
return ['sql' => $sql, 'params' => $params]; |
|
591
|
|
|
} |
|
592
|
|
|
|
|
593
|
6 |
|
$offset = null; |
|
594
|
6 |
|
$replacement = null; |
|
595
|
6 |
|
foreach ($params as $name => $param) { |
|
596
|
|
|
|
|
597
|
|
|
// use this only for named parameters |
|
598
|
6 |
|
if (\is_int($name)) { |
|
599
|
|
|
continue; |
|
600
|
|
|
} |
|
601
|
|
|
|
|
602
|
|
|
// add ":" if needed |
|
603
|
6 |
|
if (\strpos($name, ':') !== 0) { |
|
604
|
6 |
|
$nameTmp = ':' . $name; |
|
605
|
|
|
} else { |
|
606
|
|
|
$nameTmp = $name; |
|
607
|
|
|
} |
|
608
|
|
|
|
|
609
|
6 |
|
if ($offset === null) { |
|
610
|
6 |
|
$offset = \strpos($sql, $nameTmp); |
|
611
|
|
|
} else { |
|
612
|
6 |
|
$offset = \strpos($sql, $nameTmp, $offset + \strlen((string) $replacement)); |
|
613
|
|
|
} |
|
614
|
|
|
|
|
615
|
6 |
|
if ($offset === false) { |
|
616
|
3 |
|
continue; |
|
617
|
|
|
} |
|
618
|
|
|
|
|
619
|
6 |
|
$replacement = $this->secure($param); |
|
620
|
|
|
|
|
621
|
6 |
|
unset($params[$name]); |
|
622
|
|
|
|
|
623
|
6 |
|
$sql = \substr_replace($sql, $replacement, $offset, \strlen($nameTmp)); |
|
624
|
|
|
} |
|
625
|
|
|
|
|
626
|
6 |
|
return ['sql' => $sql, 'params' => $params]; |
|
627
|
|
|
} |
|
628
|
|
|
|
|
629
|
|
|
/** |
|
630
|
|
|
* Gets the number of affected rows in a previous MySQL operation. |
|
631
|
|
|
* |
|
632
|
|
|
* @return int |
|
633
|
|
|
*/ |
|
634
|
25 |
|
public function affected_rows(): int |
|
635
|
|
|
{ |
|
636
|
|
|
if ( |
|
637
|
25 |
|
$this->mysqli_link |
|
638
|
|
|
&& |
|
639
|
25 |
|
$this->mysqli_link instanceof \mysqli |
|
640
|
|
|
) { |
|
641
|
25 |
|
return \mysqli_affected_rows($this->mysqli_link); |
|
642
|
|
|
} |
|
643
|
|
|
|
|
644
|
|
|
return (int) $this->affected_rows; |
|
645
|
|
|
} |
|
646
|
|
|
|
|
647
|
|
|
/** |
|
648
|
|
|
* Begins a transaction, by turning off auto commit. |
|
649
|
|
|
* |
|
650
|
|
|
* @return bool |
|
651
|
|
|
* <p>This will return true or false indicating success of transaction</p> |
|
652
|
|
|
*/ |
|
653
|
18 |
View Code Duplication |
public function beginTransaction(): bool |
|
|
|
|
|
|
654
|
|
|
{ |
|
655
|
18 |
|
if ($this->in_transaction) { |
|
656
|
6 |
|
$this->debug->displayError('Error: mysql server already in transaction!', false); |
|
657
|
|
|
|
|
658
|
6 |
|
return false; |
|
659
|
|
|
} |
|
660
|
|
|
|
|
661
|
18 |
|
$this->clearErrors(); // needed for "$this->endTransaction()" |
|
662
|
18 |
|
$this->in_transaction = true; |
|
663
|
|
|
|
|
664
|
18 |
|
if ($this->mysqli_link) { |
|
665
|
18 |
|
$return = \mysqli_autocommit($this->mysqli_link, false); |
|
666
|
|
|
} elseif ($this->doctrine_connection && $this->isDoctrinePDOConnection()) { |
|
667
|
|
|
$this->doctrine_connection->setAutoCommit(false); |
|
668
|
|
|
$this->doctrine_connection->beginTransaction(); |
|
669
|
|
|
|
|
670
|
|
|
if ($this->doctrine_connection->isTransactionActive()) { |
|
671
|
|
|
$return = true; |
|
672
|
|
|
} else { |
|
673
|
|
|
$return = false; |
|
674
|
|
|
} |
|
675
|
|
|
} else { |
|
676
|
|
|
$return = false; |
|
677
|
|
|
} |
|
678
|
|
|
|
|
679
|
18 |
|
if (!$return) { |
|
680
|
|
|
$this->in_transaction = false; |
|
681
|
|
|
} |
|
682
|
|
|
|
|
683
|
18 |
|
return $return; |
|
684
|
|
|
} |
|
685
|
|
|
|
|
686
|
|
|
/** |
|
687
|
|
|
* Clear the errors in "_debug->_errors". |
|
688
|
|
|
* |
|
689
|
|
|
* @return bool |
|
690
|
|
|
*/ |
|
691
|
18 |
|
public function clearErrors(): bool |
|
692
|
|
|
{ |
|
693
|
18 |
|
return $this->debug->clearErrors(); |
|
694
|
|
|
} |
|
695
|
|
|
|
|
696
|
|
|
/** |
|
697
|
|
|
* Closes a previously opened database connection. |
|
698
|
|
|
* |
|
699
|
|
|
* @return bool |
|
700
|
|
|
* Will return "true", if the connection was closed, |
|
701
|
|
|
* otherwise (e.g. if the connection was already closed) "false". |
|
702
|
|
|
*/ |
|
703
|
6 |
|
public function close(): bool |
|
704
|
|
|
{ |
|
705
|
6 |
|
$this->connected = false; |
|
706
|
|
|
|
|
707
|
|
|
if ( |
|
708
|
6 |
|
$this->doctrine_connection |
|
709
|
|
|
&& |
|
710
|
6 |
|
$this->doctrine_connection instanceof \Doctrine\DBAL\Connection |
|
711
|
|
|
) { |
|
712
|
|
|
$connectedBefore = $this->doctrine_connection->isConnected(); |
|
713
|
|
|
|
|
714
|
|
|
$this->doctrine_connection->close(); |
|
715
|
|
|
|
|
716
|
|
|
$this->mysqli_link = null; |
|
717
|
|
|
|
|
718
|
|
|
if ($connectedBefore) { |
|
719
|
|
|
return !$this->doctrine_connection->isConnected(); |
|
720
|
|
|
} |
|
721
|
|
|
|
|
722
|
|
|
return false; |
|
723
|
|
|
} |
|
724
|
|
|
|
|
725
|
|
|
if ( |
|
726
|
6 |
|
$this->mysqli_link |
|
727
|
|
|
&& |
|
728
|
6 |
|
$this->mysqli_link instanceof \mysqli |
|
729
|
|
|
) { |
|
730
|
6 |
|
$result = \mysqli_close($this->mysqli_link); |
|
731
|
6 |
|
$this->mysqli_link = null; |
|
732
|
|
|
|
|
733
|
6 |
|
return $result; |
|
734
|
|
|
} |
|
735
|
|
|
|
|
736
|
3 |
|
$this->mysqli_link = null; |
|
737
|
|
|
|
|
738
|
3 |
|
return false; |
|
739
|
|
|
} |
|
740
|
|
|
|
|
741
|
|
|
/** |
|
742
|
|
|
* Commits the current transaction and end the transaction. |
|
743
|
|
|
* |
|
744
|
|
|
* @return bool |
|
745
|
|
|
* <p>bool true on success, false otherwise.</p> |
|
746
|
|
|
*/ |
|
747
|
9 |
View Code Duplication |
public function commit(): bool |
|
|
|
|
|
|
748
|
|
|
{ |
|
749
|
9 |
|
if (!$this->in_transaction) { |
|
750
|
|
|
$this->debug->displayError('Error: mysql server is not in transaction!', false); |
|
751
|
|
|
|
|
752
|
|
|
return false; |
|
753
|
|
|
} |
|
754
|
|
|
|
|
755
|
9 |
|
if ($this->mysqli_link) { |
|
756
|
9 |
|
$return = \mysqli_commit($this->mysqli_link); |
|
757
|
9 |
|
\mysqli_autocommit($this->mysqli_link, true); |
|
758
|
|
|
} elseif ($this->doctrine_connection && $this->isDoctrinePDOConnection()) { |
|
759
|
|
|
$this->doctrine_connection->commit(); |
|
760
|
|
|
$this->doctrine_connection->setAutoCommit(true); |
|
761
|
|
|
|
|
762
|
|
|
if ($this->doctrine_connection->isAutoCommit()) { |
|
763
|
|
|
$return = true; |
|
764
|
|
|
} else { |
|
765
|
|
|
$return = false; |
|
766
|
|
|
} |
|
767
|
|
|
} else { |
|
768
|
|
|
$return = false; |
|
769
|
|
|
} |
|
770
|
|
|
|
|
771
|
9 |
|
$this->in_transaction = false; |
|
772
|
|
|
|
|
773
|
9 |
|
return $return; |
|
774
|
|
|
} |
|
775
|
|
|
|
|
776
|
|
|
/** |
|
777
|
|
|
* Open a new connection to the MySQL server. |
|
778
|
|
|
* |
|
779
|
|
|
* @throws DBConnectException |
|
780
|
|
|
* |
|
781
|
|
|
* @return bool |
|
782
|
|
|
*/ |
|
783
|
21 |
|
public function connect(): bool |
|
784
|
|
|
{ |
|
785
|
21 |
|
if ($this->isReady()) { |
|
786
|
3 |
|
return true; |
|
787
|
|
|
} |
|
788
|
|
|
|
|
789
|
21 |
|
if ($this->doctrine_connection) { |
|
790
|
|
|
$this->doctrine_connection->connect(); |
|
791
|
|
|
|
|
792
|
|
|
$doctrineWrappedConnection = $this->doctrine_connection->getWrappedConnection(); |
|
793
|
|
|
|
|
794
|
|
|
if ($this->isDoctrineMySQLiConnection()) { |
|
795
|
|
|
\assert($doctrineWrappedConnection instanceof \Doctrine\DBAL\Driver\Mysqli\MysqliConnection); |
|
796
|
|
|
|
|
797
|
|
|
$this->mysqli_link = $doctrineWrappedConnection->getWrappedResourceHandle(); |
|
798
|
|
|
|
|
799
|
|
|
return $this->connect_helper(); |
|
800
|
|
|
} |
|
801
|
|
|
|
|
802
|
|
|
if ($this->isDoctrinePDOConnection()) { |
|
803
|
|
|
$this->mysqli_link = null; |
|
804
|
|
|
|
|
805
|
|
|
return $this->connect_helper(); |
|
806
|
|
|
} |
|
807
|
|
|
} |
|
808
|
|
|
|
|
809
|
21 |
|
$flags = $this->flags; |
|
810
|
|
|
|
|
811
|
21 |
|
\mysqli_report(\MYSQLI_REPORT_STRICT); |
|
812
|
|
|
|
|
813
|
|
|
try { |
|
814
|
21 |
|
$this->mysqli_link = \mysqli_init(); |
|
|
|
|
|
|
815
|
|
|
|
|
816
|
21 |
|
if (Helper::isMysqlndIsUsed()) { |
|
817
|
21 |
|
\mysqli_options($this->mysqli_link, \MYSQLI_OPT_INT_AND_FLOAT_NATIVE, true); |
|
818
|
|
|
} |
|
819
|
|
|
|
|
820
|
21 |
|
if ($this->ssl) { |
|
821
|
|
|
if (empty($this->clientcert)) { |
|
822
|
|
|
throw new DBConnectException('Error connecting to mysql server: clientcert not defined'); |
|
823
|
|
|
} |
|
824
|
|
|
|
|
825
|
|
|
if (empty($this->clientkey)) { |
|
826
|
|
|
throw new DBConnectException('Error connecting to mysql server: clientkey not defined'); |
|
827
|
|
|
} |
|
828
|
|
|
|
|
829
|
|
|
if (empty($this->cacert)) { |
|
830
|
|
|
throw new DBConnectException('Error connecting to mysql server: cacert not defined'); |
|
831
|
|
|
} |
|
832
|
|
|
|
|
833
|
|
|
\mysqli_options($this->mysqli_link, \MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, true); |
|
834
|
|
|
|
|
835
|
|
|
\mysqli_ssl_set( |
|
836
|
|
|
$this->mysqli_link, |
|
837
|
|
|
$this->clientkey, |
|
838
|
|
|
$this->clientcert, |
|
839
|
|
|
$this->cacert, |
|
840
|
|
|
'', |
|
841
|
|
|
'' |
|
842
|
|
|
); |
|
843
|
|
|
|
|
844
|
|
|
$flags |= \MYSQLI_CLIENT_SSL; |
|
845
|
|
|
} |
|
846
|
|
|
|
|
847
|
|
|
/** @noinspection PhpUsageOfSilenceOperatorInspection */ |
|
848
|
21 |
|
$this->connected = @\mysqli_real_connect( |
|
849
|
21 |
|
$this->mysqli_link, |
|
850
|
21 |
|
$this->hostname, |
|
851
|
21 |
|
$this->username, |
|
852
|
21 |
|
$this->password, |
|
853
|
21 |
|
$this->database, |
|
854
|
21 |
|
$this->port, |
|
855
|
21 |
|
$this->socket, |
|
856
|
21 |
|
(int) $flags |
|
857
|
|
|
); |
|
858
|
9 |
|
} catch (\Exception $e) { |
|
859
|
9 |
|
$error = 'Error connecting to mysql server: ' . $e->getMessage(); |
|
860
|
9 |
|
$this->debug->displayError($error, false); |
|
861
|
|
|
|
|
862
|
9 |
|
throw new DBConnectException($error, 100, $e); |
|
863
|
|
|
} |
|
864
|
12 |
|
\mysqli_report(\MYSQLI_REPORT_OFF); |
|
865
|
|
|
|
|
866
|
12 |
|
$errno = \mysqli_connect_errno(); |
|
867
|
12 |
|
if (!$this->connected || $errno) { |
|
868
|
|
|
$error = 'Error connecting to mysql server: ' . \mysqli_connect_error() . ' (' . $errno . ')'; |
|
869
|
|
|
$this->debug->displayError($error, false); |
|
870
|
|
|
|
|
871
|
|
|
throw new DBConnectException($error, 101); |
|
872
|
|
|
} |
|
873
|
|
|
|
|
874
|
12 |
|
$this->set_charset($this->charset); |
|
875
|
|
|
|
|
876
|
12 |
|
return $this->isReady(); |
|
877
|
|
|
} |
|
878
|
|
|
|
|
879
|
|
|
/** |
|
880
|
|
|
* @return bool |
|
881
|
|
|
*/ |
|
882
|
|
|
private function connect_helper(): bool |
|
883
|
|
|
{ |
|
884
|
|
|
if (!$this->doctrine_connection) { |
|
885
|
|
|
$this->connected = false; |
|
886
|
|
|
} else { |
|
887
|
|
|
$this->connected = $this->doctrine_connection->isConnected(); |
|
888
|
|
|
} |
|
889
|
|
|
|
|
890
|
|
|
if (!$this->connected) { |
|
891
|
|
|
$error = 'Error connecting to mysql server: ' . \print_r($this->doctrine_connection ? $this->doctrine_connection->errorInfo() : [], false); |
|
892
|
|
|
$this->debug->displayError($error, false); |
|
893
|
|
|
|
|
894
|
|
|
throw new DBConnectException($error, 101); |
|
895
|
|
|
} |
|
896
|
|
|
|
|
897
|
|
|
$this->set_charset($this->charset); |
|
898
|
|
|
|
|
899
|
|
|
return $this->isReady(); |
|
900
|
|
|
} |
|
901
|
|
|
|
|
902
|
|
|
/** |
|
903
|
|
|
* Execute a "delete"-query. |
|
904
|
|
|
* |
|
905
|
|
|
* @param string $table |
|
906
|
|
|
* @param array|string $where |
|
907
|
|
|
* @param string|null $databaseName <p>Use <strong>null</strong> if you will use the current database.</p> |
|
908
|
|
|
* |
|
909
|
|
|
* @throws QueryException |
|
910
|
|
|
* |
|
911
|
|
|
* @return false|int |
|
912
|
|
|
* <p>false on error</p> |
|
913
|
|
|
*/ |
|
914
|
4 |
View Code Duplication |
public function delete(string $table, $where, string $databaseName = null) |
|
|
|
|
|
|
915
|
|
|
{ |
|
916
|
|
|
// init |
|
917
|
4 |
|
$table = \trim($table); |
|
918
|
|
|
|
|
919
|
4 |
|
if ($table === '') { |
|
920
|
3 |
|
$this->debug->displayError('Invalid table name, table name in empty.', false); |
|
921
|
|
|
|
|
922
|
3 |
|
return false; |
|
923
|
|
|
} |
|
924
|
|
|
|
|
925
|
4 |
|
if (\is_string($where)) { |
|
926
|
3 |
|
$WHERE = $this->escape($where, false); |
|
927
|
4 |
|
} elseif (\is_array($where)) { |
|
928
|
4 |
|
$WHERE = $this->_parseArrayPair($where, 'AND'); |
|
929
|
|
|
} else { |
|
930
|
3 |
|
$WHERE = ''; |
|
931
|
|
|
} |
|
932
|
|
|
|
|
933
|
4 |
|
if ($databaseName) { |
|
|
|
|
|
|
934
|
|
|
$databaseName = $this->quote_string(\trim($databaseName)) . '.'; |
|
935
|
|
|
} |
|
936
|
|
|
|
|
937
|
4 |
|
$sql = 'DELETE FROM ' . $databaseName . $this->quote_string($table) . " WHERE (${WHERE})"; |
|
938
|
|
|
|
|
939
|
4 |
|
$return = $this->query($sql); |
|
940
|
|
|
|
|
941
|
4 |
|
\assert(\is_int($return) || $return === false); |
|
942
|
|
|
|
|
943
|
4 |
|
return $return; |
|
944
|
|
|
} |
|
945
|
|
|
|
|
946
|
|
|
/** |
|
947
|
|
|
* Ends a transaction and commits if no errors, then ends autocommit. |
|
948
|
|
|
* |
|
949
|
|
|
* @return bool |
|
950
|
|
|
* <p>This will return true or false indicating success of transactions.</p> |
|
951
|
|
|
*/ |
|
952
|
12 |
|
public function endTransaction(): bool |
|
953
|
|
|
{ |
|
954
|
12 |
|
if (!$this->in_transaction) { |
|
955
|
|
|
$this->debug->displayError('Error: mysql server is not in transaction!', false); |
|
956
|
|
|
|
|
957
|
|
|
return false; |
|
958
|
|
|
} |
|
959
|
|
|
|
|
960
|
12 |
|
if (!$this->errors()) { |
|
961
|
3 |
|
$return = $this->commit(); |
|
962
|
|
|
} else { |
|
963
|
9 |
|
$this->rollback(); |
|
964
|
9 |
|
$return = false; |
|
965
|
|
|
} |
|
966
|
|
|
|
|
967
|
12 |
|
if ($this->mysqli_link) { |
|
968
|
12 |
|
\mysqli_autocommit($this->mysqli_link, true); |
|
969
|
|
|
} elseif ($this->doctrine_connection && $this->isDoctrinePDOConnection()) { |
|
970
|
|
|
$this->doctrine_connection->setAutoCommit(true); |
|
971
|
|
|
|
|
972
|
|
|
if ($this->doctrine_connection->isAutoCommit()) { |
|
973
|
|
|
$return = true; |
|
974
|
|
|
} else { |
|
975
|
|
|
$return = false; |
|
976
|
|
|
} |
|
977
|
|
|
} |
|
978
|
|
|
|
|
979
|
12 |
|
$this->in_transaction = false; |
|
980
|
|
|
|
|
981
|
12 |
|
return $return; |
|
982
|
|
|
} |
|
983
|
|
|
|
|
984
|
|
|
/** |
|
985
|
|
|
* Get all errors from "$this->errors". |
|
986
|
|
|
* |
|
987
|
|
|
* @return array|false |
|
988
|
|
|
* <p>false === on errors</p> |
|
989
|
|
|
*/ |
|
990
|
12 |
|
public function errors() |
|
991
|
|
|
{ |
|
992
|
12 |
|
$errors = $this->debug->getErrors(); |
|
993
|
|
|
|
|
994
|
12 |
|
return \count($errors) > 0 ? $errors : false; |
|
995
|
|
|
} |
|
996
|
|
|
|
|
997
|
|
|
/** |
|
998
|
|
|
* Escape: Use "mysqli_real_escape_string" and clean non UTF-8 chars + some extra optional stuff. |
|
999
|
|
|
* |
|
1000
|
|
|
* @param mixed $var bool: convert into "integer"<br /> |
|
1001
|
|
|
* int: int (don't change it)<br /> |
|
1002
|
|
|
* float: float (don't change it)<br /> |
|
1003
|
|
|
* null: null (don't change it)<br /> |
|
1004
|
|
|
* array: run escape() for every key => value<br /> |
|
1005
|
|
|
* string: run UTF8::cleanup() and mysqli_real_escape_string()<br /> |
|
1006
|
|
|
* @param bool $stripe_non_utf8 |
|
1007
|
|
|
* @param bool $html_entity_decode |
|
1008
|
|
|
* @param bool|null $convert_array <strong>false</strong> => Keep the array.<br /> |
|
1009
|
|
|
* <strong>true</strong> => Convert to string var1,var2,var3...<br /> |
|
1010
|
|
|
* <strong>null</strong> => Convert the array into null, every time. |
|
1011
|
|
|
* |
|
1012
|
|
|
* @return mixed |
|
1013
|
|
|
*/ |
|
1014
|
110 |
|
public function escape($var = '', bool $stripe_non_utf8 = true, bool $html_entity_decode = false, $convert_array = false) |
|
1015
|
|
|
{ |
|
1016
|
|
|
// [empty] |
|
1017
|
110 |
|
if ($var === '') { |
|
1018
|
6 |
|
return ''; |
|
1019
|
|
|
} |
|
1020
|
|
|
|
|
1021
|
|
|
// '' |
|
1022
|
110 |
|
if ($var === "''") { |
|
1023
|
|
|
return "''"; |
|
1024
|
|
|
} |
|
1025
|
|
|
|
|
1026
|
|
|
// check the type |
|
1027
|
110 |
|
$type = \gettype($var); |
|
1028
|
|
|
|
|
1029
|
110 |
|
if ($type === 'object') { |
|
1030
|
9 |
|
if ($var instanceof \DateTimeInterface) { |
|
1031
|
9 |
|
$var = $var->format('Y-m-d H:i:s'); |
|
1032
|
9 |
|
$type = 'string'; |
|
1033
|
6 |
|
} elseif (\method_exists($var, '__toString')) { |
|
1034
|
6 |
|
$var = (string) $var; |
|
1035
|
6 |
|
$type = 'string'; |
|
1036
|
|
|
} |
|
1037
|
|
|
} |
|
1038
|
|
|
|
|
1039
|
|
|
switch ($type) { |
|
1040
|
110 |
|
case 'boolean': |
|
1041
|
9 |
|
$var = (int) $var; |
|
1042
|
|
|
|
|
1043
|
9 |
|
break; |
|
1044
|
|
|
|
|
1045
|
110 |
|
case 'double': |
|
1046
|
110 |
|
case 'integer': |
|
1047
|
65 |
|
break; |
|
1048
|
|
|
|
|
1049
|
107 |
|
case 'string': |
|
1050
|
107 |
|
if ($stripe_non_utf8) { |
|
1051
|
23 |
|
$var = UTF8::cleanup($var); |
|
1052
|
|
|
} |
|
1053
|
|
|
|
|
1054
|
107 |
|
if ($html_entity_decode) { |
|
1055
|
3 |
|
$var = UTF8::html_entity_decode($var); |
|
1056
|
|
|
} |
|
1057
|
|
|
|
|
1058
|
|
|
/** @noinspection PhpUsageOfSilenceOperatorInspection */ |
|
1059
|
107 |
|
$var = @\get_magic_quotes_gpc() ? \stripslashes($var) : $var; |
|
1060
|
|
|
|
|
1061
|
|
|
if ( |
|
1062
|
107 |
|
$this->mysqli_link |
|
1063
|
|
|
&& |
|
1064
|
107 |
|
$this->mysqli_link instanceof \mysqli |
|
1065
|
|
|
) { |
|
1066
|
107 |
|
$var = \mysqli_real_escape_string($this->mysqli_link, $var); |
|
1067
|
|
|
} elseif ($this->doctrine_connection && $this->isDoctrinePDOConnection()) { |
|
1068
|
|
|
$pdoConnection = $this->getDoctrinePDOConnection(); |
|
1069
|
|
|
\assert($pdoConnection !== false); |
|
1070
|
|
|
$var = $pdoConnection->quote($var); |
|
1071
|
|
|
$var = \substr($var, 1, -1); |
|
1072
|
|
|
} |
|
1073
|
|
|
|
|
1074
|
107 |
|
break; |
|
1075
|
|
|
|
|
1076
|
9 |
|
case 'array': |
|
1077
|
6 |
|
if ($convert_array === null) { |
|
1078
|
3 |
|
if ($this->convert_null_to_empty_string) { |
|
1079
|
|
|
$var = "''"; |
|
1080
|
|
|
} else { |
|
1081
|
3 |
|
$var = 'NULL'; |
|
1082
|
|
|
} |
|
1083
|
|
|
} else { |
|
1084
|
6 |
|
$varCleaned = []; |
|
1085
|
6 |
|
foreach ((array) $var as $key => $value) { |
|
1086
|
6 |
|
$key = $this->escape($key, $stripe_non_utf8, $html_entity_decode); |
|
1087
|
6 |
|
$value = $this->escape($value, $stripe_non_utf8, $html_entity_decode); |
|
1088
|
|
|
|
|
1089
|
|
|
/** @noinspection OffsetOperationsInspection */ |
|
1090
|
6 |
|
$varCleaned[$key] = $value; |
|
1091
|
|
|
} |
|
1092
|
|
|
|
|
1093
|
6 |
View Code Duplication |
if ($convert_array === true) { |
|
1094
|
3 |
|
$varCleaned = \implode(',', $varCleaned); |
|
1095
|
|
|
|
|
1096
|
3 |
|
$var = $varCleaned; |
|
1097
|
|
|
} else { |
|
1098
|
6 |
|
$var = $varCleaned; |
|
1099
|
|
|
} |
|
1100
|
|
|
} |
|
1101
|
|
|
|
|
1102
|
6 |
|
break; |
|
1103
|
|
|
|
|
1104
|
9 |
|
case 'NULL': |
|
1105
|
6 |
|
if ($this->convert_null_to_empty_string) { |
|
1106
|
|
|
$var = "''"; |
|
1107
|
|
|
} else { |
|
1108
|
6 |
|
$var = 'NULL'; |
|
1109
|
|
|
} |
|
1110
|
|
|
|
|
1111
|
6 |
|
break; |
|
1112
|
|
|
|
|
1113
|
|
|
default: |
|
1114
|
6 |
|
throw new \InvalidArgumentException(\sprintf('Not supported value "%s" of type %s.', \print_r($var, true), $type)); |
|
1115
|
|
|
|
|
1116
|
|
|
break; |
|
|
|
|
|
|
1117
|
|
|
} |
|
1118
|
|
|
|
|
1119
|
110 |
|
return $var; |
|
1120
|
|
|
} |
|
1121
|
|
|
|
|
1122
|
|
|
/** |
|
1123
|
|
|
* Execute select/insert/update/delete sql-queries. |
|
1124
|
|
|
* |
|
1125
|
|
|
* @param string $query <p>sql-query</p> |
|
1126
|
|
|
* @param bool $useCache optional <p>use cache?</p> |
|
1127
|
|
|
* @param int $cacheTTL optional <p>cache-ttl in seconds</p> |
|
1128
|
|
|
* @param DB|null $db optional <p>the database connection</p> |
|
1129
|
|
|
* |
|
1130
|
|
|
* @throws QueryException |
|
1131
|
|
|
* |
|
1132
|
|
|
* @return mixed |
|
1133
|
|
|
* <ul> |
|
1134
|
|
|
* <li>"array" by "<b>SELECT</b>"-queries</li> |
|
1135
|
|
|
* <li>"int|string" (insert_id) by "<b>INSERT</b>"-queries</li> |
|
1136
|
|
|
* <li>"int" (affected_rows) by "<b>UPDATE / DELETE</b>"-queries</li> |
|
1137
|
|
|
* <li>"true" by e.g. "DROP"-queries</li> |
|
1138
|
|
|
* <li>"false" on error</li> |
|
1139
|
|
|
* </ul> |
|
1140
|
|
|
*/ |
|
1141
|
9 |
|
public static function execSQL(string $query, bool $useCache = false, int $cacheTTL = 3600, self $db = null) |
|
1142
|
|
|
{ |
|
1143
|
|
|
// init |
|
1144
|
9 |
|
$cacheKey = null; |
|
1145
|
9 |
|
if (!$db) { |
|
1146
|
9 |
|
$db = self::getInstance(); |
|
1147
|
|
|
} |
|
1148
|
|
|
|
|
1149
|
9 |
View Code Duplication |
if ($useCache) { |
|
1150
|
3 |
|
$cache = new Cache(null, null, false, $useCache); |
|
1151
|
3 |
|
$cacheKey = 'sql-' . \md5($query); |
|
1152
|
|
|
|
|
1153
|
|
|
if ( |
|
1154
|
3 |
|
$cache->getCacheIsReady() |
|
1155
|
|
|
&& |
|
1156
|
3 |
|
$cache->existsItem($cacheKey) |
|
1157
|
|
|
) { |
|
1158
|
3 |
|
return $cache->getItem($cacheKey); |
|
1159
|
|
|
} |
|
1160
|
|
|
} else { |
|
1161
|
9 |
|
$cache = false; |
|
1162
|
|
|
} |
|
1163
|
|
|
|
|
1164
|
9 |
|
$result = $db->query($query); |
|
1165
|
|
|
|
|
1166
|
9 |
|
if ($result instanceof Result) { |
|
1167
|
3 |
|
$return = $result->fetchAllArray(); |
|
1168
|
|
|
|
|
1169
|
|
|
// save into the cache |
|
1170
|
|
View Code Duplication |
if ( |
|
1171
|
3 |
|
$cacheKey !== null |
|
1172
|
|
|
&& |
|
1173
|
3 |
|
$useCache |
|
1174
|
|
|
&& |
|
1175
|
3 |
|
$cache instanceof Cache |
|
1176
|
|
|
&& |
|
1177
|
3 |
|
$cache->getCacheIsReady() |
|
1178
|
|
|
) { |
|
1179
|
3 |
|
$cache->setItem($cacheKey, $return, $cacheTTL); |
|
1180
|
|
|
} |
|
1181
|
|
|
} else { |
|
1182
|
6 |
|
$return = $result; |
|
1183
|
|
|
} |
|
1184
|
|
|
|
|
1185
|
9 |
|
return $return; |
|
1186
|
|
|
} |
|
1187
|
|
|
|
|
1188
|
|
|
/** |
|
1189
|
|
|
* Get all table-names via "SHOW TABLES". |
|
1190
|
|
|
* |
|
1191
|
|
|
* @return array |
|
1192
|
|
|
*/ |
|
1193
|
3 |
|
public function getAllTables(): array |
|
1194
|
|
|
{ |
|
1195
|
3 |
|
$query = 'SHOW TABLES'; |
|
1196
|
3 |
|
$result = $this->query($query); |
|
1197
|
|
|
|
|
1198
|
3 |
|
\assert($result instanceof Result); |
|
1199
|
|
|
|
|
1200
|
3 |
|
return $result->fetchAllArray(); |
|
1201
|
|
|
} |
|
1202
|
|
|
|
|
1203
|
|
|
/** |
|
1204
|
|
|
* @return array |
|
1205
|
|
|
*/ |
|
1206
|
49 |
|
public function getConfig(): array |
|
1207
|
|
|
{ |
|
1208
|
|
|
$config = [ |
|
1209
|
49 |
|
'hostname' => $this->hostname, |
|
1210
|
49 |
|
'username' => $this->username, |
|
1211
|
49 |
|
'password' => $this->password, |
|
1212
|
49 |
|
'port' => $this->port, |
|
1213
|
49 |
|
'database' => $this->database, |
|
1214
|
49 |
|
'socket' => $this->socket, |
|
1215
|
49 |
|
'charset' => $this->charset, |
|
1216
|
49 |
|
'cacert' => $this->cacert, |
|
1217
|
49 |
|
'clientcert' => $this->clientcert, |
|
1218
|
49 |
|
'clientkey' => $this->clientkey, |
|
1219
|
|
|
]; |
|
1220
|
|
|
|
|
1221
|
49 |
|
if ($this->doctrine_connection instanceof \Doctrine\DBAL\Connection) { |
|
1222
|
|
|
$config += $this->doctrine_connection->getParams(); |
|
1223
|
|
|
} |
|
1224
|
|
|
|
|
1225
|
49 |
|
return $config; |
|
1226
|
|
|
} |
|
1227
|
|
|
|
|
1228
|
|
|
/** |
|
1229
|
|
|
* @return Debug |
|
1230
|
|
|
*/ |
|
1231
|
10 |
|
public function getDebugger(): Debug |
|
1232
|
|
|
{ |
|
1233
|
10 |
|
return $this->debug; |
|
1234
|
|
|
} |
|
1235
|
|
|
|
|
1236
|
|
|
/** |
|
1237
|
|
|
* @return \Doctrine\DBAL\Connection|null|null |
|
1238
|
|
|
*/ |
|
1239
|
2 |
|
public function getDoctrineConnection() |
|
1240
|
|
|
{ |
|
1241
|
2 |
|
return $this->doctrine_connection; |
|
1242
|
|
|
} |
|
1243
|
|
|
|
|
1244
|
|
|
/** |
|
1245
|
|
|
* @return \Doctrine\DBAL\Driver\Connection|false |
|
1246
|
|
|
*/ |
|
1247
|
|
View Code Duplication |
private function getDoctrinePDOConnection() |
|
|
|
|
|
|
1248
|
|
|
{ |
|
1249
|
|
|
if ($this->doctrine_connection) { |
|
1250
|
|
|
$doctrineWrappedConnection = $this->doctrine_connection->getWrappedConnection(); |
|
1251
|
|
|
if ($doctrineWrappedConnection instanceof \Doctrine\DBAL\Driver\PDOConnection) { |
|
1252
|
|
|
return $doctrineWrappedConnection; |
|
1253
|
|
|
} |
|
1254
|
|
|
} |
|
1255
|
|
|
|
|
1256
|
|
|
return false; |
|
1257
|
|
|
} |
|
1258
|
|
|
|
|
1259
|
|
|
/** |
|
1260
|
|
|
* Get errors from "$this->errors". |
|
1261
|
|
|
* |
|
1262
|
|
|
* @return array |
|
1263
|
|
|
*/ |
|
1264
|
3 |
|
public function getErrors(): array |
|
1265
|
|
|
{ |
|
1266
|
3 |
|
return $this->debug->getErrors(); |
|
1267
|
|
|
} |
|
1268
|
|
|
|
|
1269
|
|
|
/** |
|
1270
|
|
|
* @param string $hostname <p>Hostname of the mysql server</p> |
|
1271
|
|
|
* @param string $username <p>Username for the mysql connection</p> |
|
1272
|
|
|
* @param string $password <p>Password for the mysql connection</p> |
|
1273
|
|
|
* @param string $database <p>Database for the mysql connection</p> |
|
1274
|
|
|
* @param int $port <p>default is (int)3306</p> |
|
1275
|
|
|
* @param string $charset <p>default is 'utf8' or 'utf8mb4' (if supported)</p> |
|
1276
|
|
|
* @param bool $exit_on_error <p>Throw a 'Exception' when a query failed, otherwise it will return |
|
1277
|
|
|
* 'false'. Use false to disable it.</p> |
|
1278
|
|
|
* @param bool $echo_on_error <p>Echo the error if "checkForDev()" returns true. |
|
1279
|
|
|
* Use false to disable it.</p> |
|
1280
|
|
|
* @param string $logger_class_name |
|
1281
|
|
|
* @param string $logger_level <p>'TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'</p> |
|
1282
|
|
|
* @param array $extra_config <p> |
|
1283
|
|
|
* 're_connect' => bool<br> |
|
1284
|
|
|
* 'session_to_db' => bool<br> |
|
1285
|
|
|
* 'doctrine' => \Doctrine\DBAL\Connection<br> |
|
1286
|
|
|
* 'socket' => string (path)<br> |
|
1287
|
|
|
* 'flags' => null|int<br> |
|
1288
|
|
|
* 'ssl' => bool<br> |
|
1289
|
|
|
* 'clientkey' => string (path)<br> |
|
1290
|
|
|
* 'clientcert' => string (path)<br> |
|
1291
|
|
|
* 'cacert' => string (path)<br> |
|
1292
|
|
|
* </p> |
|
1293
|
|
|
* |
|
1294
|
|
|
* @return self |
|
1295
|
|
|
*/ |
|
1296
|
207 |
|
public static function getInstance( |
|
1297
|
|
|
string $hostname = '', |
|
1298
|
|
|
string $username = '', |
|
1299
|
|
|
string $password = '', |
|
1300
|
|
|
string $database = '', |
|
1301
|
|
|
$port = 3306, |
|
1302
|
|
|
string $charset = 'utf8', |
|
1303
|
|
|
bool $exit_on_error = true, |
|
1304
|
|
|
bool $echo_on_error = true, |
|
1305
|
|
|
string $logger_class_name = '', |
|
1306
|
|
|
string $logger_level = '', |
|
1307
|
|
|
array $extra_config = [] |
|
1308
|
|
|
): self { |
|
1309
|
|
|
/** |
|
1310
|
|
|
* @var self[] |
|
1311
|
|
|
*/ |
|
1312
|
207 |
|
static $instance = []; |
|
1313
|
|
|
|
|
1314
|
|
|
/** |
|
1315
|
|
|
* @var self|null |
|
1316
|
|
|
*/ |
|
1317
|
207 |
|
static $firstInstance = null; |
|
1318
|
|
|
|
|
1319
|
|
|
// fallback |
|
1320
|
207 |
|
if (!$charset) { |
|
1321
|
121 |
|
$charset = 'utf8'; |
|
1322
|
|
|
} |
|
1323
|
|
|
|
|
1324
|
|
|
if ( |
|
1325
|
207 |
|
'' . $hostname . $username . $password . $database . $port . $charset === '' . $port . $charset |
|
1326
|
|
|
&& |
|
1327
|
207 |
|
$firstInstance instanceof self |
|
1328
|
|
|
) { |
|
1329
|
123 |
|
if (isset($extra_config['re_connect']) && $extra_config['re_connect'] === true) { |
|
1330
|
|
|
$firstInstance->reconnect(true); |
|
1331
|
|
|
} |
|
1332
|
|
|
|
|
1333
|
123 |
|
return $firstInstance; |
|
1334
|
|
|
} |
|
1335
|
|
|
|
|
1336
|
126 |
|
$extra_config_string = ''; |
|
1337
|
126 |
|
foreach ($extra_config as $extra_config_key => $extra_config_value) { |
|
1338
|
56 |
|
if (\is_object($extra_config_value)) { |
|
1339
|
|
|
$extra_config_value_tmp = \spl_object_hash($extra_config_value); |
|
1340
|
|
|
} else { |
|
1341
|
56 |
|
$extra_config_value_tmp = (string) $extra_config_value; |
|
1342
|
|
|
} |
|
1343
|
56 |
|
$extra_config_string .= $extra_config_key . $extra_config_value_tmp; |
|
1344
|
|
|
} |
|
1345
|
|
|
|
|
1346
|
126 |
|
$connection = \md5( |
|
1347
|
126 |
|
$hostname . $username . $password . $database . $port . $charset . (int) $exit_on_error . (int) $echo_on_error . $logger_class_name . $logger_level . $extra_config_string |
|
1348
|
|
|
); |
|
1349
|
|
|
|
|
1350
|
126 |
|
if (!isset($instance[$connection])) { |
|
1351
|
24 |
|
$instance[$connection] = new self( |
|
1352
|
24 |
|
$hostname, |
|
1353
|
24 |
|
$username, |
|
1354
|
24 |
|
$password, |
|
1355
|
24 |
|
$database, |
|
1356
|
24 |
|
$port, |
|
1357
|
24 |
|
$charset, |
|
1358
|
24 |
|
$exit_on_error, |
|
1359
|
24 |
|
$echo_on_error, |
|
1360
|
24 |
|
$logger_class_name, |
|
1361
|
24 |
|
$logger_level, |
|
1362
|
24 |
|
$extra_config |
|
1363
|
|
|
); |
|
1364
|
|
|
|
|
1365
|
6 |
|
if ($firstInstance === null) { |
|
1366
|
1 |
|
$firstInstance = $instance[$connection]; |
|
1367
|
|
|
} |
|
1368
|
|
|
} |
|
1369
|
|
|
|
|
1370
|
114 |
|
if (isset($extra_config['re_connect']) && $extra_config['re_connect'] === true) { |
|
1371
|
|
|
$instance[$connection]->reconnect(true); |
|
1372
|
|
|
} |
|
1373
|
|
|
|
|
1374
|
114 |
|
return $instance[$connection]; |
|
1375
|
|
|
} |
|
1376
|
|
|
|
|
1377
|
|
|
/** |
|
1378
|
|
|
* @param \Doctrine\DBAL\Connection $doctrine |
|
1379
|
|
|
* @param string $charset <p>default is 'utf8' or 'utf8mb4' (if supported)</p> |
|
1380
|
|
|
* @param bool $exit_on_error <p>Throw a 'Exception' when a query failed, otherwise it will |
|
1381
|
|
|
* return 'false'. Use false to disable it.</p> |
|
1382
|
|
|
* @param bool $echo_on_error <p>Echo the error if "checkForDev()" returns true. |
|
1383
|
|
|
* Use false to disable it.</p> |
|
1384
|
|
|
* @param string $logger_class_name |
|
1385
|
|
|
* @param string $logger_level <p>'TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'</p> |
|
1386
|
|
|
* @param array $extra_config <p> |
|
1387
|
|
|
* 're_connect' => bool<br> |
|
1388
|
|
|
* 'session_to_db' => bool<br> |
|
1389
|
|
|
* 'socket' => string (path)<br> |
|
1390
|
|
|
* 'flags' => null|int<br> |
|
1391
|
|
|
* 'ssl' => bool<br> |
|
1392
|
|
|
* 'clientkey' => string (path)<br> |
|
1393
|
|
|
* 'clientcert' => string (path)<br> |
|
1394
|
|
|
* 'cacert' => string (path)<br> |
|
1395
|
|
|
* </p> |
|
1396
|
|
|
* |
|
1397
|
|
|
* @return self |
|
1398
|
|
|
*/ |
|
1399
|
55 |
|
public static function getInstanceDoctrineHelper( |
|
1400
|
|
|
\Doctrine\DBAL\Connection $doctrine, |
|
1401
|
|
|
string $charset = 'utf8', |
|
1402
|
|
|
bool $exit_on_error = true, |
|
1403
|
|
|
bool $echo_on_error = true, |
|
1404
|
|
|
string $logger_class_name = '', |
|
1405
|
|
|
string $logger_level = '', |
|
1406
|
|
|
array $extra_config = [] |
|
1407
|
|
|
): self { |
|
1408
|
55 |
|
$extra_config['doctrine'] = $doctrine; |
|
1409
|
|
|
|
|
1410
|
55 |
|
return self::getInstance( |
|
1411
|
55 |
|
'', |
|
1412
|
55 |
|
'', |
|
1413
|
55 |
|
'', |
|
1414
|
55 |
|
'', |
|
1415
|
55 |
|
3306, |
|
1416
|
55 |
|
$charset, |
|
1417
|
55 |
|
$exit_on_error, |
|
1418
|
55 |
|
$echo_on_error, |
|
1419
|
55 |
|
$logger_class_name, |
|
1420
|
55 |
|
$logger_level, |
|
1421
|
55 |
|
$extra_config |
|
1422
|
|
|
); |
|
1423
|
|
|
} |
|
1424
|
|
|
|
|
1425
|
|
|
/** |
|
1426
|
|
|
* Get the mysqli-link (link identifier returned by mysqli-connect). |
|
1427
|
|
|
* |
|
1428
|
|
|
* @return \mysqli|null |
|
1429
|
|
|
*/ |
|
1430
|
15 |
|
public function getLink() |
|
1431
|
|
|
{ |
|
1432
|
15 |
|
return $this->mysqli_link; |
|
1433
|
|
|
} |
|
1434
|
|
|
|
|
1435
|
|
|
/** |
|
1436
|
|
|
* Get the current charset. |
|
1437
|
|
|
* |
|
1438
|
|
|
* @return string |
|
1439
|
|
|
*/ |
|
1440
|
3 |
|
public function get_charset(): string |
|
1441
|
|
|
{ |
|
1442
|
3 |
|
return $this->charset; |
|
1443
|
|
|
} |
|
1444
|
|
|
|
|
1445
|
|
|
/** |
|
1446
|
|
|
* Check if we are in a transaction. |
|
1447
|
|
|
* |
|
1448
|
|
|
* @return bool |
|
1449
|
|
|
*/ |
|
1450
|
|
|
public function inTransaction(): bool |
|
1451
|
|
|
{ |
|
1452
|
|
|
return $this->in_transaction; |
|
1453
|
|
|
} |
|
1454
|
|
|
|
|
1455
|
|
|
/** |
|
1456
|
|
|
* Execute a "insert"-query. |
|
1457
|
|
|
* |
|
1458
|
|
|
* @param string $table |
|
1459
|
|
|
* @param array $data |
|
1460
|
|
|
* @param string|null $databaseName <p>Use <strong>null</strong> if you will use the current database.</p> |
|
1461
|
|
|
* |
|
1462
|
|
|
* @throws QueryException |
|
1463
|
|
|
* |
|
1464
|
|
|
* @return false|int|string |
|
1465
|
|
|
* <p>false on error</p> |
|
1466
|
|
|
*/ |
|
1467
|
74 |
|
public function insert(string $table, array $data = [], string $databaseName = null) |
|
1468
|
|
|
{ |
|
1469
|
|
|
// init |
|
1470
|
74 |
|
$table = \trim($table); |
|
1471
|
|
|
|
|
1472
|
74 |
|
if ($table === '') { |
|
1473
|
6 |
|
$this->debug->displayError('Invalid table name, table name in empty.', false); |
|
1474
|
|
|
|
|
1475
|
6 |
|
return false; |
|
1476
|
|
|
} |
|
1477
|
|
|
|
|
1478
|
71 |
|
if (\count($data) === 0) { |
|
1479
|
9 |
|
$this->debug->displayError('Invalid data for INSERT, data is empty.', false); |
|
1480
|
|
|
|
|
1481
|
9 |
|
return false; |
|
1482
|
|
|
} |
|
1483
|
|
|
|
|
1484
|
65 |
|
$SET = $this->_parseArrayPair($data); |
|
1485
|
|
|
|
|
1486
|
65 |
|
if ($databaseName) { |
|
|
|
|
|
|
1487
|
|
|
$databaseName = $this->quote_string(\trim($databaseName)) . '.'; |
|
1488
|
|
|
} |
|
1489
|
|
|
|
|
1490
|
65 |
|
$sql = 'INSERT INTO ' . $databaseName . $this->quote_string($table) . " SET ${SET}"; |
|
1491
|
|
|
|
|
1492
|
65 |
|
$return = $this->query($sql); |
|
1493
|
65 |
|
if ($return === false) { |
|
1494
|
3 |
|
return false; |
|
1495
|
|
|
} |
|
1496
|
|
|
|
|
1497
|
65 |
|
\assert(\is_int($return) || \is_string($return)); |
|
1498
|
|
|
|
|
1499
|
65 |
|
return $return; |
|
1500
|
|
|
} |
|
1501
|
|
|
|
|
1502
|
|
|
/** |
|
1503
|
|
|
* Returns the auto generated id used in the last query. |
|
1504
|
|
|
* |
|
1505
|
|
|
* @return false|int|string |
|
1506
|
|
|
*/ |
|
1507
|
100 |
|
public function insert_id() |
|
1508
|
|
|
{ |
|
1509
|
100 |
|
if ($this->mysqli_link) { |
|
1510
|
100 |
|
return \mysqli_insert_id($this->mysqli_link); |
|
1511
|
|
|
} |
|
1512
|
|
|
|
|
1513
|
|
|
$doctrinePDOConnection = $this->getDoctrinePDOConnection(); |
|
1514
|
|
|
if ($doctrinePDOConnection) { |
|
1515
|
|
|
return $doctrinePDOConnection->lastInsertId(); |
|
1516
|
|
|
} |
|
1517
|
|
|
|
|
1518
|
|
|
return false; |
|
1519
|
|
|
} |
|
1520
|
|
|
|
|
1521
|
|
|
/** |
|
1522
|
|
|
* @return bool |
|
1523
|
|
|
*/ |
|
1524
|
|
|
public function isDoctrineMySQLiConnection(): bool |
|
1525
|
|
|
{ |
|
1526
|
|
|
if ($this->doctrine_connection) { |
|
1527
|
|
|
$doctrineWrappedConnection = $this->doctrine_connection->getWrappedConnection(); |
|
1528
|
|
|
if ($doctrineWrappedConnection instanceof \Doctrine\DBAL\Driver\Mysqli\MysqliConnection) { |
|
1529
|
|
|
return true; |
|
1530
|
|
|
} |
|
1531
|
|
|
} |
|
1532
|
|
|
|
|
1533
|
|
|
return false; |
|
1534
|
|
|
} |
|
1535
|
|
|
|
|
1536
|
|
|
/** |
|
1537
|
|
|
* @return bool |
|
1538
|
|
|
*/ |
|
1539
|
|
View Code Duplication |
public function isDoctrinePDOConnection(): bool |
|
|
|
|
|
|
1540
|
|
|
{ |
|
1541
|
|
|
if ($this->doctrine_connection) { |
|
1542
|
|
|
$doctrineWrappedConnection = $this->doctrine_connection->getWrappedConnection(); |
|
1543
|
|
|
if ($doctrineWrappedConnection instanceof \Doctrine\DBAL\Driver\PDOConnection) { |
|
1544
|
|
|
return true; |
|
1545
|
|
|
} |
|
1546
|
|
|
} |
|
1547
|
|
|
|
|
1548
|
|
|
return false; |
|
1549
|
|
|
} |
|
1550
|
|
|
|
|
1551
|
|
|
/** |
|
1552
|
|
|
* Check if db-connection is ready. |
|
1553
|
|
|
* |
|
1554
|
|
|
* @return bool |
|
1555
|
|
|
*/ |
|
1556
|
162 |
|
public function isReady(): bool |
|
1557
|
|
|
{ |
|
1558
|
162 |
|
return $this->connected ? true : false; |
|
1559
|
|
|
} |
|
1560
|
|
|
|
|
1561
|
|
|
/** |
|
1562
|
|
|
* Get the last sql-error. |
|
1563
|
|
|
* |
|
1564
|
|
|
* @return false|string |
|
1565
|
|
|
* <p>false === there was no error</p> |
|
1566
|
|
|
*/ |
|
1567
|
3 |
|
public function lastError() |
|
1568
|
|
|
{ |
|
1569
|
3 |
|
$errors = $this->debug->getErrors(); |
|
1570
|
|
|
|
|
1571
|
3 |
|
return \count($errors) > 0 ? \end($errors) : false; |
|
1572
|
|
|
} |
|
1573
|
|
|
|
|
1574
|
|
|
/** |
|
1575
|
|
|
* Execute a sql-multi-query. |
|
1576
|
|
|
* |
|
1577
|
|
|
* @param string $sql |
|
1578
|
|
|
* |
|
1579
|
|
|
* @throws QueryException |
|
1580
|
|
|
* |
|
1581
|
|
|
* @return bool|Result[] |
|
1582
|
|
|
* <ul> |
|
1583
|
|
|
* <li>"Result"-Array by "<b>SELECT</b>"-queries</li> |
|
1584
|
|
|
* <li>"bool" by only "<b>INSERT</b>"-queries</li> |
|
1585
|
|
|
* <li>"bool" by only (affected_rows) by "<b>UPDATE / DELETE</b>"-queries</li> |
|
1586
|
|
|
* <li>"bool" by only by e.g. "DROP"-queries</li> |
|
1587
|
|
|
* </ul> |
|
1588
|
|
|
*/ |
|
1589
|
3 |
|
public function multi_query(string $sql) |
|
1590
|
|
|
{ |
|
1591
|
3 |
|
if (!$this->isReady()) { |
|
1592
|
|
|
return false; |
|
1593
|
|
|
} |
|
1594
|
|
|
|
|
1595
|
3 |
|
if (!$sql || $sql === '') { |
|
1596
|
3 |
|
$this->debug->displayError('Can not execute an empty query.', false); |
|
1597
|
|
|
|
|
1598
|
3 |
|
return false; |
|
1599
|
|
|
} |
|
1600
|
|
|
|
|
1601
|
3 |
|
if ($this->doctrine_connection && $this->isDoctrinePDOConnection()) { |
|
1602
|
|
|
$query_start_time = \microtime(true); |
|
1603
|
|
|
$queryException = null; |
|
1604
|
|
|
$query_result_doctrine = false; |
|
1605
|
|
|
|
|
1606
|
|
|
try { |
|
1607
|
|
|
$query_result_doctrine = $this->doctrine_connection->prepare($sql); |
|
1608
|
|
|
$resultTmp = $query_result_doctrine->execute(); |
|
1609
|
|
|
$mysqli_field_count = $query_result_doctrine->columnCount(); |
|
1610
|
|
|
} catch (\Exception $e) { |
|
1611
|
|
|
$resultTmp = false; |
|
1612
|
|
|
$mysqli_field_count = null; |
|
1613
|
|
|
|
|
1614
|
|
|
$queryException = $e; |
|
1615
|
|
|
} |
|
1616
|
|
|
|
|
1617
|
|
|
$query_duration = \microtime(true) - $query_start_time; |
|
1618
|
|
|
|
|
1619
|
|
|
$this->debug->logQuery($sql, $query_duration, 0); |
|
1620
|
|
|
|
|
1621
|
|
|
$returnTheResult = false; |
|
1622
|
|
|
$result = []; |
|
1623
|
|
|
|
|
1624
|
|
|
if ($resultTmp) { |
|
1625
|
|
|
if ($mysqli_field_count) { |
|
|
|
|
|
|
1626
|
|
|
if ( |
|
1627
|
|
|
$query_result_doctrine |
|
1628
|
|
|
&& |
|
1629
|
|
|
$query_result_doctrine instanceof \Doctrine\DBAL\Statement |
|
1630
|
|
|
) { |
|
1631
|
|
|
$result = $query_result_doctrine; |
|
1632
|
|
|
} |
|
1633
|
|
|
} else { |
|
1634
|
|
|
$result = $resultTmp; |
|
1635
|
|
|
} |
|
1636
|
|
|
|
|
1637
|
|
|
if ( |
|
1638
|
|
|
$result instanceof \Doctrine\DBAL\Statement |
|
1639
|
|
|
&& |
|
1640
|
|
|
$result->columnCount() > 0 |
|
1641
|
|
|
) { |
|
1642
|
|
|
$returnTheResult = true; |
|
1643
|
|
|
|
|
1644
|
|
|
// return query result object |
|
1645
|
|
|
$result = [new Result($sql, $result)]; |
|
1646
|
|
|
} else { |
|
1647
|
|
|
$result = [$result]; |
|
1648
|
|
|
} |
|
1649
|
|
|
} else { |
|
1650
|
|
|
|
|
1651
|
|
|
// log the error query |
|
1652
|
|
|
$this->debug->logQuery($sql, $query_duration, 0, true); |
|
1653
|
|
|
|
|
1654
|
|
|
if ( |
|
1655
|
|
|
isset($queryException) |
|
1656
|
|
|
&& |
|
1657
|
|
|
$queryException instanceof \Doctrine\DBAL\Query\QueryException |
|
1658
|
|
|
) { |
|
1659
|
|
|
return $this->queryErrorHandling($queryException->getMessage(), $queryException->getCode(), $sql, false, true); |
|
1660
|
|
|
} |
|
1661
|
|
|
} |
|
1662
|
3 |
|
} elseif ($this->mysqli_link) { |
|
1663
|
3 |
|
$query_start_time = \microtime(true); |
|
1664
|
3 |
|
$resultTmp = \mysqli_multi_query($this->mysqli_link, $sql); |
|
1665
|
3 |
|
$query_duration = \microtime(true) - $query_start_time; |
|
1666
|
|
|
|
|
1667
|
3 |
|
$this->debug->logQuery($sql, $query_duration, 0); |
|
1668
|
|
|
|
|
1669
|
3 |
|
$returnTheResult = false; |
|
1670
|
3 |
|
$result = []; |
|
1671
|
|
|
|
|
1672
|
3 |
|
if ($resultTmp) { |
|
1673
|
|
|
do { |
|
1674
|
3 |
|
$resultTmpInner = \mysqli_store_result($this->mysqli_link); |
|
1675
|
|
|
|
|
1676
|
3 |
|
if ($resultTmpInner instanceof \mysqli_result) { |
|
1677
|
3 |
|
$returnTheResult = true; |
|
1678
|
3 |
|
$result[] = new Result($sql, $resultTmpInner); |
|
1679
|
3 |
|
} elseif (\mysqli_errno($this->mysqli_link)) { |
|
1680
|
|
|
$result[] = false; |
|
1681
|
|
|
} else { |
|
1682
|
3 |
|
$result[] = true; |
|
1683
|
|
|
} |
|
1684
|
3 |
|
} while (\mysqli_more_results($this->mysqli_link) ? \mysqli_next_result($this->mysqli_link) : false); |
|
1685
|
|
|
} else { |
|
1686
|
|
|
|
|
1687
|
|
|
// log the error query |
|
1688
|
3 |
|
$this->debug->logQuery($sql, $query_duration, 0, true); |
|
1689
|
|
|
|
|
1690
|
3 |
|
return $this->queryErrorHandling(\mysqli_error($this->mysqli_link), \mysqli_errno($this->mysqli_link), $sql, false, true); |
|
1691
|
|
|
} |
|
1692
|
|
|
} else { |
|
1693
|
|
|
|
|
1694
|
|
|
// log the error query |
|
1695
|
|
|
$this->debug->logQuery($sql, 0, 0, true); |
|
1696
|
|
|
|
|
1697
|
|
|
return $this->queryErrorHandling('no database connection', 1, $sql, false, true); |
|
1698
|
|
|
} |
|
1699
|
|
|
|
|
1700
|
|
|
// return the result only if there was a "SELECT"-query |
|
1701
|
3 |
|
if ($returnTheResult) { |
|
1702
|
3 |
|
return $result; |
|
1703
|
|
|
} |
|
1704
|
|
|
|
|
1705
|
|
|
if ( |
|
1706
|
3 |
|
\count($result) > 0 |
|
1707
|
|
|
&& |
|
1708
|
3 |
|
!\in_array(false, $result, true) |
|
1709
|
|
|
) { |
|
1710
|
3 |
|
return true; |
|
1711
|
|
|
} |
|
1712
|
|
|
|
|
1713
|
|
|
return false; |
|
1714
|
|
|
} |
|
1715
|
|
|
|
|
1716
|
|
|
/** |
|
1717
|
|
|
* Count number of rows found matching a specific query. |
|
1718
|
|
|
* |
|
1719
|
|
|
* @param string $query |
|
1720
|
|
|
* |
|
1721
|
|
|
* @return int |
|
1722
|
|
|
*/ |
|
1723
|
3 |
|
public function num_rows(string $query): int |
|
1724
|
|
|
{ |
|
1725
|
3 |
|
$check = $this->query($query); |
|
1726
|
|
|
|
|
1727
|
|
|
if ( |
|
1728
|
3 |
|
$check === false |
|
1729
|
|
|
|| |
|
1730
|
3 |
|
!$check instanceof Result |
|
1731
|
|
|
) { |
|
1732
|
|
|
return 0; |
|
1733
|
|
|
} |
|
1734
|
|
|
|
|
1735
|
3 |
|
return $check->num_rows; |
|
1736
|
|
|
} |
|
1737
|
|
|
|
|
1738
|
|
|
/** |
|
1739
|
|
|
* Pings a server connection, or tries to reconnect |
|
1740
|
|
|
* if the connection has gone down. |
|
1741
|
|
|
* |
|
1742
|
|
|
* @return bool |
|
1743
|
|
|
*/ |
|
1744
|
9 |
|
public function ping(): bool |
|
1745
|
|
|
{ |
|
1746
|
9 |
|
if (!$this->connected) { |
|
1747
|
3 |
|
return false; |
|
1748
|
|
|
} |
|
1749
|
|
|
|
|
1750
|
6 |
|
if ($this->doctrine_connection && $this->isDoctrinePDOConnection()) { |
|
1751
|
|
|
return $this->doctrine_connection->ping(); |
|
1752
|
|
|
} |
|
1753
|
|
|
|
|
1754
|
|
|
if ( |
|
1755
|
6 |
|
$this->mysqli_link |
|
1756
|
|
|
&& |
|
1757
|
6 |
|
$this->mysqli_link instanceof \mysqli |
|
1758
|
|
|
) { |
|
1759
|
6 |
|
return \mysqli_ping($this->mysqli_link); |
|
1760
|
|
|
} |
|
1761
|
|
|
|
|
1762
|
|
|
return false; |
|
1763
|
|
|
} |
|
1764
|
|
|
|
|
1765
|
|
|
/** |
|
1766
|
|
|
* Get a new "Prepare"-Object for your sql-query. |
|
1767
|
|
|
* |
|
1768
|
|
|
* @param string $query |
|
1769
|
|
|
* |
|
1770
|
|
|
* @return Prepare |
|
1771
|
|
|
*/ |
|
1772
|
2 |
|
public function prepare(string $query): Prepare |
|
1773
|
|
|
{ |
|
1774
|
2 |
|
return new Prepare($this, $query); |
|
1775
|
|
|
} |
|
1776
|
|
|
|
|
1777
|
|
|
/** |
|
1778
|
|
|
* Execute a sql-query and return the result-array for select-statements. |
|
1779
|
|
|
* |
|
1780
|
|
|
* @param string $query |
|
1781
|
|
|
* |
|
1782
|
|
|
* @throws \Exception |
|
1783
|
|
|
* |
|
1784
|
|
|
* @return mixed |
|
1785
|
|
|
* |
|
1786
|
|
|
* @deprecated |
|
1787
|
|
|
*/ |
|
1788
|
3 |
|
public static function qry(string $query) |
|
|
|
|
|
|
1789
|
|
|
{ |
|
1790
|
3 |
|
$db = self::getInstance(); |
|
1791
|
|
|
|
|
1792
|
3 |
|
$args = \func_get_args(); |
|
1793
|
|
|
/** @noinspection SuspiciousAssignmentsInspection */ |
|
1794
|
3 |
|
$query = \array_shift($args); |
|
1795
|
3 |
|
$query = \str_replace('?', '%s', $query); |
|
1796
|
3 |
|
$args = \array_map( |
|
1797
|
|
|
[ |
|
1798
|
3 |
|
$db, |
|
1799
|
3 |
|
'escape', |
|
1800
|
|
|
], |
|
1801
|
3 |
|
$args |
|
1802
|
|
|
); |
|
1803
|
3 |
|
\array_unshift($args, $query); |
|
1804
|
3 |
|
$query = \sprintf(...$args); |
|
1805
|
3 |
|
$result = $db->query($query); |
|
1806
|
|
|
|
|
1807
|
3 |
|
if ($result instanceof Result) { |
|
1808
|
3 |
|
return $result->fetchAllArray(); |
|
1809
|
|
|
} |
|
1810
|
|
|
|
|
1811
|
3 |
|
return $result; |
|
1812
|
|
|
} |
|
1813
|
|
|
|
|
1814
|
|
|
/** |
|
1815
|
|
|
* Execute a sql-query. |
|
1816
|
|
|
* |
|
1817
|
|
|
* example: |
|
1818
|
|
|
* <code> |
|
1819
|
|
|
* $sql = "INSERT INTO TABLE_NAME_HERE |
|
1820
|
|
|
* SET |
|
1821
|
|
|
* foo = :foo, |
|
1822
|
|
|
* bar = :bar |
|
1823
|
|
|
* "; |
|
1824
|
|
|
* $insert_id = $db->query( |
|
1825
|
|
|
* $sql, |
|
1826
|
|
|
* [ |
|
1827
|
|
|
* 'foo' => 1.1, |
|
1828
|
|
|
* 'bar' => 1, |
|
1829
|
|
|
* ] |
|
1830
|
|
|
* ); |
|
1831
|
|
|
* </code> |
|
1832
|
|
|
* |
|
1833
|
|
|
* @param string $sql <p>The sql query-string.</p> |
|
1834
|
|
|
* @param array|bool $params <p> |
|
1835
|
|
|
* "array" of sql-query-parameters<br/> |
|
1836
|
|
|
* "false" if you don't need any parameter (default)<br/> |
|
1837
|
|
|
* </p> |
|
1838
|
|
|
* |
|
1839
|
|
|
* @throws QueryException |
|
1840
|
|
|
* |
|
1841
|
|
|
* @return bool|int|Result|string |
|
1842
|
|
|
* <p> |
|
1843
|
|
|
* "Result" by "<b>SELECT</b>"-queries<br /> |
|
1844
|
|
|
* "int|string" (insert_id) by "<b>INSERT / REPLACE</b>"-queries<br /> |
|
1845
|
|
|
* "int" (affected_rows) by "<b>UPDATE / DELETE</b>"-queries<br /> |
|
1846
|
|
|
* "true" by e.g. "DROP"-queries<br /> |
|
1847
|
|
|
* "false" on error |
|
1848
|
|
|
* </p> |
|
1849
|
|
|
*/ |
|
1850
|
140 |
|
public function query(string $sql = '', $params = false) |
|
1851
|
|
|
{ |
|
1852
|
140 |
|
if (!$this->isReady()) { |
|
1853
|
|
|
return false; |
|
1854
|
|
|
} |
|
1855
|
|
|
|
|
1856
|
140 |
|
if ($sql === '') { |
|
1857
|
12 |
|
$this->debug->displayError('Can not execute an empty query.', false); |
|
1858
|
|
|
|
|
1859
|
12 |
|
return false; |
|
1860
|
|
|
} |
|
1861
|
|
|
|
|
1862
|
|
|
if ( |
|
1863
|
134 |
|
$params !== false |
|
1864
|
|
|
&& |
|
1865
|
134 |
|
\is_array($params) |
|
1866
|
|
|
&& |
|
1867
|
134 |
|
\count($params) > 0 |
|
1868
|
|
|
) { |
|
1869
|
7 |
|
$parseQueryParams = $this->_parseQueryParams($sql, $params); |
|
1870
|
7 |
|
$parseQueryParamsByName = $this->_parseQueryParamsByName($parseQueryParams['sql'], $parseQueryParams['params']); |
|
1871
|
7 |
|
$sql = $parseQueryParamsByName['sql']; |
|
1872
|
|
|
} |
|
1873
|
|
|
|
|
1874
|
|
|
// DEBUG |
|
1875
|
|
|
// var_dump($params); |
|
1876
|
|
|
// echo $sql . "\n"; |
|
1877
|
|
|
|
|
1878
|
134 |
|
$query_start_time = \microtime(true); |
|
1879
|
134 |
|
$queryException = null; |
|
1880
|
134 |
|
$query_result_doctrine = false; |
|
1881
|
|
|
|
|
1882
|
134 |
|
if ($this->doctrine_connection) { |
|
1883
|
|
|
try { |
|
1884
|
|
|
$query_result_doctrine = $this->doctrine_connection->prepare($sql); |
|
1885
|
|
|
$query_result = $query_result_doctrine->execute(); |
|
1886
|
|
|
$mysqli_field_count = $query_result_doctrine->columnCount(); |
|
1887
|
|
|
} catch (\Exception $e) { |
|
1888
|
|
|
$query_result = false; |
|
1889
|
|
|
$mysqli_field_count = null; |
|
1890
|
|
|
|
|
1891
|
|
|
$queryException = $e; |
|
1892
|
|
|
} |
|
1893
|
134 |
|
} elseif ($this->mysqli_link) { |
|
1894
|
134 |
|
$query_result = \mysqli_real_query($this->mysqli_link, $sql); |
|
1895
|
134 |
|
$mysqli_field_count = \mysqli_field_count($this->mysqli_link); |
|
1896
|
|
|
} else { |
|
1897
|
|
|
$query_result = false; |
|
1898
|
|
|
$mysqli_field_count = null; |
|
1899
|
|
|
|
|
1900
|
|
|
$queryException = new DBConnectException('no mysql connection'); |
|
1901
|
|
|
} |
|
1902
|
|
|
|
|
1903
|
134 |
|
$query_duration = \microtime(true) - $query_start_time; |
|
1904
|
|
|
|
|
1905
|
134 |
|
$this->query_count++; |
|
1906
|
|
|
|
|
1907
|
134 |
|
if ($mysqli_field_count) { |
|
|
|
|
|
|
1908
|
99 |
|
if ($this->doctrine_connection) { |
|
1909
|
|
|
$result = false; |
|
1910
|
|
|
if ( |
|
1911
|
|
|
$query_result_doctrine |
|
1912
|
|
|
&& |
|
1913
|
|
|
$query_result_doctrine instanceof \Doctrine\DBAL\Statement |
|
1914
|
|
|
) { |
|
1915
|
|
|
$result = $query_result_doctrine; |
|
1916
|
|
|
} |
|
1917
|
99 |
|
} elseif ($this->mysqli_link) { |
|
1918
|
99 |
|
$result = \mysqli_store_result($this->mysqli_link); |
|
1919
|
|
|
} else { |
|
1920
|
99 |
|
$result = false; |
|
1921
|
|
|
} |
|
1922
|
|
|
} else { |
|
1923
|
106 |
|
$result = $query_result; |
|
1924
|
|
|
} |
|
1925
|
|
|
|
|
1926
|
|
|
if ( |
|
1927
|
134 |
|
$result instanceof \Doctrine\DBAL\Statement |
|
1928
|
|
|
&& |
|
1929
|
134 |
|
$result->columnCount() > 0 |
|
1930
|
|
|
) { |
|
1931
|
|
|
|
|
1932
|
|
|
// log the select query |
|
1933
|
|
|
$this->debug->logQuery($sql, $query_duration, $mysqli_field_count); |
|
1934
|
|
|
|
|
1935
|
|
|
// return query result object |
|
1936
|
|
|
return new Result($sql, $result); |
|
1937
|
|
|
} |
|
1938
|
|
|
|
|
1939
|
134 |
|
if ($result instanceof \mysqli_result) { |
|
1940
|
|
|
|
|
1941
|
|
|
// log the select query |
|
1942
|
96 |
|
$this->debug->logQuery($sql, $query_duration, $mysqli_field_count); |
|
1943
|
|
|
|
|
1944
|
|
|
// return query result object |
|
1945
|
96 |
|
return new Result($sql, $result); |
|
1946
|
|
|
} |
|
1947
|
|
|
|
|
1948
|
112 |
|
if ($query_result) { |
|
1949
|
|
|
|
|
1950
|
|
|
// "INSERT" || "REPLACE" |
|
1951
|
103 |
|
if (\preg_match('/^\s*?(?:INSERT|REPLACE)\s+/i', $sql)) { |
|
1952
|
100 |
|
$insert_id = $this->insert_id(); |
|
1953
|
|
|
|
|
1954
|
100 |
|
$this->debug->logQuery($sql, $query_duration, $insert_id); |
|
1955
|
|
|
|
|
1956
|
100 |
|
return $insert_id; |
|
1957
|
|
|
} |
|
1958
|
|
|
|
|
1959
|
|
|
// "UPDATE" || "DELETE" |
|
1960
|
50 |
|
if (\preg_match('/^\s*?(?:UPDATE|DELETE)\s+/i', $sql)) { |
|
1961
|
25 |
|
if ($this->mysqli_link) { |
|
1962
|
25 |
|
$this->affected_rows = $this->affected_rows(); |
|
1963
|
|
|
} elseif ($query_result_doctrine) { |
|
1964
|
|
|
$this->affected_rows = $query_result_doctrine->rowCount(); |
|
1965
|
|
|
} |
|
1966
|
|
|
|
|
1967
|
25 |
|
$this->debug->logQuery($sql, $query_duration, $this->affected_rows); |
|
1968
|
|
|
|
|
1969
|
25 |
|
return $this->affected_rows; |
|
1970
|
|
|
} |
|
1971
|
|
|
|
|
1972
|
|
|
// log the ? query |
|
1973
|
25 |
|
$this->debug->logQuery($sql, $query_duration, 0); |
|
1974
|
|
|
|
|
1975
|
25 |
|
return true; |
|
1976
|
|
|
} |
|
1977
|
|
|
|
|
1978
|
|
|
// log the error query |
|
1979
|
33 |
|
$this->debug->logQuery($sql, $query_duration, 0, true); |
|
1980
|
|
|
|
|
1981
|
33 |
|
if ($queryException) { |
|
1982
|
|
|
return $this->queryErrorHandling($queryException->getMessage(), $queryException->getCode(), $sql, $params); |
|
1983
|
|
|
} |
|
1984
|
|
|
|
|
1985
|
33 |
|
if ($this->mysqli_link) { |
|
1986
|
33 |
|
return $this->queryErrorHandling(\mysqli_error($this->mysqli_link), \mysqli_errno($this->mysqli_link), $sql, $params); |
|
1987
|
|
|
} |
|
1988
|
|
|
|
|
1989
|
|
|
return false; |
|
1990
|
|
|
} |
|
1991
|
|
|
|
|
1992
|
|
|
/** |
|
1993
|
|
|
* Error-handling for the sql-query. |
|
1994
|
|
|
* |
|
1995
|
|
|
* @param string $errorMessage |
|
1996
|
|
|
* @param int $errorNumber |
|
1997
|
|
|
* @param string $sql |
|
1998
|
|
|
* @param array|bool $sqlParams <p>false if there wasn't any parameter</p> |
|
1999
|
|
|
* @param bool $sqlMultiQuery |
|
2000
|
|
|
* |
|
2001
|
|
|
* @throws QueryException |
|
2002
|
|
|
* @throws DBGoneAwayException |
|
2003
|
|
|
* |
|
2004
|
|
|
* @return false|mixed |
|
2005
|
|
|
*/ |
|
2006
|
39 |
|
private function queryErrorHandling(string $errorMessage, int $errorNumber, string $sql, $sqlParams = false, bool $sqlMultiQuery = false) |
|
2007
|
|
|
{ |
|
2008
|
|
|
if ( |
|
2009
|
39 |
|
$errorMessage === 'DB server has gone away' |
|
2010
|
|
|
|| |
|
2011
|
36 |
|
$errorMessage === 'MySQL server has gone away' |
|
2012
|
|
|
|| |
|
2013
|
39 |
|
$errorNumber === 2006 |
|
2014
|
|
|
) { |
|
2015
|
3 |
|
static $RECONNECT_COUNTER; |
|
2016
|
|
|
|
|
2017
|
|
|
// exit if we have more then 3 "DB server has gone away"-errors |
|
2018
|
3 |
|
if ($RECONNECT_COUNTER > 3) { |
|
2019
|
|
|
$this->debug->mailToAdmin('DB-Fatal-Error', $errorMessage . '(' . $errorNumber . ') ' . ":\n<br />" . $sql, 5); |
|
2020
|
|
|
|
|
2021
|
|
|
throw new DBGoneAwayException($errorMessage); |
|
2022
|
|
|
} |
|
2023
|
|
|
|
|
2024
|
3 |
|
$this->debug->mailToAdmin('DB-Error', $errorMessage . '(' . $errorNumber . ') ' . ":\n<br />" . $sql); |
|
2025
|
|
|
|
|
2026
|
|
|
// reconnect |
|
2027
|
3 |
|
$RECONNECT_COUNTER++; |
|
2028
|
3 |
|
$this->reconnect(true); |
|
2029
|
|
|
|
|
2030
|
|
|
// re-run the current (non multi) query |
|
2031
|
3 |
|
if (!$sqlMultiQuery) { |
|
2032
|
3 |
|
return $this->query($sql, $sqlParams); |
|
2033
|
|
|
} |
|
2034
|
|
|
|
|
2035
|
|
|
return false; |
|
2036
|
|
|
} |
|
2037
|
|
|
|
|
2038
|
36 |
|
$this->debug->mailToAdmin('SQL-Error', $errorMessage . '(' . $errorNumber . ') ' . ":\n<br />" . $sql); |
|
2039
|
|
|
|
|
2040
|
36 |
|
$force_exception_after_error = null; // auto |
|
2041
|
36 |
|
if ($this->in_transaction) { |
|
2042
|
12 |
|
$force_exception_after_error = false; |
|
2043
|
|
|
} |
|
2044
|
|
|
// this query returned an error, we must display it (only for dev) !!! |
|
2045
|
|
|
|
|
2046
|
36 |
|
$this->debug->displayError($errorMessage . '(' . $errorNumber . ') ' . ' | ' . $sql, $force_exception_after_error); |
|
2047
|
|
|
|
|
2048
|
36 |
|
return false; |
|
2049
|
|
|
} |
|
2050
|
|
|
|
|
2051
|
|
|
/** |
|
2052
|
|
|
* Quote && Escape e.g. a table name string. |
|
2053
|
|
|
* |
|
2054
|
|
|
* @param mixed $str |
|
2055
|
|
|
* |
|
2056
|
|
|
* @return string |
|
2057
|
|
|
*/ |
|
2058
|
86 |
|
public function quote_string($str): string |
|
2059
|
|
|
{ |
|
2060
|
86 |
|
$str = \str_replace( |
|
2061
|
86 |
|
'`', |
|
2062
|
86 |
|
'``', |
|
2063
|
86 |
|
\trim( |
|
2064
|
86 |
|
(string) $this->escape($str, false), |
|
2065
|
86 |
|
'`' |
|
2066
|
|
|
) |
|
2067
|
|
|
); |
|
2068
|
|
|
|
|
2069
|
86 |
|
return '`' . $str . '`'; |
|
2070
|
|
|
} |
|
2071
|
|
|
|
|
2072
|
|
|
/** |
|
2073
|
|
|
* Reconnect to the MySQL-Server. |
|
2074
|
|
|
* |
|
2075
|
|
|
* @param bool $checkViaPing |
|
2076
|
|
|
* |
|
2077
|
|
|
* @return bool |
|
2078
|
|
|
*/ |
|
2079
|
7 |
|
public function reconnect(bool $checkViaPing = false): bool |
|
2080
|
|
|
{ |
|
2081
|
7 |
|
$ping = false; |
|
2082
|
7 |
|
if ($checkViaPing) { |
|
2083
|
6 |
|
$ping = $this->ping(); |
|
2084
|
|
|
} |
|
2085
|
|
|
|
|
2086
|
7 |
|
if (!$ping) { |
|
2087
|
7 |
|
$this->connected = false; |
|
2088
|
7 |
|
$this->connect(); |
|
2089
|
|
|
} |
|
2090
|
|
|
|
|
2091
|
7 |
|
return $this->isReady(); |
|
2092
|
|
|
} |
|
2093
|
|
|
|
|
2094
|
|
|
/** |
|
2095
|
|
|
* Execute a "replace"-query. |
|
2096
|
|
|
* |
|
2097
|
|
|
* @param string $table |
|
2098
|
|
|
* @param array $data |
|
2099
|
|
|
* @param string|null $databaseName <p>Use <strong>null</strong> if you will use the current database.</p> |
|
2100
|
|
|
* |
|
2101
|
|
|
* @throws QueryException |
|
2102
|
|
|
* |
|
2103
|
|
|
* @return false|int |
|
2104
|
|
|
* <p>false on error</p> |
|
2105
|
|
|
*/ |
|
2106
|
3 |
|
public function replace(string $table, array $data = [], string $databaseName = null) |
|
2107
|
|
|
{ |
|
2108
|
|
|
// init |
|
2109
|
3 |
|
$table = \trim($table); |
|
2110
|
|
|
|
|
2111
|
3 |
|
if ($table === '') { |
|
2112
|
3 |
|
$this->debug->displayError('Invalid table name, table name in empty.', false); |
|
2113
|
|
|
|
|
2114
|
3 |
|
return false; |
|
2115
|
|
|
} |
|
2116
|
|
|
|
|
2117
|
3 |
|
if (\count($data) === 0) { |
|
2118
|
3 |
|
$this->debug->displayError('Invalid data for REPLACE, data is empty.', false); |
|
2119
|
|
|
|
|
2120
|
3 |
|
return false; |
|
2121
|
|
|
} |
|
2122
|
|
|
|
|
2123
|
|
|
// extracting column names |
|
2124
|
3 |
|
$columns = \array_keys($data); |
|
2125
|
3 |
|
foreach ($columns as $k => $_key) { |
|
2126
|
3 |
|
$columns[$k] = $this->quote_string($_key); |
|
2127
|
|
|
} |
|
2128
|
|
|
|
|
2129
|
3 |
|
$columns = \implode(',', $columns); |
|
2130
|
|
|
|
|
2131
|
|
|
// extracting values |
|
2132
|
3 |
|
foreach ($data as $k => $_value) { |
|
2133
|
3 |
|
$data[$k] = $this->secure($_value); |
|
2134
|
|
|
} |
|
2135
|
3 |
|
$values = \implode(',', $data); |
|
2136
|
|
|
|
|
2137
|
3 |
|
if ($databaseName) { |
|
|
|
|
|
|
2138
|
|
|
$databaseName = $this->quote_string(\trim($databaseName)) . '.'; |
|
2139
|
|
|
} |
|
2140
|
|
|
|
|
2141
|
3 |
|
$sql = 'REPLACE INTO ' . $databaseName . $this->quote_string($table) . " (${columns}) VALUES (${values})"; |
|
2142
|
|
|
|
|
2143
|
3 |
|
$return = $this->query($sql); |
|
2144
|
3 |
|
\assert(\is_int($return) || $return === false); |
|
2145
|
|
|
|
|
2146
|
3 |
|
return $return; |
|
2147
|
|
|
} |
|
2148
|
|
|
|
|
2149
|
|
|
/** |
|
2150
|
|
|
* Rollback in a transaction and end the transaction. |
|
2151
|
|
|
* |
|
2152
|
|
|
* @return bool |
|
2153
|
|
|
* <p>bool true on success, false otherwise.</p> |
|
2154
|
|
|
*/ |
|
2155
|
12 |
View Code Duplication |
public function rollback(): bool |
|
|
|
|
|
|
2156
|
|
|
{ |
|
2157
|
12 |
|
if (!$this->in_transaction) { |
|
2158
|
|
|
$this->debug->displayError('Error: mysql server is not in transaction!', false); |
|
2159
|
|
|
|
|
2160
|
|
|
return false; |
|
2161
|
|
|
} |
|
2162
|
|
|
|
|
2163
|
|
|
// init |
|
2164
|
12 |
|
$return = false; |
|
2165
|
|
|
|
|
2166
|
12 |
|
if ($this->mysqli_link) { |
|
2167
|
12 |
|
$return = \mysqli_rollback($this->mysqli_link); |
|
2168
|
12 |
|
\mysqli_autocommit($this->mysqli_link, true); |
|
2169
|
|
|
} elseif ($this->doctrine_connection && $this->isDoctrinePDOConnection()) { |
|
2170
|
|
|
$this->doctrine_connection->rollBack(); |
|
2171
|
|
|
$this->doctrine_connection->setAutoCommit(true); |
|
2172
|
|
|
|
|
2173
|
|
|
if ($this->doctrine_connection->isAutoCommit()) { |
|
2174
|
|
|
$return = true; |
|
2175
|
|
|
} else { |
|
2176
|
|
|
$return = false; |
|
2177
|
|
|
} |
|
2178
|
|
|
} |
|
2179
|
|
|
|
|
2180
|
12 |
|
$this->in_transaction = false; |
|
2181
|
|
|
|
|
2182
|
12 |
|
return $return; |
|
2183
|
|
|
} |
|
2184
|
|
|
|
|
2185
|
|
|
/** |
|
2186
|
|
|
* Try to secure a variable, so can you use it in sql-queries. |
|
2187
|
|
|
* |
|
2188
|
|
|
* <p> |
|
2189
|
|
|
* <strong>int:</strong> (also strings that contains only an int-value)<br /> |
|
2190
|
|
|
* 1. parse into "int" |
|
2191
|
|
|
* </p><br /> |
|
2192
|
|
|
* |
|
2193
|
|
|
* <p> |
|
2194
|
|
|
* <strong>float:</strong><br /> |
|
2195
|
|
|
* 1. return "float" |
|
2196
|
|
|
* </p><br /> |
|
2197
|
|
|
* |
|
2198
|
|
|
* <p> |
|
2199
|
|
|
* <strong>string:</strong><br /> |
|
2200
|
|
|
* 1. check if the string isn't a default mysql-time-function e.g. 'CURDATE()'<br /> |
|
2201
|
|
|
* 2. trim '<br /> |
|
2202
|
|
|
* 3. escape the string (and remove non utf-8 chars)<br /> |
|
2203
|
|
|
* 4. trim ' again (because we maybe removed some chars)<br /> |
|
2204
|
|
|
* 5. add ' around the new string<br /> |
|
2205
|
|
|
* </p><br /> |
|
2206
|
|
|
* |
|
2207
|
|
|
* <p> |
|
2208
|
|
|
* <strong>array:</strong><br /> |
|
2209
|
|
|
* 1. return null |
|
2210
|
|
|
* </p><br /> |
|
2211
|
|
|
* |
|
2212
|
|
|
* <p> |
|
2213
|
|
|
* <strong>object:</strong><br /> |
|
2214
|
|
|
* 1. return false |
|
2215
|
|
|
* </p><br /> |
|
2216
|
|
|
* |
|
2217
|
|
|
* <p> |
|
2218
|
|
|
* <strong>null:</strong><br /> |
|
2219
|
|
|
* 1. return null |
|
2220
|
|
|
* </p> |
|
2221
|
|
|
* |
|
2222
|
|
|
* @param mixed $var |
|
2223
|
|
|
* @param bool|null $convert_array <strong>false</strong> => Keep the array.<br /> |
|
2224
|
|
|
* <strong>true</strong> => Convert to string var1,var2,var3...<br /> |
|
2225
|
|
|
* <strong>null</strong> => Convert the array into null, every time. |
|
2226
|
|
|
* |
|
2227
|
|
|
* @return mixed |
|
2228
|
|
|
*/ |
|
2229
|
87 |
|
public function secure($var, $convert_array = true) |
|
2230
|
|
|
{ |
|
2231
|
87 |
|
if (\is_array($var)) { |
|
2232
|
6 |
|
if ($convert_array === null) { |
|
2233
|
|
|
if ($this->convert_null_to_empty_string) { |
|
2234
|
|
|
$var = "''"; |
|
2235
|
|
|
} else { |
|
2236
|
|
|
$var = 'NULL'; |
|
2237
|
|
|
} |
|
2238
|
|
|
} else { |
|
2239
|
6 |
|
$varCleaned = []; |
|
2240
|
6 |
|
foreach ((array) $var as $key => $value) { |
|
2241
|
6 |
|
$key = $this->escape($key, false, false, $convert_array); |
|
2242
|
6 |
|
$value = $this->secure($value); |
|
2243
|
|
|
|
|
2244
|
|
|
/** @noinspection OffsetOperationsInspection */ |
|
2245
|
6 |
|
$varCleaned[$key] = $value; |
|
2246
|
|
|
} |
|
2247
|
|
|
|
|
2248
|
6 |
View Code Duplication |
if ($convert_array === true) { |
|
2249
|
6 |
|
$varCleaned = \implode(',', $varCleaned); |
|
2250
|
|
|
|
|
2251
|
6 |
|
$var = $varCleaned; |
|
2252
|
|
|
} else { |
|
2253
|
|
|
$var = $varCleaned; |
|
2254
|
|
|
} |
|
2255
|
|
|
} |
|
2256
|
|
|
|
|
2257
|
6 |
|
return $var; |
|
2258
|
|
|
} |
|
2259
|
|
|
|
|
2260
|
87 |
|
if ($var === '') { |
|
2261
|
6 |
|
return "''"; |
|
2262
|
|
|
} |
|
2263
|
|
|
|
|
2264
|
87 |
|
if ($var === "''") { |
|
2265
|
3 |
|
return "''"; |
|
2266
|
|
|
} |
|
2267
|
|
|
|
|
2268
|
87 |
|
if ($var === null) { |
|
2269
|
3 |
|
if ($this->convert_null_to_empty_string) { |
|
2270
|
3 |
|
return "''"; |
|
2271
|
|
|
} |
|
2272
|
|
|
|
|
2273
|
3 |
|
return 'NULL'; |
|
2274
|
|
|
} |
|
2275
|
|
|
|
|
2276
|
87 |
|
if (\in_array($var, $this->mysqlDefaultTimeFunctions, true)) { |
|
2277
|
3 |
|
return $var; |
|
2278
|
|
|
} |
|
2279
|
|
|
|
|
2280
|
87 |
|
if (\is_string($var)) { |
|
2281
|
77 |
|
$var = \trim($var, "'"); |
|
2282
|
|
|
} |
|
2283
|
|
|
|
|
2284
|
87 |
|
$var = $this->escape($var, false, false, null); |
|
2285
|
|
|
|
|
2286
|
84 |
|
if (\is_string($var)) { |
|
2287
|
77 |
|
$var = "'" . \trim($var, "'") . "'"; |
|
2288
|
|
|
} |
|
2289
|
|
|
|
|
2290
|
84 |
|
return $var; |
|
2291
|
|
|
} |
|
2292
|
|
|
|
|
2293
|
|
|
/** |
|
2294
|
|
|
* Execute a "select"-query. |
|
2295
|
|
|
* |
|
2296
|
|
|
* @param string $table |
|
2297
|
|
|
* @param array|string $where |
|
2298
|
|
|
* @param string|null $databaseName <p>Use <strong>null</strong> if you will use the current database.</p> |
|
2299
|
|
|
* |
|
2300
|
|
|
* @throws QueryException |
|
2301
|
|
|
* |
|
2302
|
|
|
* @return false|Result |
|
2303
|
|
|
* <p>false on error</p> |
|
2304
|
|
|
*/ |
|
2305
|
62 |
View Code Duplication |
public function select(string $table, $where = '1=1', string $databaseName = null) |
|
|
|
|
|
|
2306
|
|
|
{ |
|
2307
|
|
|
// init |
|
2308
|
62 |
|
$table = \trim($table); |
|
2309
|
|
|
|
|
2310
|
62 |
|
if ($table === '') { |
|
2311
|
3 |
|
$this->debug->displayError('Invalid table name, table name in empty.', false); |
|
2312
|
|
|
|
|
2313
|
3 |
|
return false; |
|
2314
|
|
|
} |
|
2315
|
|
|
|
|
2316
|
62 |
|
if (\is_string($where)) { |
|
2317
|
25 |
|
$WHERE = $this->escape($where, false); |
|
2318
|
41 |
|
} elseif (\is_array($where)) { |
|
2319
|
41 |
|
$WHERE = $this->_parseArrayPair($where, 'AND'); |
|
2320
|
|
|
} else { |
|
2321
|
3 |
|
$WHERE = ''; |
|
2322
|
|
|
} |
|
2323
|
|
|
|
|
2324
|
62 |
|
if ($databaseName) { |
|
|
|
|
|
|
2325
|
|
|
$databaseName = $this->quote_string(\trim($databaseName)) . '.'; |
|
2326
|
|
|
} |
|
2327
|
|
|
|
|
2328
|
62 |
|
$sql = 'SELECT * FROM ' . $databaseName . $this->quote_string($table) . " WHERE (${WHERE})"; |
|
2329
|
|
|
|
|
2330
|
62 |
|
$return = $this->query($sql); |
|
2331
|
62 |
|
\assert($return instanceof Result || $return === false); |
|
2332
|
|
|
|
|
2333
|
62 |
|
return $return; |
|
2334
|
|
|
} |
|
2335
|
|
|
|
|
2336
|
|
|
/** |
|
2337
|
|
|
* Selects a different database than the one specified on construction. |
|
2338
|
|
|
* |
|
2339
|
|
|
* @param string $database <p>Database name to switch to.</p> |
|
2340
|
|
|
* |
|
2341
|
|
|
* @return bool |
|
2342
|
|
|
* <p>bool true on success, false otherwise.</p> |
|
2343
|
|
|
*/ |
|
2344
|
|
|
public function select_db(string $database): bool |
|
2345
|
|
|
{ |
|
2346
|
|
|
if (!$this->isReady()) { |
|
2347
|
|
|
return false; |
|
2348
|
|
|
} |
|
2349
|
|
|
|
|
2350
|
|
|
if ($this->mysqli_link) { |
|
2351
|
|
|
return \mysqli_select_db($this->mysqli_link, $database); |
|
2352
|
|
|
} |
|
2353
|
|
|
|
|
2354
|
|
|
if ($this->doctrine_connection && $this->isDoctrinePDOConnection()) { |
|
2355
|
|
|
$return = $this->query('use :database', ['database' => $database]); |
|
2356
|
|
|
\assert(\is_bool($return)); |
|
2357
|
|
|
|
|
2358
|
|
|
return $return; |
|
2359
|
|
|
} |
|
2360
|
|
|
|
|
2361
|
|
|
return false; |
|
2362
|
|
|
} |
|
2363
|
|
|
|
|
2364
|
|
|
/** |
|
2365
|
|
|
* @param array $extra_config <p> |
|
2366
|
|
|
* 'session_to_db' => bool<br> |
|
2367
|
|
|
* 'doctrine' => \Doctrine\DBAL\Connection<br> |
|
2368
|
|
|
* 'socket' => string (path)<br> |
|
2369
|
|
|
* 'flags' => null|int<br> |
|
2370
|
|
|
* 'ssl' => bool<br> |
|
2371
|
|
|
* 'clientkey' => string (path)<br> |
|
2372
|
|
|
* 'clientcert' => string (path)<br> |
|
2373
|
|
|
* 'cacert' => string (path)<br> |
|
2374
|
|
|
* </p> |
|
2375
|
|
|
*/ |
|
2376
|
24 |
|
public function setConfigExtra(array $extra_config) |
|
2377
|
|
|
{ |
|
2378
|
24 |
|
if (isset($extra_config['session_to_db'])) { |
|
2379
|
|
|
$this->session_to_db = (bool) $extra_config['session_to_db']; |
|
2380
|
|
|
} |
|
2381
|
|
|
|
|
2382
|
24 |
|
if (isset($extra_config['doctrine'])) { |
|
2383
|
|
|
if ($extra_config['doctrine'] instanceof \Doctrine\DBAL\Connection) { |
|
2384
|
|
|
$this->doctrine_connection = $extra_config['doctrine']; |
|
2385
|
|
|
} else { |
|
2386
|
|
|
throw new DBConnectException('Error "doctrine"-connection is not valid'); |
|
2387
|
|
|
} |
|
2388
|
|
|
} |
|
2389
|
|
|
|
|
2390
|
24 |
|
if (isset($extra_config['socket'])) { |
|
2391
|
|
|
$this->socket = $extra_config['socket']; |
|
2392
|
|
|
} |
|
2393
|
|
|
|
|
2394
|
24 |
|
if (isset($extra_config['flags'])) { |
|
2395
|
1 |
|
$this->flags = $extra_config['flags']; |
|
2396
|
|
|
} |
|
2397
|
|
|
|
|
2398
|
24 |
|
if (isset($extra_config['ssl'])) { |
|
2399
|
|
|
$this->ssl = $extra_config['ssl']; |
|
2400
|
|
|
} |
|
2401
|
|
|
|
|
2402
|
24 |
|
if (isset($extra_config['clientkey'])) { |
|
2403
|
|
|
$this->clientkey = $extra_config['clientkey']; |
|
2404
|
|
|
} |
|
2405
|
|
|
|
|
2406
|
24 |
|
if (isset($extra_config['clientcert'])) { |
|
2407
|
|
|
$this->clientcert = $extra_config['clientcert']; |
|
2408
|
|
|
} |
|
2409
|
|
|
|
|
2410
|
24 |
|
if (isset($extra_config['cacert'])) { |
|
2411
|
|
|
$this->cacert = $extra_config['cacert']; |
|
2412
|
|
|
} |
|
2413
|
24 |
|
} |
|
2414
|
|
|
|
|
2415
|
|
|
/** |
|
2416
|
|
|
* Set the current charset. |
|
2417
|
|
|
* |
|
2418
|
|
|
* @param string $charset |
|
2419
|
|
|
* |
|
2420
|
|
|
* @return bool |
|
2421
|
|
|
*/ |
|
2422
|
15 |
|
public function set_charset(string $charset): bool |
|
2423
|
|
|
{ |
|
2424
|
15 |
|
$charsetLower = \strtolower($charset); |
|
2425
|
15 |
|
if ($charsetLower === 'utf8' || $charsetLower === 'utf-8') { |
|
2426
|
9 |
|
$charset = 'utf8'; |
|
2427
|
|
|
} |
|
2428
|
15 |
|
if ($charset === 'utf8' && Helper::isUtf8mb4Supported($this)) { |
|
2429
|
9 |
|
$charset = 'utf8mb4'; |
|
2430
|
|
|
} |
|
2431
|
|
|
|
|
2432
|
15 |
|
$this->charset = $charset; |
|
2433
|
|
|
|
|
2434
|
|
|
if ( |
|
2435
|
15 |
|
$this->mysqli_link |
|
2436
|
|
|
&& |
|
2437
|
15 |
|
$this->mysqli_link instanceof \mysqli |
|
2438
|
|
|
) { |
|
2439
|
15 |
|
$return = \mysqli_set_charset($this->mysqli_link, $charset); |
|
2440
|
|
|
|
|
2441
|
|
|
/** @noinspection PhpUsageOfSilenceOperatorInspection */ |
|
2442
|
15 |
|
@\mysqli_query($this->mysqli_link, 'SET CHARACTER SET ' . $charset); |
|
2443
|
|
|
/** @noinspection PhpUsageOfSilenceOperatorInspection */ |
|
2444
|
15 |
|
@\mysqli_query($this->mysqli_link, "SET NAMES '" . $charset . "'"); |
|
|
|
|
|
|
2445
|
|
|
} elseif ($this->doctrine_connection && $this->isDoctrinePDOConnection()) { |
|
2446
|
|
|
$doctrineWrappedConnection = $this->getDoctrinePDOConnection(); |
|
2447
|
|
|
if (!$doctrineWrappedConnection instanceof Connection) { |
|
2448
|
|
|
return false; |
|
2449
|
|
|
} |
|
2450
|
|
|
|
|
2451
|
|
|
/** @noinspection PhpUsageOfSilenceOperatorInspection */ |
|
2452
|
|
|
@$doctrineWrappedConnection->exec('SET CHARACTER SET ' . $charset); |
|
|
|
|
|
|
2453
|
|
|
/** @noinspection PhpUsageOfSilenceOperatorInspection */ |
|
2454
|
|
|
@$doctrineWrappedConnection->exec("SET NAMES '" . $charset . "'"); |
|
|
|
|
|
|
2455
|
|
|
|
|
2456
|
|
|
$return = true; |
|
2457
|
|
|
} else { |
|
2458
|
|
|
$return = false; |
|
2459
|
|
|
} |
|
2460
|
|
|
|
|
2461
|
15 |
|
return $return; |
|
2462
|
|
|
} |
|
2463
|
|
|
|
|
2464
|
|
|
/** |
|
2465
|
|
|
* Set the option to convert null to "''" (empty string). |
|
2466
|
|
|
* |
|
2467
|
|
|
* Used in secure() => select(), insert(), update(), delete() |
|
2468
|
|
|
* |
|
2469
|
|
|
* @deprecated It's not recommended to convert NULL into an empty string! |
|
2470
|
|
|
* |
|
2471
|
|
|
* @param bool $bool |
|
2472
|
|
|
* |
|
2473
|
|
|
* @return self |
|
2474
|
|
|
*/ |
|
2475
|
3 |
|
public function set_convert_null_to_empty_string(bool $bool): self |
|
2476
|
|
|
{ |
|
2477
|
3 |
|
$this->convert_null_to_empty_string = $bool; |
|
2478
|
|
|
|
|
2479
|
3 |
|
return $this; |
|
2480
|
|
|
} |
|
2481
|
|
|
|
|
2482
|
|
|
/** |
|
2483
|
|
|
* Enables or disables internal report functions |
|
2484
|
|
|
* |
|
2485
|
|
|
* @see http://php.net/manual/en/function.mysqli-report.php |
|
2486
|
|
|
* |
|
2487
|
|
|
* @param int $flags <p> |
|
2488
|
|
|
* <table> |
|
2489
|
|
|
* Supported flags |
|
2490
|
|
|
* <tr valign="top"> |
|
2491
|
|
|
* <td>Name</td> |
|
2492
|
|
|
* <td>Description</td> |
|
2493
|
|
|
* </tr> |
|
2494
|
|
|
* <tr valign="top"> |
|
2495
|
|
|
* <td><b>MYSQLI_REPORT_OFF</b></td> |
|
2496
|
|
|
* <td>Turns reporting off</td> |
|
2497
|
|
|
* </tr> |
|
2498
|
|
|
* <tr valign="top"> |
|
2499
|
|
|
* <td><b>MYSQLI_REPORT_ERROR</b></td> |
|
2500
|
|
|
* <td>Report errors from mysqli function calls</td> |
|
2501
|
|
|
* </tr> |
|
2502
|
|
|
* <tr valign="top"> |
|
2503
|
|
|
* <td><b>MYSQLI_REPORT_STRICT</b></td> |
|
2504
|
|
|
* <td> |
|
2505
|
|
|
* Throw <b>mysqli_sql_exception</b> for errors |
|
2506
|
|
|
* instead of warnings |
|
2507
|
|
|
* </td> |
|
2508
|
|
|
* </tr> |
|
2509
|
|
|
* <tr valign="top"> |
|
2510
|
|
|
* <td><b>MYSQLI_REPORT_INDEX</b></td> |
|
2511
|
|
|
* <td>Report if no index or bad index was used in a query</td> |
|
2512
|
|
|
* </tr> |
|
2513
|
|
|
* <tr valign="top"> |
|
2514
|
|
|
* <td><b>MYSQLI_REPORT_ALL</b></td> |
|
2515
|
|
|
* <td>Set all options (report all)</td> |
|
2516
|
|
|
* </tr> |
|
2517
|
|
|
* </table> |
|
2518
|
|
|
* </p> |
|
2519
|
|
|
* |
|
2520
|
|
|
* @return bool |
|
2521
|
|
|
*/ |
|
2522
|
|
|
public function set_mysqli_report(int $flags): bool |
|
2523
|
|
|
{ |
|
2524
|
|
|
if ( |
|
2525
|
|
|
$this->mysqli_link |
|
2526
|
|
|
&& |
|
2527
|
|
|
$this->mysqli_link instanceof \mysqli |
|
2528
|
|
|
) { |
|
2529
|
|
|
return \mysqli_report($flags); |
|
2530
|
|
|
} |
|
2531
|
|
|
|
|
2532
|
|
|
return false; |
|
2533
|
|
|
} |
|
2534
|
|
|
|
|
2535
|
|
|
/** |
|
2536
|
|
|
* Show config errors by throw exceptions. |
|
2537
|
|
|
* |
|
2538
|
|
|
* @throws \InvalidArgumentException |
|
2539
|
|
|
* |
|
2540
|
|
|
* @return bool |
|
2541
|
|
|
*/ |
|
2542
|
24 |
|
public function showConfigError(): bool |
|
2543
|
|
|
{ |
|
2544
|
|
|
// check if a doctrine connection is already open, first |
|
2545
|
|
|
if ( |
|
2546
|
24 |
|
$this->doctrine_connection |
|
2547
|
|
|
&& |
|
2548
|
24 |
|
$this->doctrine_connection->isConnected() |
|
2549
|
|
|
) { |
|
2550
|
|
|
return true; |
|
2551
|
|
|
} |
|
2552
|
|
|
|
|
2553
|
|
|
if ( |
|
2554
|
24 |
|
!$this->hostname |
|
2555
|
|
|
|| |
|
2556
|
21 |
|
!$this->username |
|
2557
|
|
|
|| |
|
2558
|
24 |
|
!$this->database |
|
2559
|
|
|
) { |
|
2560
|
9 |
|
if (!$this->hostname) { |
|
2561
|
3 |
|
throw new \InvalidArgumentException('no-sql-hostname'); |
|
2562
|
|
|
} |
|
2563
|
|
|
|
|
2564
|
6 |
|
if (!$this->username) { |
|
2565
|
3 |
|
throw new \InvalidArgumentException('no-sql-username'); |
|
2566
|
|
|
} |
|
2567
|
|
|
|
|
2568
|
3 |
|
if (!$this->database) { |
|
2569
|
3 |
|
throw new \InvalidArgumentException('no-sql-database'); |
|
2570
|
|
|
} |
|
2571
|
|
|
|
|
2572
|
|
|
return false; |
|
2573
|
|
|
} |
|
2574
|
|
|
|
|
2575
|
15 |
|
return true; |
|
2576
|
|
|
} |
|
2577
|
|
|
|
|
2578
|
|
|
/** |
|
2579
|
|
|
* alias: "beginTransaction()" |
|
2580
|
|
|
*/ |
|
2581
|
3 |
|
public function startTransaction(): bool |
|
2582
|
|
|
{ |
|
2583
|
3 |
|
return $this->beginTransaction(); |
|
2584
|
|
|
} |
|
2585
|
|
|
|
|
2586
|
|
|
/** |
|
2587
|
|
|
* Determine if database table exists |
|
2588
|
|
|
* |
|
2589
|
|
|
* @param string $table |
|
2590
|
|
|
* |
|
2591
|
|
|
* @return bool |
|
2592
|
|
|
*/ |
|
2593
|
3 |
|
public function table_exists(string $table): bool |
|
2594
|
|
|
{ |
|
2595
|
3 |
|
$check = $this->query('SELECT 1 FROM ' . $this->quote_string($table)); |
|
2596
|
|
|
|
|
2597
|
3 |
|
return $check !== false |
|
2598
|
|
|
&& |
|
2599
|
3 |
|
$check instanceof Result |
|
2600
|
|
|
&& |
|
2601
|
3 |
|
$check->num_rows > 0; |
|
2602
|
|
|
} |
|
2603
|
|
|
|
|
2604
|
|
|
/** |
|
2605
|
|
|
* Execute a callback inside a transaction. |
|
2606
|
|
|
* |
|
2607
|
|
|
* @param \Closure $callback <p>The callback to run inside the transaction, if it's throws an "Exception" or if it's |
|
2608
|
|
|
* returns "false", all SQL-statements in the callback will be rollbacked.</p> |
|
2609
|
|
|
* |
|
2610
|
|
|
* @return bool |
|
2611
|
|
|
* <p>bool true on success, false otherwise.</p> |
|
2612
|
|
|
*/ |
|
2613
|
3 |
|
public function transact($callback): bool |
|
2614
|
|
|
{ |
|
2615
|
|
|
try { |
|
2616
|
3 |
|
$beginTransaction = $this->beginTransaction(); |
|
2617
|
3 |
|
if (!$beginTransaction) { |
|
2618
|
3 |
|
$this->debug->displayError('Error: transact -> can not start transaction!', false); |
|
2619
|
|
|
|
|
2620
|
3 |
|
return false; |
|
2621
|
|
|
} |
|
2622
|
|
|
|
|
2623
|
3 |
|
$result = $callback($this); |
|
2624
|
3 |
|
if ($result === false) { |
|
2625
|
|
|
/** @noinspection ThrowRawExceptionInspection */ |
|
2626
|
3 |
|
throw new \Exception('call_user_func [' . \print_r($callback, true) . '] === false'); |
|
2627
|
|
|
} |
|
2628
|
|
|
|
|
2629
|
3 |
|
return $this->commit(); |
|
2630
|
3 |
|
} catch (\Exception $e) { |
|
2631
|
3 |
|
$this->rollback(); |
|
2632
|
|
|
|
|
2633
|
3 |
|
return false; |
|
2634
|
|
|
} |
|
2635
|
|
|
} |
|
2636
|
|
|
|
|
2637
|
|
|
/** |
|
2638
|
|
|
* Execute a "update"-query. |
|
2639
|
|
|
* |
|
2640
|
|
|
* @param string $table |
|
2641
|
|
|
* @param array $data |
|
2642
|
|
|
* @param array|string $where |
|
2643
|
|
|
* @param string|null $databaseName <p>Use <strong>null</strong> if you will use the current database.</p> |
|
2644
|
|
|
* |
|
2645
|
|
|
* @throws QueryException |
|
2646
|
|
|
* |
|
2647
|
|
|
* @return false|int |
|
2648
|
|
|
* <p>false on error</p> |
|
2649
|
|
|
*/ |
|
2650
|
21 |
|
public function update(string $table, array $data = [], $where = '1=1', string $databaseName = null) |
|
2651
|
|
|
{ |
|
2652
|
|
|
// init |
|
2653
|
21 |
|
$table = \trim($table); |
|
2654
|
|
|
|
|
2655
|
21 |
|
if ($table === '') { |
|
2656
|
3 |
|
$this->debug->displayError('Invalid table name, table name in empty.', false); |
|
2657
|
|
|
|
|
2658
|
3 |
|
return false; |
|
2659
|
|
|
} |
|
2660
|
|
|
|
|
2661
|
21 |
|
if (\count($data) === 0) { |
|
2662
|
6 |
|
$this->debug->displayError('Invalid data for UPDATE, data is empty.', false); |
|
2663
|
|
|
|
|
2664
|
6 |
|
return false; |
|
2665
|
|
|
} |
|
2666
|
|
|
|
|
2667
|
|
|
// DEBUG |
|
2668
|
|
|
//var_dump($data); |
|
2669
|
|
|
|
|
2670
|
21 |
|
$SET = $this->_parseArrayPair($data); |
|
2671
|
|
|
|
|
2672
|
|
|
// DEBUG |
|
2673
|
|
|
//var_dump($SET); |
|
2674
|
|
|
|
|
2675
|
21 |
|
if (\is_string($where)) { |
|
2676
|
6 |
|
$WHERE = $this->escape($where, false); |
|
2677
|
18 |
|
} elseif (\is_array($where)) { |
|
2678
|
15 |
|
$WHERE = $this->_parseArrayPair($where, 'AND'); |
|
2679
|
|
|
} else { |
|
2680
|
3 |
|
$WHERE = ''; |
|
2681
|
|
|
} |
|
2682
|
|
|
|
|
2683
|
21 |
|
if ($databaseName) { |
|
|
|
|
|
|
2684
|
|
|
$databaseName = $this->quote_string(\trim($databaseName)) . '.'; |
|
2685
|
|
|
} |
|
2686
|
|
|
|
|
2687
|
21 |
|
$sql = 'UPDATE ' . $databaseName . $this->quote_string($table) . " SET ${SET} WHERE (${WHERE})"; |
|
2688
|
|
|
|
|
2689
|
21 |
|
$return = $this->query($sql); |
|
2690
|
21 |
|
\assert(\is_int($return) || $return === false); |
|
2691
|
|
|
|
|
2692
|
21 |
|
return $return; |
|
2693
|
|
|
} |
|
2694
|
|
|
} |
|
2695
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.