|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/** |
|
4
|
|
|
* This file is part of the Phalcon Framework. |
|
5
|
|
|
* |
|
6
|
|
|
* (c) Phalcon Team <[email protected]> |
|
7
|
|
|
* |
|
8
|
|
|
* For the full copyright and license information, please view the LICENSE.txt |
|
9
|
|
|
* file that was distributed with this source code. |
|
10
|
|
|
* |
|
11
|
|
|
* Implementation of this file has been influenced by AtlasPHP |
|
12
|
|
|
* |
|
13
|
|
|
* @link https://github.com/atlasphp/Atlas.Pdo |
|
14
|
|
|
* @license https://github.com/atlasphp/Atlas.Pdo/blob/1.x/LICENSE.md |
|
15
|
|
|
*/ |
|
16
|
|
|
|
|
17
|
|
|
declare(strict_types=1); |
|
18
|
|
|
|
|
19
|
|
|
namespace Phalcon\DataMapper\Pdo\Connection; |
|
20
|
|
|
|
|
21
|
|
|
use BadMethodCallException; |
|
22
|
|
|
use PDO; |
|
23
|
|
|
use PDOStatement; |
|
24
|
|
|
use Phalcon\DataMapper\Pdo\Exception\CannotBindValue; |
|
25
|
|
|
use Phalcon\DataMapper\Pdo\Profiler\ProfilerInterface; |
|
26
|
|
|
|
|
27
|
|
|
use function array_merge; |
|
28
|
|
|
use function call_user_func_array; |
|
29
|
|
|
use function current; |
|
30
|
|
|
use function func_get_args; |
|
31
|
|
|
use function get_class; |
|
32
|
|
|
use function implode; |
|
33
|
|
|
use function is_array; |
|
34
|
|
|
use function is_bool; |
|
35
|
|
|
use function is_int; |
|
36
|
|
|
use function method_exists; |
|
37
|
|
|
|
|
38
|
|
|
/** |
|
39
|
|
|
* Provides array quoting, profiling, a new `perform()` method, new `fetch*()` |
|
40
|
|
|
* methods |
|
41
|
|
|
* |
|
42
|
|
|
* @property PDO $pdo |
|
43
|
|
|
* @property ProfilerInterface $profiler |
|
44
|
|
|
*/ |
|
45
|
|
|
abstract class AbstractConnection implements ConnectionInterface |
|
46
|
|
|
{ |
|
47
|
|
|
/** |
|
48
|
|
|
* @var PDO |
|
49
|
|
|
*/ |
|
50
|
|
|
protected $pdo; |
|
51
|
|
|
|
|
52
|
|
|
/** |
|
53
|
|
|
* @var ProfilerInterface |
|
54
|
|
|
*/ |
|
55
|
|
|
protected $profiler; |
|
56
|
|
|
|
|
57
|
|
|
/** |
|
58
|
|
|
* Proxies to PDO methods created for specific drivers; in particular, |
|
59
|
|
|
* `sqlite` and `pgsql`. |
|
60
|
|
|
* |
|
61
|
|
|
* @param string $name |
|
62
|
|
|
* @param array $arguments |
|
63
|
|
|
* |
|
64
|
|
|
* @return mixed |
|
65
|
|
|
* @throws BadMethodCallException |
|
66
|
|
|
*/ |
|
67
|
8 |
|
public function __call($name, array $arguments) |
|
68
|
|
|
{ |
|
69
|
8 |
|
$this->connect(); |
|
70
|
|
|
|
|
71
|
8 |
|
if (!method_exists($this->pdo, $name)) { |
|
72
|
4 |
|
$class = get_class($this); |
|
73
|
4 |
|
$message = "Class '" . $class |
|
74
|
4 |
|
. "' does not have a method '" . $name . "'"; |
|
75
|
4 |
|
throw new BadMethodCallException($message); |
|
76
|
|
|
} |
|
77
|
|
|
|
|
78
|
4 |
|
return call_user_func_array([$this->pdo, $name], $arguments); |
|
79
|
|
|
} |
|
80
|
|
|
|
|
81
|
|
|
/** |
|
82
|
|
|
* Begins a transaction. If the profiler is enabled, the operation will |
|
83
|
|
|
* be recorded. |
|
84
|
|
|
* |
|
85
|
|
|
* @return bool |
|
86
|
|
|
*/ |
|
87
|
8 |
|
public function beginTransaction(): bool |
|
88
|
|
|
{ |
|
89
|
8 |
|
$this->connect(); |
|
90
|
8 |
|
$this->profiler->start(__FUNCTION__); |
|
91
|
8 |
|
$result = $this->pdo->beginTransaction(); |
|
92
|
8 |
|
$this->profiler->finish(); |
|
93
|
|
|
|
|
94
|
8 |
|
return $result; |
|
95
|
|
|
} |
|
96
|
|
|
|
|
97
|
|
|
/** |
|
98
|
|
|
* Commits the existing transaction. If the profiler is enabled, the |
|
99
|
|
|
* operation will be recorded. |
|
100
|
|
|
* |
|
101
|
|
|
* @return bool |
|
102
|
|
|
*/ |
|
103
|
4 |
|
public function commit(): bool |
|
104
|
|
|
{ |
|
105
|
4 |
|
$this->connect(); |
|
106
|
4 |
|
$this->profiler->start(__FUNCTION__); |
|
107
|
4 |
|
$result = $this->pdo->commit(); |
|
108
|
4 |
|
$this->profiler->finish(); |
|
109
|
|
|
|
|
110
|
4 |
|
return $result; |
|
111
|
|
|
} |
|
112
|
|
|
|
|
113
|
|
|
/** |
|
114
|
|
|
* Connects to the database. |
|
115
|
|
|
*/ |
|
116
|
|
|
abstract public function connect(): void; |
|
117
|
|
|
|
|
118
|
|
|
/** |
|
119
|
|
|
* Disconnects from the database. |
|
120
|
|
|
*/ |
|
121
|
|
|
abstract public function disconnect(): void; |
|
122
|
|
|
|
|
123
|
|
|
/** |
|
124
|
|
|
* Gets the most recent error code. |
|
125
|
|
|
* |
|
126
|
|
|
* @return string|null |
|
127
|
|
|
*/ |
|
128
|
4 |
|
public function errorCode(): ?string |
|
129
|
|
|
{ |
|
130
|
4 |
|
$this->connect(); |
|
131
|
|
|
|
|
132
|
4 |
|
return $this->pdo->errorCode(); |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
|
|
/** |
|
136
|
|
|
* Gets the most recent error info. |
|
137
|
|
|
* |
|
138
|
|
|
* @return array |
|
139
|
|
|
*/ |
|
140
|
4 |
|
public function errorInfo(): array |
|
141
|
|
|
{ |
|
142
|
4 |
|
$this->connect(); |
|
143
|
|
|
|
|
144
|
4 |
|
return $this->pdo->errorInfo(); |
|
145
|
|
|
} |
|
146
|
|
|
|
|
147
|
|
|
/** |
|
148
|
|
|
* Executes an SQL statement and returns the number of affected rows. If |
|
149
|
|
|
* the profiler is enabled, the operation will be recorded. |
|
150
|
|
|
* |
|
151
|
|
|
* @param string $statement |
|
152
|
|
|
* |
|
153
|
|
|
* @return int |
|
154
|
|
|
*/ |
|
155
|
18 |
|
public function exec(string $statement): int |
|
156
|
|
|
{ |
|
157
|
18 |
|
$this->connect(); |
|
158
|
18 |
|
$this->profiler->start(__FUNCTION__); |
|
159
|
18 |
|
$affectedRows = $this->pdo->exec($statement); |
|
160
|
18 |
|
$this->profiler->finish($statement); |
|
161
|
|
|
|
|
162
|
18 |
|
return $affectedRows; |
|
163
|
|
|
} |
|
164
|
|
|
|
|
165
|
|
|
/** |
|
166
|
|
|
* Performs a statement and returns the number of affected rows. |
|
167
|
|
|
* |
|
168
|
|
|
* @param string $statement |
|
169
|
|
|
* @param array $values |
|
170
|
|
|
* |
|
171
|
|
|
* @return int |
|
172
|
|
|
* @throws CannotBindValue |
|
173
|
|
|
*/ |
|
174
|
4 |
|
public function fetchAffected(string $statement, array $values = []): int |
|
175
|
|
|
{ |
|
176
|
4 |
|
$sth = $this->perform($statement, $values); |
|
177
|
|
|
|
|
178
|
4 |
|
return $sth->rowCount(); |
|
179
|
|
|
} |
|
180
|
|
|
|
|
181
|
|
|
/** |
|
182
|
|
|
* Fetches a sequential array of rows from the database; the rows are |
|
183
|
|
|
* returned as associative arrays. |
|
184
|
|
|
* |
|
185
|
|
|
* @param string $statement |
|
186
|
|
|
* @param array $values |
|
187
|
|
|
* |
|
188
|
|
|
* @return array |
|
189
|
|
|
* @throws CannotBindValue |
|
190
|
|
|
*/ |
|
191
|
8 |
|
public function fetchAll(string $statement, array $values = []): array |
|
192
|
|
|
{ |
|
193
|
8 |
|
return $this->fetchData( |
|
194
|
8 |
|
"fetchAll", |
|
195
|
8 |
|
[PDO::FETCH_ASSOC], |
|
196
|
|
|
$statement, |
|
197
|
|
|
$values |
|
198
|
|
|
); |
|
199
|
|
|
} |
|
200
|
|
|
|
|
201
|
|
|
/** |
|
202
|
|
|
* Fetches an associative array of rows from the database; the rows are |
|
203
|
|
|
* returned as associative arrays, and the array of rows is keyed on the |
|
204
|
|
|
* first column of each row. |
|
205
|
|
|
* |
|
206
|
|
|
* If multiple rows have the same first column value, the last row with |
|
207
|
|
|
* that value will overwrite earlier rows. This method is more resource |
|
208
|
|
|
* intensive and should be avoided if possible. |
|
209
|
|
|
* |
|
210
|
|
|
* @param string $statement |
|
211
|
|
|
* @param array $values |
|
212
|
|
|
* |
|
213
|
|
|
* @return array |
|
214
|
|
|
* @throws CannotBindValue |
|
215
|
|
|
*/ |
|
216
|
4 |
|
public function fetchAssoc(string $statement, array $values = []): array |
|
217
|
|
|
{ |
|
218
|
4 |
|
$sth = $this->perform($statement, $values); |
|
219
|
4 |
|
$data = []; |
|
220
|
4 |
|
while ($row = $sth->fetch(PDO::FETCH_ASSOC)) { |
|
221
|
4 |
|
$data[current($row)] = $row; |
|
222
|
|
|
} |
|
223
|
|
|
|
|
224
|
4 |
|
return $data; |
|
225
|
|
|
} |
|
226
|
|
|
|
|
227
|
|
|
/** |
|
228
|
|
|
* Fetches a column of rows as a sequential array (default first one). |
|
229
|
|
|
* |
|
230
|
|
|
* @param string $statement |
|
231
|
|
|
* @param array $values |
|
232
|
|
|
* @param int $column |
|
233
|
|
|
* |
|
234
|
|
|
* @return array |
|
235
|
|
|
* @throws CannotBindValue |
|
236
|
|
|
*/ |
|
237
|
4 |
|
public function fetchColumn( |
|
238
|
|
|
string $statement, |
|
239
|
|
|
array $values = [], |
|
240
|
|
|
int $column = 0 |
|
241
|
|
|
): array { |
|
242
|
4 |
|
return $this->fetchData( |
|
243
|
4 |
|
"fetchAll", |
|
244
|
4 |
|
[PDO::FETCH_COLUMN, $column], |
|
245
|
|
|
$statement, |
|
246
|
|
|
$values |
|
247
|
|
|
); |
|
248
|
|
|
} |
|
249
|
|
|
|
|
250
|
|
|
/** |
|
251
|
|
|
* Fetches multiple from the database as an associative array. The first |
|
252
|
|
|
* column will be the index key. The default flags are |
|
253
|
|
|
* PDO::FETCH_ASSOC | PDO::FETCH_GROUP |
|
254
|
|
|
* |
|
255
|
|
|
* @param string $statement |
|
256
|
|
|
* @param array $values |
|
257
|
|
|
* @param int $flags |
|
258
|
|
|
* |
|
259
|
|
|
* @return array |
|
260
|
|
|
* @throws CannotBindValue |
|
261
|
|
|
*/ |
|
262
|
4 |
|
public function fetchGroup( |
|
263
|
|
|
string $statement, |
|
264
|
|
|
array $values = [], |
|
265
|
|
|
int $flags = PDO::FETCH_ASSOC |
|
266
|
|
|
): array { |
|
267
|
4 |
|
return $this->fetchData( |
|
268
|
4 |
|
"fetchAll", |
|
269
|
4 |
|
[PDO::FETCH_GROUP | $flags], |
|
270
|
|
|
$statement, |
|
271
|
|
|
$values |
|
272
|
|
|
); |
|
273
|
|
|
} |
|
274
|
|
|
|
|
275
|
|
|
/** |
|
276
|
|
|
* Fetches one row from the database as an object where the column values |
|
277
|
|
|
* are mapped to object properties. |
|
278
|
|
|
* |
|
279
|
|
|
* Since PDO injects property values before invoking the constructor, any |
|
280
|
|
|
* initializations for defaults that you potentially have in your object's |
|
281
|
|
|
* constructor, will override the values that have been injected by |
|
282
|
|
|
* `fetchObject`. The default object returned is `\stdClass` |
|
283
|
|
|
* |
|
284
|
|
|
* @param string $statement |
|
285
|
|
|
* @param array $values |
|
286
|
|
|
* @param string $class |
|
287
|
|
|
* @param array $arguments |
|
288
|
|
|
* |
|
289
|
|
|
* @return object |
|
290
|
|
|
* @throws CannotBindValue |
|
291
|
|
|
*/ |
|
292
|
8 |
|
public function fetchObject( |
|
293
|
|
|
string $statement, |
|
294
|
|
|
array $values = [], |
|
295
|
|
|
string $class = 'stdClass', |
|
296
|
|
|
array $arguments = [] |
|
297
|
|
|
): object { |
|
298
|
8 |
|
$sth = $this->perform($statement, $values); |
|
299
|
|
|
|
|
300
|
8 |
|
return $sth->fetchObject($class, $arguments); |
|
301
|
|
|
} |
|
302
|
|
|
|
|
303
|
|
|
/** |
|
304
|
|
|
* Fetches a sequential array of rows from the database; the rows are |
|
305
|
|
|
* returned as objects where the column values are mapped to object |
|
306
|
|
|
* properties. |
|
307
|
|
|
* |
|
308
|
|
|
* Since PDO injects property values before invoking the constructor, any |
|
309
|
|
|
* initializations for defaults that you potentially have in your object's |
|
310
|
|
|
* constructor, will override the values that have been injected by |
|
311
|
|
|
* `fetchObject`. The default object returned is `\stdClass` |
|
312
|
|
|
* |
|
313
|
|
|
* @param string $statement |
|
314
|
|
|
* @param array $values |
|
315
|
|
|
* @param string $class |
|
316
|
|
|
* @param array $arguments |
|
317
|
|
|
* |
|
318
|
|
|
* @return array |
|
319
|
|
|
* @throws CannotBindValue |
|
320
|
|
|
*/ |
|
321
|
8 |
|
public function fetchObjects( |
|
322
|
|
|
string $statement, |
|
323
|
|
|
array $values = [], |
|
324
|
|
|
string $class = 'stdClass', |
|
325
|
|
|
array $arguments = [] |
|
326
|
|
|
): array { |
|
327
|
8 |
|
$sth = $this->perform($statement, $values); |
|
328
|
|
|
|
|
329
|
8 |
|
return $sth->fetchAll(PDO::FETCH_CLASS, $class, $arguments); |
|
330
|
|
|
} |
|
331
|
|
|
|
|
332
|
|
|
/** |
|
333
|
|
|
* Fetches one row from the database as an associative array. |
|
334
|
|
|
* |
|
335
|
|
|
* @param string $statement |
|
336
|
|
|
* @param array $values |
|
337
|
|
|
* |
|
338
|
|
|
* @return array |
|
339
|
|
|
* @throws CannotBindValue |
|
340
|
|
|
*/ |
|
341
|
26 |
|
public function fetchOne(string $statement, array $values = []): array |
|
342
|
|
|
{ |
|
343
|
26 |
|
return $this->fetchData( |
|
344
|
26 |
|
"fetch", |
|
345
|
26 |
|
[PDO::FETCH_ASSOC], |
|
346
|
|
|
$statement, |
|
347
|
|
|
$values |
|
348
|
|
|
); |
|
349
|
|
|
} |
|
350
|
|
|
|
|
351
|
|
|
/** |
|
352
|
|
|
* Fetches an associative array of rows as key-value pairs (first column is |
|
353
|
|
|
* the key, second column is the value). |
|
354
|
|
|
* |
|
355
|
|
|
* @param string $statement |
|
356
|
|
|
* @param array $values |
|
357
|
|
|
* |
|
358
|
|
|
* @return array |
|
359
|
|
|
* @throws CannotBindValue |
|
360
|
|
|
*/ |
|
361
|
4 |
|
public function fetchPairs(string $statement, array $values = []): array |
|
362
|
|
|
{ |
|
363
|
4 |
|
return $this->fetchData( |
|
364
|
4 |
|
"fetchAll", |
|
365
|
4 |
|
[PDO::FETCH_KEY_PAIR], |
|
366
|
|
|
$statement, |
|
367
|
|
|
$values |
|
368
|
|
|
); |
|
369
|
|
|
} |
|
370
|
|
|
|
|
371
|
|
|
/** |
|
372
|
|
|
* Fetches the very first value (i.e., first column of the first row). |
|
373
|
|
|
* |
|
374
|
|
|
* @param string $statement |
|
375
|
|
|
* @param array $values |
|
376
|
|
|
* |
|
377
|
|
|
* @return mixed |
|
378
|
|
|
* @throws CannotBindValue |
|
379
|
|
|
*/ |
|
380
|
4 |
|
public function fetchValue(string $statement, array $values = []) |
|
381
|
|
|
{ |
|
382
|
4 |
|
$sth = $this->perform($statement, $values); |
|
383
|
|
|
|
|
384
|
4 |
|
return $sth->fetchColumn(0); |
|
385
|
|
|
} |
|
386
|
|
|
|
|
387
|
|
|
/** |
|
388
|
|
|
* Return the inner PDO (if any) |
|
389
|
|
|
* |
|
390
|
|
|
* @return PDO |
|
391
|
|
|
*/ |
|
392
|
92 |
|
public function getAdapter(): PDO |
|
393
|
|
|
{ |
|
394
|
92 |
|
$this->connect(); |
|
395
|
|
|
|
|
396
|
92 |
|
return $this->pdo; |
|
397
|
|
|
} |
|
398
|
|
|
|
|
399
|
|
|
/** |
|
400
|
|
|
* Retrieve a database connection attribute |
|
401
|
|
|
* |
|
402
|
|
|
* @param int $attribute |
|
403
|
|
|
* |
|
404
|
|
|
* @return mixed |
|
405
|
|
|
*/ |
|
406
|
4 |
|
public function getAttribute($attribute) |
|
407
|
|
|
{ |
|
408
|
4 |
|
$this->connect(); |
|
409
|
|
|
|
|
410
|
4 |
|
return $this->pdo->getAttribute($attribute); |
|
411
|
|
|
} |
|
412
|
|
|
|
|
413
|
|
|
/** |
|
414
|
|
|
* Return an array of available PDO drivers (empty array if none available) |
|
415
|
|
|
* |
|
416
|
|
|
* @return array |
|
417
|
|
|
*/ |
|
418
|
4 |
|
public static function getAvailableDrivers(): array |
|
419
|
|
|
{ |
|
420
|
4 |
|
return PDO::getAvailableDrivers(); |
|
421
|
|
|
} |
|
422
|
|
|
|
|
423
|
|
|
/** |
|
424
|
|
|
* Return the driver name |
|
425
|
|
|
* |
|
426
|
|
|
* @return string |
|
427
|
|
|
*/ |
|
428
|
140 |
|
public function getDriverName(): string |
|
429
|
|
|
{ |
|
430
|
140 |
|
$this->connect(); |
|
431
|
|
|
|
|
432
|
140 |
|
return $this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME); |
|
433
|
|
|
} |
|
434
|
|
|
|
|
435
|
|
|
/** |
|
436
|
|
|
* Returns the Profiler instance. |
|
437
|
|
|
* |
|
438
|
|
|
* @return ProfilerInterface |
|
439
|
|
|
*/ |
|
440
|
8 |
|
public function getProfiler(): ProfilerInterface |
|
441
|
|
|
{ |
|
442
|
8 |
|
return $this->profiler; |
|
443
|
|
|
} |
|
444
|
|
|
|
|
445
|
|
|
/** |
|
446
|
|
|
* Gets the quote parameters based on the driver |
|
447
|
|
|
* |
|
448
|
|
|
* @param string $driver |
|
449
|
|
|
* |
|
450
|
|
|
* @return array |
|
451
|
|
|
*/ |
|
452
|
354 |
|
public function getQuoteNames($driver = ""): array |
|
453
|
|
|
{ |
|
454
|
354 |
|
$driver = "" === $driver ? $this->getDriverName() : $driver; |
|
455
|
354 |
|
switch ($driver) { |
|
456
|
354 |
|
case 'mysql': |
|
457
|
|
|
return [ |
|
458
|
180 |
|
"prefix" => '`', |
|
459
|
|
|
"suffix" => '`', |
|
460
|
|
|
"find" => '`', |
|
461
|
|
|
"replace" => '``', |
|
462
|
|
|
]; |
|
463
|
|
|
|
|
464
|
178 |
|
case 'sqlsrv': |
|
465
|
|
|
return [ |
|
466
|
4 |
|
"prefix" => '[', |
|
467
|
|
|
"suffix" => ']', |
|
468
|
|
|
"find" => ']', |
|
469
|
|
|
"replace" => '][', |
|
470
|
|
|
]; |
|
471
|
|
|
|
|
472
|
|
|
default: |
|
473
|
|
|
return [ |
|
474
|
178 |
|
"prefix" => '"', |
|
475
|
|
|
"suffix" => '"', |
|
476
|
|
|
"find" => '"', |
|
477
|
|
|
"replace" => '""', |
|
478
|
|
|
]; |
|
479
|
|
|
} |
|
480
|
|
|
} |
|
481
|
|
|
|
|
482
|
|
|
/** |
|
483
|
|
|
* Is a transaction currently active? If the profiler is enabled, the |
|
484
|
|
|
* operation will be recorded. If the profiler is enabled, the operation |
|
485
|
|
|
* will be recorded. |
|
486
|
|
|
* |
|
487
|
|
|
* @return bool |
|
488
|
|
|
*/ |
|
489
|
8 |
|
public function inTransaction(): bool |
|
490
|
|
|
{ |
|
491
|
8 |
|
$this->connect(); |
|
492
|
8 |
|
$this->profiler->start(__FUNCTION__); |
|
493
|
8 |
|
$result = $this->pdo->inTransaction(); |
|
494
|
8 |
|
$this->profiler->finish(); |
|
495
|
8 |
|
return $result; |
|
496
|
|
|
} |
|
497
|
|
|
|
|
498
|
|
|
/** |
|
499
|
|
|
* Is the PDO connection active? |
|
500
|
|
|
* |
|
501
|
|
|
* @return bool |
|
502
|
|
|
*/ |
|
503
|
14 |
|
public function isConnected(): bool |
|
504
|
|
|
{ |
|
505
|
14 |
|
return (bool) $this->pdo; |
|
506
|
|
|
} |
|
507
|
|
|
|
|
508
|
|
|
/** |
|
509
|
|
|
* Returns the last inserted autoincrement sequence value. If the profiler |
|
510
|
|
|
* is enabled, the operation will be recorded. |
|
511
|
|
|
* |
|
512
|
|
|
* @param string $name |
|
513
|
|
|
* |
|
514
|
|
|
* @return string |
|
515
|
|
|
*/ |
|
516
|
8 |
|
public function lastInsertId(string $name = null): string |
|
517
|
|
|
{ |
|
518
|
8 |
|
$this->connect(); |
|
519
|
|
|
|
|
520
|
8 |
|
$this->profiler->start(__FUNCTION__); |
|
521
|
8 |
|
$result = $this->pdo->lastInsertId($name); |
|
522
|
8 |
|
$this->profiler->finish(); |
|
523
|
|
|
|
|
524
|
8 |
|
return $result; |
|
525
|
|
|
} |
|
526
|
|
|
|
|
527
|
|
|
/** |
|
528
|
|
|
* Performs a query with bound values and returns the resulting |
|
529
|
|
|
* PDOStatement; array values will be passed through `quote()` and their |
|
530
|
|
|
* respective placeholders will be replaced in the query string. If the |
|
531
|
|
|
* profiler is enabled, the operation will be recorded. |
|
532
|
|
|
* |
|
533
|
|
|
* @param string $statement |
|
534
|
|
|
* @param array $values |
|
535
|
|
|
* |
|
536
|
|
|
* @return PDOStatement |
|
537
|
|
|
*/ |
|
538
|
74 |
|
public function perform(string $statement, array $values = []): PDOStatement |
|
539
|
|
|
{ |
|
540
|
74 |
|
$this->connect(); |
|
541
|
|
|
|
|
542
|
74 |
|
$this->profiler->start(__FUNCTION__); |
|
543
|
|
|
|
|
544
|
74 |
|
$sth = $this->prepare($statement); |
|
545
|
74 |
|
foreach ($values as $name => $value) { |
|
546
|
36 |
|
$this->performBind($sth, $name, $value); |
|
|
|
|
|
|
547
|
|
|
} |
|
548
|
74 |
|
$sth->execute(); |
|
549
|
|
|
|
|
550
|
74 |
|
$this->profiler->finish($statement, $values); |
|
551
|
|
|
|
|
552
|
74 |
|
return $sth; |
|
553
|
|
|
} |
|
554
|
|
|
|
|
555
|
|
|
/** |
|
556
|
|
|
* Prepares an SQL statement for execution. |
|
557
|
|
|
* |
|
558
|
|
|
* @param string $statement |
|
559
|
|
|
* @param array $options |
|
560
|
|
|
* |
|
561
|
|
|
* @return PDOStatement|false |
|
562
|
|
|
*/ |
|
563
|
74 |
|
public function prepare(string $statement, array $options = []) |
|
564
|
|
|
{ |
|
565
|
74 |
|
$this->connect(); |
|
566
|
|
|
|
|
567
|
74 |
|
$this->profiler->start(__FUNCTION__); |
|
568
|
74 |
|
$sth = $this->pdo->prepare($statement, $options); |
|
569
|
74 |
|
$this->profiler->finish($sth->queryString); |
|
570
|
|
|
|
|
571
|
74 |
|
return $sth; |
|
572
|
|
|
} |
|
573
|
|
|
|
|
574
|
|
|
/** |
|
575
|
|
|
* Queries the database and returns a PDOStatement. If the profiler is |
|
576
|
|
|
* enabled, the operation will be recorded. |
|
577
|
|
|
* |
|
578
|
|
|
* @param string $statement |
|
579
|
|
|
* @param mixed ...$fetch |
|
580
|
|
|
* |
|
581
|
|
|
* @return PDOStatement|false |
|
582
|
|
|
*/ |
|
583
|
4 |
|
public function query(string $statement) |
|
584
|
|
|
{ |
|
585
|
4 |
|
$this->connect(); |
|
586
|
|
|
|
|
587
|
4 |
|
$this->profiler->start(__FUNCTION__); |
|
588
|
|
|
$sth = call_user_func_array( |
|
589
|
|
|
[ |
|
590
|
4 |
|
$this->pdo, |
|
591
|
4 |
|
"query", |
|
592
|
|
|
], |
|
593
|
4 |
|
func_get_args() |
|
594
|
|
|
); |
|
595
|
4 |
|
$this->profiler->finish($sth->queryString); |
|
596
|
|
|
|
|
597
|
4 |
|
return $sth; |
|
598
|
|
|
} |
|
599
|
|
|
|
|
600
|
|
|
/** |
|
601
|
|
|
* Quotes a value for use in an SQL statement. This differs from |
|
602
|
|
|
* `PDO::quote()` in that it will convert an array into a string of |
|
603
|
|
|
* comma-separated quoted values. The default type is `PDO::PARAM_STR` |
|
604
|
|
|
* |
|
605
|
|
|
* @param mixed $value |
|
606
|
|
|
* @param int $type |
|
607
|
|
|
* |
|
608
|
|
|
* @return string The quoted value. |
|
609
|
|
|
*/ |
|
610
|
20 |
|
public function quote($value, int $type = PDO::PARAM_STR): string |
|
611
|
|
|
{ |
|
612
|
20 |
|
$this->connect(); |
|
613
|
|
|
|
|
614
|
20 |
|
$quotes = $this->getQuoteNames(); |
|
615
|
20 |
|
if (!is_array($value)) { |
|
616
|
20 |
|
$value = (string) $value; |
|
617
|
|
|
|
|
618
|
20 |
|
return $quotes["prefix"] . $value . $quotes["suffix"]; |
|
619
|
|
|
} |
|
620
|
|
|
|
|
621
|
|
|
// quote array values, not keys, then combine with commas |
|
622
|
4 |
|
foreach ($value as $key => $element) { |
|
623
|
4 |
|
$element = (string) $element; |
|
624
|
4 |
|
$value[$key] = $quotes["prefix"] . $element . $quotes["suffix"]; |
|
625
|
|
|
} |
|
626
|
|
|
|
|
627
|
4 |
|
return implode(', ', $value); |
|
628
|
|
|
} |
|
629
|
|
|
|
|
630
|
|
|
/** |
|
631
|
|
|
* Rolls back the current transaction, and restores autocommit mode. If the |
|
632
|
|
|
* profiler is enabled, the operation will be recorded. |
|
633
|
|
|
* |
|
634
|
|
|
* @return bool |
|
635
|
|
|
*/ |
|
636
|
4 |
|
public function rollBack(): bool |
|
637
|
|
|
{ |
|
638
|
4 |
|
$this->connect(); |
|
639
|
|
|
|
|
640
|
4 |
|
$this->profiler->start(__FUNCTION__); |
|
641
|
4 |
|
$result = $this->pdo->rollBack(); |
|
642
|
4 |
|
$this->profiler->finish(); |
|
643
|
|
|
|
|
644
|
4 |
|
return $result; |
|
645
|
|
|
} |
|
646
|
|
|
|
|
647
|
|
|
/** |
|
648
|
|
|
* Set a database connection attribute |
|
649
|
|
|
* |
|
650
|
|
|
* @param int $attribute |
|
651
|
|
|
* @param mixed $value |
|
652
|
|
|
* |
|
653
|
|
|
* @return bool |
|
654
|
|
|
*/ |
|
655
|
4 |
|
public function setAttribute(int $attribute, $value): bool |
|
656
|
|
|
{ |
|
657
|
4 |
|
$this->connect(); |
|
658
|
|
|
|
|
659
|
4 |
|
return $this->pdo->setAttribute($attribute, $value); |
|
660
|
|
|
} |
|
661
|
|
|
|
|
662
|
|
|
/** |
|
663
|
|
|
* Sets the Profiler instance. |
|
664
|
|
|
* |
|
665
|
|
|
* @param ProfilerInterface $profiler |
|
666
|
|
|
*/ |
|
667
|
362 |
|
public function setProfiler(ProfilerInterface $profiler) |
|
668
|
|
|
{ |
|
669
|
362 |
|
$this->profiler = $profiler; |
|
670
|
362 |
|
} |
|
671
|
|
|
|
|
672
|
|
|
/** |
|
673
|
|
|
* Bind a value using the proper PDO::PARAM_* type. |
|
674
|
|
|
* |
|
675
|
|
|
* @param PDOStatement $statement |
|
676
|
|
|
* @param mixed $name |
|
677
|
|
|
* @param mixed $arguments |
|
678
|
|
|
*/ |
|
679
|
36 |
|
protected function performBind(PDOStatement $statement, $name, $arguments): void |
|
680
|
|
|
{ |
|
681
|
36 |
|
if (is_int($name)) { |
|
682
|
32 |
|
$name++; |
|
683
|
|
|
} |
|
684
|
|
|
|
|
685
|
36 |
|
if (is_array($arguments)) { |
|
686
|
8 |
|
$type = $arguments[1] ?? PDO::PARAM_STR; |
|
687
|
8 |
|
if ($type === PDO::PARAM_BOOL && is_bool($arguments[0])) { |
|
688
|
4 |
|
$arguments[0] = $arguments[0] ? '1' : '0'; |
|
689
|
|
|
} |
|
690
|
|
|
|
|
691
|
8 |
|
$parameters = array_merge([$name], $arguments); |
|
692
|
|
|
} else { |
|
693
|
32 |
|
$parameters = [$name, $arguments]; |
|
694
|
|
|
} |
|
695
|
|
|
|
|
696
|
|
|
call_user_func_array( |
|
697
|
|
|
[ |
|
698
|
36 |
|
$statement, |
|
699
|
36 |
|
"bindValue", |
|
700
|
|
|
], |
|
701
|
|
|
$parameters |
|
702
|
|
|
); |
|
703
|
36 |
|
} |
|
704
|
|
|
|
|
705
|
|
|
/** |
|
706
|
|
|
* Helper method to get data from PDO based on the method passed |
|
707
|
|
|
* |
|
708
|
|
|
* @param string $method |
|
709
|
|
|
* @param array $arguments |
|
710
|
|
|
* @param string $statement |
|
711
|
|
|
* @param array $values |
|
712
|
|
|
* |
|
713
|
|
|
* @return array |
|
714
|
|
|
*/ |
|
715
|
46 |
|
protected function fetchData( |
|
716
|
|
|
string $method, |
|
717
|
|
|
array $arguments, |
|
718
|
|
|
string $statement, |
|
719
|
|
|
array $values = [] |
|
720
|
|
|
) { |
|
721
|
46 |
|
$sth = $this->perform($statement, $values); |
|
722
|
46 |
|
$result = call_user_func_array([$sth, $method], $arguments); |
|
723
|
|
|
|
|
724
|
|
|
/** |
|
725
|
|
|
* If this returns boolean or anything other than an array, return |
|
726
|
|
|
* an empty array back |
|
727
|
|
|
*/ |
|
728
|
46 |
|
if (!is_array($result)) { |
|
729
|
8 |
|
$result = []; |
|
730
|
|
|
} |
|
731
|
|
|
|
|
732
|
46 |
|
return $result; |
|
733
|
|
|
} |
|
734
|
|
|
} |
|
735
|
|
|
|
This check looks for type mismatches where the missing type is
false. This is usually indicative of an error condtion.Consider the follow example
This function either returns a new
DateTimeobject or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returnedfalsebefore passing on the value to another function or method that may not be able to handle afalse.