1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Licensed to CRATE Technology GmbH("Crate") under one or more contributor |
4
|
|
|
* license agreements. See the NOTICE file distributed with this work for |
5
|
|
|
* additional information regarding copyright ownership. Crate licenses |
6
|
|
|
* this file to you under the Apache License, Version 2.0 (the "License"); |
7
|
|
|
* you may not use this file except in compliance with the License. You may |
8
|
|
|
* obtain a copy of the License at |
9
|
|
|
* |
10
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0 |
11
|
|
|
* |
12
|
|
|
* Unless required by applicable law or agreed to in writing, software |
13
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
14
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
15
|
|
|
* License for the specific language governing permissions and limitations |
16
|
|
|
* under the License. |
17
|
|
|
* |
18
|
|
|
* However, if you have executed another commercial license agreement |
19
|
|
|
* with Crate these terms will supersede the license and you may use the |
20
|
|
|
* software solely pursuant to the terms of the relevant commercial agreement. |
21
|
|
|
*/ |
22
|
|
|
|
23
|
|
|
namespace Crate\PDO; |
24
|
|
|
|
25
|
|
|
use ArrayIterator; |
26
|
|
|
use Closure; |
27
|
|
|
use Crate\Stdlib\ArrayUtils; |
28
|
|
|
use Crate\Stdlib\CollectionInterface; |
29
|
|
|
use Crate\Stdlib\CrateConst; |
30
|
|
|
use InvalidArgumentException; |
31
|
|
|
use IteratorAggregate; |
32
|
|
|
use PDOStatement as BasePDOStatement; |
33
|
|
|
|
34
|
|
|
class PDOStatement extends BasePDOStatement implements IteratorAggregate |
35
|
|
|
{ |
36
|
|
|
/** |
37
|
|
|
* @var array |
38
|
|
|
*/ |
39
|
|
|
private $parameters = []; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @var string|null |
43
|
|
|
*/ |
44
|
|
|
private $errorCode; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @var string|null |
48
|
|
|
*/ |
49
|
|
|
private $errorMessage; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @var string |
53
|
|
|
*/ |
54
|
|
|
private $sql; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @var array |
58
|
|
|
*/ |
59
|
|
|
private $options = [ |
60
|
|
|
'fetchMode' => null, |
61
|
|
|
'fetchColumn' => 0, |
62
|
|
|
'fetchClass' => 'array', |
63
|
|
|
'fetchClassCtorArgs' => null, |
64
|
|
|
]; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Used for the {@see PDO::FETCH_BOUND} |
68
|
|
|
* |
69
|
|
|
* @var array |
70
|
|
|
*/ |
71
|
|
|
private $columnBinding = []; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* @var CollectionInterface|null |
75
|
|
|
*/ |
76
|
|
|
private $collection; |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* @var PDOInterface |
80
|
|
|
*/ |
81
|
|
|
private $pdo; |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @var Closure |
85
|
|
|
*/ |
86
|
|
|
private $request; |
87
|
|
|
|
88
|
|
|
private $namedToPositionalMap = array(); |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* @param PDOInterface $pdo |
92
|
|
|
* @param Closure $request |
93
|
|
|
* @param string $sql |
94
|
|
|
* @param array $options |
95
|
|
|
*/ |
96
|
1 |
|
public function __construct(PDOInterface $pdo, Closure $request, $sql, array $options) |
97
|
|
|
{ |
98
|
1 |
|
$this->sql = $this->replaceNamedParametersWithPositionals($sql); |
99
|
1 |
|
$this->pdo = $pdo; |
100
|
1 |
|
$this->options = array_merge($this->options, $options); |
101
|
1 |
|
$this->request = $request; |
102
|
1 |
|
} |
103
|
|
|
|
104
|
67 |
|
private function replaceNamedParametersWithPositionals($sql) |
105
|
|
|
{ |
106
|
67 |
|
if (strpos($sql, ':') === false) { |
107
|
67 |
|
return $sql; |
108
|
|
|
} |
109
|
1 |
|
$pattern = '/:((?:[\w|\d|_](?=([^\'\\\]*(\\\.|\'([^\'\\\]*\\\.)*[^\'\\\]*\'))*[^\']*$))*)/'; |
110
|
|
|
|
111
|
1 |
|
$idx = 0; |
112
|
|
|
$callback = function ($matches) use (&$idx) { |
113
|
1 |
|
$value = $matches[1]; |
114
|
1 |
|
if (empty($value)) { |
115
|
1 |
|
return $matches[0]; |
116
|
|
|
} |
117
|
1 |
|
$this->namedToPositionalMap[$value] = $idx; |
118
|
1 |
|
$idx++; |
119
|
1 |
|
return '?'; |
120
|
1 |
|
}; |
121
|
|
|
|
122
|
1 |
|
return preg_replace_callback($pattern, $callback, $sql); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Determines if the statement has been executed |
127
|
|
|
* |
128
|
|
|
* @internal |
129
|
|
|
* |
130
|
|
|
* @return bool |
131
|
|
|
*/ |
132
|
30 |
|
private function hasExecuted() |
133
|
|
|
{ |
134
|
30 |
|
return ($this->collection !== null || $this->errorCode !== null); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* Internal pointer to mark the state of the current query |
139
|
|
|
* |
140
|
|
|
* @internal |
141
|
|
|
* |
142
|
|
|
* @return bool |
143
|
|
|
*/ |
144
|
29 |
|
private function isSuccessful() |
145
|
|
|
{ |
146
|
29 |
|
if (!$this->hasExecuted()) { |
147
|
|
|
// @codeCoverageIgnoreStart |
148
|
|
|
throw new Exception\LogicException('The statement has not been executed yet'); |
149
|
|
|
// @codeCoverageIgnoreEnd |
150
|
|
|
} |
151
|
|
|
|
152
|
29 |
|
return $this->collection !== null; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Get the fetch style to be used |
157
|
|
|
* |
158
|
|
|
* @internal |
159
|
|
|
* |
160
|
|
|
* @return int |
161
|
|
|
*/ |
162
|
2 |
|
private function getFetchStyle() |
163
|
|
|
{ |
164
|
2 |
|
return $this->options['fetchMode'] ?: $this->pdo->getAttribute(PDO::ATTR_DEFAULT_FETCH_MODE); |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* Update all the bound column references |
169
|
|
|
* |
170
|
|
|
* @internal |
171
|
|
|
* |
172
|
|
|
* @param array $row |
173
|
|
|
* |
174
|
|
|
* @return void |
175
|
|
|
*/ |
176
|
1 |
|
private function updateBoundColumns(array $row) |
177
|
|
|
{ |
178
|
1 |
|
foreach ($this->columnBinding as $column => &$metadata) { |
179
|
|
|
|
180
|
1 |
|
$index = $this->collection->getColumnIndex($column); |
181
|
1 |
|
if ($index === null) { |
182
|
|
|
// todo: I would like to throw an exception and tell someone they screwed up |
183
|
|
|
// but i think that would violate the PDO api |
184
|
|
|
continue; |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
// Update by reference |
188
|
1 |
|
$value = $this->typedValue($row[$index], $metadata['type']); |
189
|
1 |
|
$metadata['ref'] = $value; |
190
|
1 |
|
} |
191
|
|
|
|
192
|
1 |
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* {@inheritDoc} |
196
|
|
|
*/ |
197
|
2 |
|
public function execute($input_parameters = null) |
198
|
|
|
{ |
199
|
2 |
|
$input_parameters_array = ArrayUtils::toArray($input_parameters); |
200
|
2 |
|
$zero_based = isset($input_parameters_array[0]); |
201
|
2 |
|
foreach ($input_parameters_array as $parameter => $value) { |
202
|
1 |
|
if (is_int($parameter) && $zero_based) { |
203
|
1 |
|
$parameter++; |
204
|
1 |
|
} |
205
|
1 |
|
$this->bindValue($parameter, $value); |
206
|
2 |
|
} |
207
|
|
|
|
208
|
2 |
|
$result = $this->request->__invoke($this, $this->sql, $this->parameters); |
209
|
|
|
|
210
|
2 |
|
if (is_array($result)) { |
211
|
1 |
|
$this->errorCode = $result['code']; |
212
|
1 |
|
$this->errorMessage = $result['message']; |
213
|
|
|
|
214
|
1 |
|
return false; |
215
|
|
|
} |
216
|
|
|
|
217
|
1 |
|
$this->collection = $result; |
218
|
1 |
|
return true; |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* {@inheritDoc} |
223
|
|
|
*/ |
224
|
8 |
|
public function fetch($fetch_style = null, $cursor_orientation = PDO::FETCH_ORI_NEXT, $cursor_offset = 0) |
225
|
|
|
{ |
226
|
8 |
|
if (!$this->hasExecuted()) { |
227
|
8 |
|
$this->execute(); |
228
|
8 |
|
} |
229
|
|
|
|
230
|
8 |
|
if (!$this->isSuccessful()) { |
231
|
1 |
|
return false; |
232
|
|
|
} |
233
|
|
|
|
234
|
7 |
|
if (!$this->collection->valid()) { |
235
|
1 |
|
return false; |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
// Get the current row |
239
|
6 |
|
$row = $this->collection->current(); |
240
|
|
|
|
241
|
|
|
// Traverse |
242
|
6 |
|
$this->collection->next(); |
243
|
|
|
|
244
|
6 |
|
$fetch_style = $fetch_style ?: $this->getFetchStyle(); |
245
|
|
|
|
246
|
|
|
switch ($fetch_style) |
247
|
|
|
{ |
248
|
6 |
|
case PDO::FETCH_NAMED: |
249
|
6 |
|
case PDO::FETCH_ASSOC: |
250
|
2 |
|
return array_combine($this->collection->getColumns(false), $row); |
251
|
|
|
|
252
|
4 |
|
case PDO::FETCH_BOTH: |
253
|
1 |
|
return array_merge($row, array_combine($this->collection->getColumns(false), $row)); |
254
|
|
|
|
255
|
3 |
|
case PDO::FETCH_BOUND: |
256
|
1 |
|
$this->updateBoundColumns($row); |
257
|
1 |
|
return true; |
258
|
|
|
|
259
|
2 |
|
case PDO::FETCH_NUM: |
260
|
1 |
|
return $row; |
261
|
|
|
|
262
|
1 |
|
default: |
263
|
1 |
|
throw new Exception\UnsupportedException('Unsupported fetch style'); |
264
|
1 |
|
} |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
/** |
268
|
|
|
* {@inheritDoc} |
269
|
|
|
*/ |
270
|
2 |
|
public function bindParam( |
271
|
|
|
$parameter, |
272
|
|
|
& $variable, |
273
|
|
|
$data_type = PDO::PARAM_STR, |
274
|
|
|
$length = null, |
275
|
|
|
$driver_options = null |
276
|
|
|
) { |
277
|
2 |
|
if (is_numeric($parameter)) { |
278
|
2 |
|
if ($parameter == 0) { |
279
|
1 |
|
throw new Exception\UnsupportedException("0-based parameter binding not supported, use 1-based"); |
280
|
|
|
} |
281
|
1 |
|
$parameter--; |
282
|
1 |
|
} else { |
283
|
|
|
$namedParameterKey = substr($parameter, 0, 1) === ':' ? substr($parameter, 1) : $parameter; |
284
|
|
|
if (array_key_exists($namedParameterKey, $this->namedToPositionalMap)) { |
285
|
|
|
$parameter = $this->namedToPositionalMap[$namedParameterKey]; |
286
|
|
|
} else { |
287
|
|
|
throw new Exception\OutOfBoundsException( |
288
|
|
|
sprintf('The named parameter "%s" does not exist', $parameter) |
289
|
|
|
); |
290
|
|
|
} |
291
|
|
|
} |
292
|
1 |
|
$this->parameters[$parameter] = &$variable; |
293
|
1 |
|
} |
294
|
|
|
|
295
|
|
|
/** |
296
|
|
|
* {@inheritDoc} |
297
|
|
|
*/ |
298
|
1 |
|
public function bindColumn($column, &$param, $type = null, $maxlen = null, $driverdata = null) |
299
|
|
|
{ |
300
|
1 |
|
$type = $type ?: PDO::PARAM_STR; |
301
|
|
|
|
302
|
1 |
|
$this->columnBinding[$column] = [ |
303
|
1 |
|
'ref' => &$param, |
304
|
1 |
|
'type' => $type, |
305
|
1 |
|
'maxlen' => $maxlen, |
306
|
|
|
'driverdata' => $driverdata |
307
|
1 |
|
]; |
308
|
1 |
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* {@inheritDoc} |
312
|
|
|
*/ |
313
|
4 |
|
public function bindValue($parameter, $value, $data_type = PDO::PARAM_STR) |
314
|
|
|
{ |
315
|
4 |
|
$value = $this->typedValue($value, $data_type); |
316
|
4 |
|
$this->bindParam($parameter, $value, $data_type); |
317
|
4 |
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* {@inheritDoc} |
321
|
|
|
*/ |
322
|
2 |
|
public function rowCount() |
323
|
|
|
{ |
324
|
2 |
|
if (!$this->hasExecuted()) { |
325
|
2 |
|
$this->execute(); |
326
|
2 |
|
} |
327
|
|
|
|
328
|
2 |
|
if (!$this->isSuccessful()) { |
329
|
1 |
|
return 0; |
330
|
|
|
} |
331
|
|
|
|
332
|
1 |
|
return $this->collection->count(); |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
/** |
336
|
|
|
* {@inheritDoc} |
337
|
|
|
*/ |
338
|
5 |
|
public function fetchColumn($column_number = 0) |
339
|
|
|
{ |
340
|
5 |
|
if (!is_int($column_number)) { |
341
|
1 |
|
throw new Exception\InvalidArgumentException('column_number must be a valid integer'); |
342
|
|
|
} |
343
|
|
|
|
344
|
4 |
|
if (!$this->hasExecuted()) { |
345
|
4 |
|
$this->execute(); |
346
|
4 |
|
} |
347
|
|
|
|
348
|
4 |
|
if (!$this->isSuccessful()) { |
349
|
1 |
|
return false; |
|
|
|
|
350
|
|
|
} |
351
|
|
|
|
352
|
3 |
|
if (!$this->collection->valid()) { |
353
|
2 |
|
return false; |
|
|
|
|
354
|
|
|
} |
355
|
|
|
|
356
|
2 |
|
$row = $this->collection->current(); |
357
|
2 |
|
$this->collection->next(); |
358
|
|
|
|
359
|
2 |
|
if ($column_number >= count($row)) { |
360
|
1 |
|
throw new Exception\OutOfBoundsException( |
361
|
1 |
|
sprintf('The column "%d" with the zero-based does not exist', $column_number) |
362
|
1 |
|
); |
363
|
|
|
} |
364
|
|
|
|
365
|
1 |
|
return $row[$column_number]; |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
/** |
369
|
|
|
* {@inheritDoc} |
370
|
|
|
*/ |
371
|
12 |
|
public function fetchAll($fetch_style = null, $fetch_argument = null, $ctor_args = []) |
372
|
|
|
{ |
373
|
12 |
|
if (!$this->hasExecuted()) { |
374
|
12 |
|
$this->execute(); |
375
|
12 |
|
} |
376
|
|
|
|
377
|
12 |
|
if (!$this->isSuccessful()) { |
378
|
1 |
|
return false; |
|
|
|
|
379
|
|
|
} |
380
|
|
|
|
381
|
11 |
|
$fetch_style = $fetch_style ?: $this->getFetchStyle(); |
382
|
|
|
|
383
|
|
|
switch ($fetch_style) |
384
|
|
|
{ |
385
|
11 |
|
case PDO::FETCH_NUM: |
386
|
1 |
|
return $this->collection->getRows(); |
387
|
|
|
|
388
|
10 |
|
case PDO::FETCH_NAMED: |
389
|
10 |
|
case PDO::FETCH_ASSOC: |
390
|
2 |
|
$columns = array_flip($this->collection->getColumns()); |
391
|
|
|
|
392
|
|
|
return $this->collection->map(function (array $row) use ($columns) { |
393
|
2 |
|
return array_combine($columns, $row); |
394
|
2 |
|
}); |
395
|
|
|
|
396
|
8 |
|
case PDO::FETCH_BOTH: |
397
|
2 |
|
$columns = array_flip($this->collection->getColumns()); |
398
|
|
|
|
399
|
|
|
return $this->collection->map(function (array $row) use ($columns) { |
400
|
2 |
|
return array_merge($row, array_combine($columns, $row)); |
401
|
2 |
|
}); |
402
|
|
|
|
403
|
6 |
|
case PDO::FETCH_FUNC: |
404
|
2 |
|
if (!is_callable($fetch_argument)) { |
405
|
1 |
|
throw new Exception\InvalidArgumentException('Second argument must be callable'); |
406
|
|
|
} |
407
|
|
|
|
408
|
|
|
return $this->collection->map(function (array $row) use ($fetch_argument) { |
409
|
1 |
|
return call_user_func_array($fetch_argument, $row); |
410
|
1 |
|
}); |
411
|
|
|
|
412
|
4 |
|
case PDO::FETCH_COLUMN: |
413
|
3 |
|
$columnIndex = $fetch_argument ?: $this->options['fetchColumn']; |
414
|
|
|
|
415
|
3 |
|
if (!is_int($columnIndex)) { |
416
|
1 |
|
throw new Exception\InvalidArgumentException('Second argument must be a integer'); |
417
|
|
|
} |
418
|
|
|
|
419
|
2 |
|
$columns = $this->collection->getColumns(false); |
420
|
2 |
|
if (!isset($columns[$columnIndex])) { |
421
|
1 |
|
throw new Exception\OutOfBoundsException( |
422
|
1 |
|
sprintf('Column with the index %d does not exist.', $columnIndex) |
423
|
1 |
|
); |
424
|
|
|
} |
425
|
|
|
|
426
|
1 |
|
return $this->collection->map(function (array $row) use ($columnIndex) { |
427
|
1 |
|
return $row[$columnIndex]; |
428
|
1 |
|
}); |
429
|
|
|
|
430
|
1 |
|
default: |
431
|
1 |
|
throw new Exception\UnsupportedException('Unsupported fetch style'); |
432
|
1 |
|
} |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
/** |
436
|
|
|
* {@inheritDoc} |
437
|
|
|
*/ |
438
|
1 |
|
public function fetchObject($class_name = null, $ctor_args = null) |
439
|
|
|
{ |
440
|
1 |
|
throw new Exception\UnsupportedException; |
441
|
|
|
} |
442
|
|
|
|
443
|
|
|
/** |
444
|
|
|
* {@inheritDoc} |
445
|
|
|
*/ |
446
|
1 |
|
public function errorCode() |
447
|
|
|
{ |
448
|
1 |
|
return $this->errorCode; |
449
|
|
|
} |
450
|
|
|
|
451
|
|
|
/** |
452
|
|
|
* {@inheritDoc} |
453
|
|
|
*/ |
454
|
2 |
|
public function errorInfo() |
455
|
|
|
{ |
456
|
2 |
|
if ($this->errorCode === null) { |
457
|
2 |
|
return null; |
458
|
|
|
} |
459
|
|
|
|
460
|
2 |
|
switch ($this->errorCode) |
461
|
|
|
{ |
462
|
2 |
|
case CrateConst::ERR_INVALID_SQL: |
463
|
1 |
|
$ansiErrorCode = 42000; |
464
|
1 |
|
break; |
465
|
|
|
|
466
|
1 |
|
default: |
467
|
1 |
|
$ansiErrorCode = 'Not available'; |
468
|
1 |
|
break; |
469
|
2 |
|
} |
470
|
|
|
|
471
|
|
|
return [ |
472
|
2 |
|
$ansiErrorCode, |
473
|
2 |
|
$this->errorCode, |
474
|
2 |
|
$this->errorMessage |
475
|
2 |
|
]; |
476
|
|
|
} |
477
|
|
|
|
478
|
|
|
/** |
479
|
|
|
* {@inheritDoc} |
480
|
|
|
*/ |
481
|
1 |
|
public function setAttribute($attribute, $value) |
482
|
|
|
{ |
483
|
1 |
|
throw new Exception\UnsupportedException('This driver doesn\'t support setting attributes'); |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
/** |
487
|
|
|
* {@inheritDoc} |
488
|
|
|
*/ |
489
|
1 |
|
public function getAttribute($attribute) |
490
|
|
|
{ |
491
|
1 |
|
throw new Exception\UnsupportedException('This driver doesn\'t support getting attributes'); |
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
/** |
495
|
|
|
* {@inheritDoc} |
496
|
|
|
*/ |
497
|
1 |
|
public function columnCount() |
498
|
|
|
{ |
499
|
1 |
|
if (!$this->hasExecuted()) { |
500
|
1 |
|
$this->execute(); |
501
|
1 |
|
} |
502
|
|
|
|
503
|
1 |
|
return count($this->collection->getColumns()); |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
/** |
507
|
|
|
* {@inheritDoc} |
508
|
|
|
*/ |
509
|
1 |
|
public function getColumnMeta($column) |
510
|
|
|
{ |
511
|
1 |
|
throw new Exception\UnsupportedException; |
512
|
|
|
} |
513
|
|
|
|
514
|
|
|
/** |
515
|
|
|
* {@inheritDoc} |
516
|
|
|
*/ |
517
|
14 |
|
public function setFetchMode($mode, $params = null) |
518
|
|
|
{ |
519
|
14 |
|
$args = func_get_args(); |
520
|
14 |
|
$argCount = count($args); |
521
|
|
|
|
522
|
|
|
switch ($mode) |
523
|
|
|
{ |
524
|
14 |
|
case PDO::FETCH_COLUMN: |
525
|
3 |
|
if ($argCount != 2) { |
526
|
1 |
|
throw new Exception\InvalidArgumentException('fetch mode requires the colno argument'); |
527
|
|
|
} |
528
|
|
|
|
529
|
2 |
|
if (!is_int($params)) { |
530
|
1 |
|
throw new Exception\InvalidArgumentException('colno must be an integer'); |
531
|
|
|
} |
532
|
|
|
|
533
|
1 |
|
$this->options['fetchMode'] = $mode; |
534
|
1 |
|
$this->options['fetchColumn'] = $params; |
535
|
1 |
|
break; |
536
|
|
|
|
537
|
11 |
|
case PDO::FETCH_ASSOC: |
538
|
11 |
|
case PDO::FETCH_NUM: |
539
|
11 |
|
case PDO::FETCH_BOTH: |
540
|
11 |
|
case PDO::FETCH_BOUND: |
541
|
11 |
|
case PDO::FETCH_NAMED: |
542
|
10 |
|
if ($params !== null) { |
543
|
5 |
|
throw new Exception\InvalidArgumentException('fetch mode doesn\'t allow any extra arguments'); |
544
|
|
|
} |
545
|
|
|
|
546
|
5 |
|
$this->options['fetchMode'] = $mode; |
547
|
5 |
|
break; |
548
|
|
|
|
549
|
1 |
|
default: |
550
|
1 |
|
throw new Exception\UnsupportedException('Invalid fetch mode specified'); |
551
|
1 |
|
} |
552
|
6 |
|
} |
553
|
|
|
|
554
|
|
|
/** |
555
|
|
|
* {@inheritDoc} |
556
|
|
|
*/ |
557
|
2 |
|
public function nextRowset() |
558
|
|
|
{ |
559
|
2 |
|
if (!$this->hasExecuted()) { |
560
|
2 |
|
$this->execute(); |
561
|
2 |
|
} |
562
|
|
|
|
563
|
2 |
|
if (!$this->isSuccessful()) { |
564
|
1 |
|
return false; |
565
|
|
|
} |
566
|
|
|
|
567
|
1 |
|
$this->collection->next(); |
568
|
1 |
|
return $this->collection->valid(); |
569
|
|
|
} |
570
|
|
|
|
571
|
|
|
/** |
572
|
|
|
* {@inheritDoc} |
573
|
|
|
*/ |
574
|
1 |
|
public function closeCursor() |
575
|
|
|
{ |
576
|
1 |
|
$this->errorCode = 0; |
|
|
|
|
577
|
1 |
|
$this->collection = null; |
578
|
1 |
|
return true; |
579
|
|
|
} |
580
|
|
|
|
581
|
|
|
/** |
582
|
|
|
* {@inheritDoc} |
583
|
|
|
*/ |
584
|
1 |
|
public function debugDumpParams() |
585
|
|
|
{ |
586
|
1 |
|
throw new Exception\UnsupportedException('Not supported, use var_dump($stmt) instead'); |
587
|
|
|
} |
588
|
|
|
|
589
|
|
|
/** |
590
|
|
|
* {@Inheritdoc} |
591
|
|
|
*/ |
592
|
1 |
|
public function getIterator() |
593
|
|
|
{ |
594
|
1 |
|
return new ArrayIterator($this->fetchAll()); |
595
|
|
|
} |
596
|
|
|
|
597
|
8 |
|
private function typedValue($value, $data_type) |
598
|
|
|
{ |
599
|
|
|
switch ($data_type) |
600
|
|
|
{ |
601
|
8 |
|
case PDO::PARAM_FLOAT: |
602
|
8 |
|
case PDO::PARAM_DOUBLE: |
603
|
1 |
|
return (float) $value; |
604
|
|
|
|
605
|
8 |
|
case PDO::PARAM_INT: |
606
|
8 |
|
case PDO::PARAM_LONG: |
607
|
3 |
|
return (int) $value; |
608
|
|
|
|
609
|
7 |
|
case PDO::PARAM_NULL: |
610
|
2 |
|
return null; |
611
|
|
|
|
612
|
6 |
|
case PDO::PARAM_BOOL: |
613
|
2 |
|
return (bool) $value; |
614
|
|
|
|
615
|
5 |
|
case PDO::PARAM_STR: |
616
|
5 |
|
case PDO::PARAM_IP: |
617
|
4 |
|
return (string) $value; |
618
|
|
|
|
619
|
2 |
|
case PDO::PARAM_OBJECT: |
620
|
2 |
|
case PDO::PARAM_ARRAY: |
621
|
1 |
|
return (array) $value; |
622
|
|
|
|
623
|
2 |
|
case PDO::PARAM_TIMESTAMP: |
624
|
1 |
|
if (is_numeric($value)) { |
625
|
1 |
|
return (int) $value; |
626
|
|
|
} |
627
|
1 |
|
return (string) $value; |
628
|
|
|
|
629
|
1 |
|
default: |
630
|
1 |
|
throw new Exception\InvalidArgumentException(sprintf('Parameter type %s not supported', $data_type)); |
631
|
1 |
|
} |
632
|
|
|
|
633
|
|
|
} |
634
|
|
|
} |
635
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.