Total Complexity | 103 |
Total Lines | 746 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like Command often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Command, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
85 | abstract class Command implements CommandInterface |
||
86 | { |
||
87 | use LoggerAwareTrait; |
||
88 | use ProfilerAwareTrait; |
||
89 | |||
90 | protected ?string $isolationLevel = null; |
||
91 | protected array $params = []; |
||
92 | protected ?PDOStatement $pdoStatement = null; |
||
93 | protected array $pendingParams = []; |
||
94 | protected ?string $refreshTableName = null; |
||
95 | /** @var callable|null */ |
||
96 | protected $retryHandler = null; |
||
97 | private int $fetchMode = PDO::FETCH_ASSOC; |
||
98 | private ?int $queryCacheDuration = null; |
||
99 | private ?string $sql = null; |
||
100 | private ?Dependency $queryCacheDependency = null; |
||
101 | |||
102 | public function __construct(private QueryCache $queryCache) |
||
103 | { |
||
104 | } |
||
105 | |||
106 | /** |
||
107 | * Returns the cache key for the query. |
||
108 | * |
||
109 | * @param string $method method of PDOStatement to be called. |
||
110 | * @param int|null $fetchMode the result fetch mode. |
||
111 | * Please refer to [PHP manual](https://secure.php.net/manual/en/function.PDOStatement-setFetchMode.php) for valid |
||
112 | * fetch modes. |
||
113 | * @param string $rawSql the raw SQL with parameter values inserted into the corresponding placeholders. |
||
114 | * |
||
115 | * @throws JsonException |
||
116 | * |
||
117 | * @return array the cache key. |
||
118 | */ |
||
119 | abstract protected function getCacheKey(string $method, ?int $fetchMode, string $rawSql): array; |
||
120 | |||
121 | /** |
||
122 | * Executes a prepared statement. |
||
123 | * |
||
124 | * It's a wrapper around {@see PDOStatement::execute()} to support transactions and retry handlers. |
||
125 | * |
||
126 | * @param string|null $rawSql the rawSql if it has been created. |
||
127 | * |
||
128 | * @throws Exception|Throwable |
||
129 | */ |
||
130 | abstract protected function internalExecute(?string $rawSql): void; |
||
131 | |||
132 | public function addCheck(string $name, string $table, string $expression): self |
||
133 | { |
||
134 | $sql = $this->queryBuilder()->addCheck($name, $table, $expression); |
||
135 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
136 | } |
||
137 | |||
138 | public function addColumn(string $table, string $column, string $type): self |
||
139 | { |
||
140 | $sql = $this->queryBuilder()->addColumn($table, $column, $type); |
||
141 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
142 | } |
||
143 | |||
144 | /** |
||
145 | * @throws \Exception |
||
146 | */ |
||
147 | public function addCommentOnColumn(string $table, string $column, string $comment): self |
||
148 | { |
||
149 | $sql = $this->queryBuilder()->addCommentOnColumn($table, $column, $comment); |
||
150 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
151 | } |
||
152 | |||
153 | /** |
||
154 | * @throws \Exception |
||
155 | */ |
||
156 | public function addCommentOnTable(string $table, string $comment): self |
||
157 | { |
||
158 | $sql = $this->queryBuilder()->addCommentOnTable($table, $comment); |
||
159 | return $this->setSql($sql); |
||
160 | } |
||
161 | |||
162 | /** |
||
163 | * @throws Exception|NotSupportedException |
||
164 | */ |
||
165 | public function addDefaultValue(string $name, string $table, string $column, mixed $value): self |
||
166 | { |
||
167 | $sql = $this->queryBuilder()->addDefaultValue($name, $table, $column, $value); |
||
168 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
169 | } |
||
170 | |||
171 | /** |
||
172 | * @throws Exception|InvalidArgumentException |
||
173 | */ |
||
174 | public function addForeignKey( |
||
175 | string $name, |
||
176 | string $table, |
||
177 | array|string $columns, |
||
178 | string $refTable, |
||
179 | array|string $refColumns, |
||
180 | ?string $delete = null, |
||
181 | ?string $update = null |
||
182 | ): self { |
||
183 | $sql = $this->queryBuilder()->addForeignKey( |
||
184 | $name, |
||
185 | $table, |
||
186 | $columns, |
||
187 | $refTable, |
||
188 | $refColumns, |
||
189 | $delete, |
||
190 | $update |
||
191 | ); |
||
192 | |||
193 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
194 | } |
||
195 | |||
196 | public function addPrimaryKey(string $name, string $table, array|string $columns): self |
||
197 | { |
||
198 | $sql = $this->queryBuilder()->addPrimaryKey($name, $table, $columns); |
||
199 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
200 | } |
||
201 | |||
202 | public function addUnique(string $name, string $table, array|string $columns): self |
||
203 | { |
||
204 | $sql = $this->queryBuilder()->addUnique($name, $table, $columns); |
||
205 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
206 | } |
||
207 | |||
208 | public function alterColumn(string $table, string $column, string $type): self |
||
209 | { |
||
210 | $sql = $this->queryBuilder()->alterColumn($table, $column, $type); |
||
211 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
212 | } |
||
213 | |||
214 | /** |
||
215 | * @throws Exception|InvalidArgumentException |
||
216 | */ |
||
217 | public function batchInsert(string $table, array $columns, iterable $rows): self |
||
218 | { |
||
219 | $table = $this->queryBuilder()->quoter()->quoteSql($table); |
||
220 | $columns = array_map(fn ($column) => $this->queryBuilder()->quoter()->quoteSql($column), $columns); |
||
221 | $params = []; |
||
222 | $sql = $this->queryBuilder()->batchInsert($table, $columns, $rows, $params); |
||
223 | |||
224 | $this->setRawSql($sql); |
||
225 | $this->bindValues($params); |
||
226 | |||
227 | return $this; |
||
228 | } |
||
229 | |||
230 | public function bindParam(int|string $name, mixed &$value, ?int $dataType = null, ?int $length = null, mixed $driverOptions = null): self |
||
231 | { |
||
232 | $this->prepare(); |
||
233 | |||
234 | if ($dataType === null) { |
||
235 | $dataType = $this->queryBuilder()->schema()->getPdoType($value); |
||
236 | } |
||
237 | |||
238 | if ($length === null) { |
||
239 | $this->pdoStatement->bindParam($name, $value, $dataType); |
||
|
|||
240 | } elseif ($driverOptions === null) { |
||
241 | $this->pdoStatement->bindParam($name, $value, $dataType, $length); |
||
242 | } else { |
||
243 | $this->pdoStatement->bindParam($name, $value, $dataType, $length, $driverOptions); |
||
244 | } |
||
245 | |||
246 | $this->params[$name] = &$value; |
||
247 | |||
248 | return $this; |
||
249 | } |
||
250 | |||
251 | public function bindValue(int|string $name, mixed $value, ?int $dataType = null): self |
||
252 | { |
||
253 | if ($dataType === null) { |
||
254 | $dataType = $this->queryBuilder()->schema()->getPdoType($value); |
||
255 | } |
||
256 | |||
257 | $this->pendingParams[$name] = [$value, $dataType]; |
||
258 | |||
259 | $this->params[$name] = $value; |
||
260 | |||
261 | return $this; |
||
262 | } |
||
263 | |||
264 | public function bindValues(array $values): self |
||
265 | { |
||
266 | if (empty($values)) { |
||
267 | return $this; |
||
268 | } |
||
269 | |||
270 | foreach ($values as $name => $value) { |
||
271 | if (is_array($value)) { // TODO: Drop in Yii 2.1 |
||
272 | $this->pendingParams[$name] = $value; |
||
273 | $this->params[$name] = $value[0]; |
||
274 | } elseif ($value instanceof PdoValue) { |
||
275 | $this->pendingParams[$name] = [$value->getValue(), $value->getType()]; |
||
276 | $this->params[$name] = $value->getValue(); |
||
277 | } else { |
||
278 | $type = $this->queryBuilder()->schema()->getPdoType($value); |
||
279 | |||
280 | $this->pendingParams[$name] = [$value, $type]; |
||
281 | $this->params[$name] = $value; |
||
282 | } |
||
283 | } |
||
284 | |||
285 | return $this; |
||
286 | } |
||
287 | |||
288 | public function cache(?int $duration = null, Dependency $dependency = null): self |
||
289 | { |
||
290 | $this->queryCacheDuration = $duration ?? $this->queryCache->getDuration(); |
||
291 | $this->queryCacheDependency = $dependency; |
||
292 | return $this; |
||
293 | } |
||
294 | |||
295 | public function cancel(): void |
||
296 | { |
||
297 | $this->pdoStatement = null; |
||
298 | } |
||
299 | |||
300 | /** |
||
301 | * @throws Exception|NotSupportedException |
||
302 | */ |
||
303 | public function checkIntegrity(string $schema, string $table, bool $check = true): self |
||
304 | { |
||
305 | $sql = $this->queryBuilder()->checkIntegrity($schema, $table, $check); |
||
306 | return $this->setSql($sql); |
||
307 | } |
||
308 | |||
309 | /** |
||
310 | * @throws Exception|InvalidArgumentException |
||
311 | */ |
||
312 | public function createIndex(string $name, string $table, array|string $columns, bool $unique = false): self |
||
313 | { |
||
314 | $sql = $this->queryBuilder()->createIndex($name, $table, $columns, $unique); |
||
315 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
316 | } |
||
317 | |||
318 | public function createTable(string $table, array $columns, ?string $options = null): self |
||
319 | { |
||
320 | $sql = $this->queryBuilder()->createTable($table, $columns, $options); |
||
321 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
322 | } |
||
323 | |||
324 | /** |
||
325 | * @throws Exception|InvalidConfigException|NotSupportedException |
||
326 | */ |
||
327 | public function createView(string $viewName, QueryInterface|string $subquery): self |
||
328 | { |
||
329 | $sql = $this->queryBuilder()->createView($viewName, $subquery); |
||
330 | return $this->setSql($sql)->requireTableSchemaRefresh($viewName); |
||
331 | } |
||
332 | |||
333 | /** |
||
334 | * @throws Exception|InvalidArgumentException |
||
335 | */ |
||
336 | public function delete(string $table, array|string $condition = '', array $params = []): self |
||
337 | { |
||
338 | $sql = $this->queryBuilder()->delete($table, $condition, $params); |
||
339 | return $this->setSql($sql)->bindValues($params); |
||
340 | } |
||
341 | |||
342 | public function dropCheck(string $name, string $table): self |
||
343 | { |
||
344 | $sql = $this->queryBuilder()->dropCheck($name, $table); |
||
345 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
346 | } |
||
347 | |||
348 | public function dropColumn(string $table, string $column): self |
||
349 | { |
||
350 | $sql = $this->queryBuilder()->dropColumn($table, $column); |
||
351 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
352 | } |
||
353 | |||
354 | public function dropCommentFromColumn(string $table, string $column): self |
||
355 | { |
||
356 | $sql = $this->queryBuilder()->dropCommentFromColumn($table, $column); |
||
357 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
358 | } |
||
359 | |||
360 | public function dropCommentFromTable(string $table): self |
||
361 | { |
||
362 | $sql = $this->queryBuilder()->dropCommentFromTable($table); |
||
363 | return $this->setSql($sql); |
||
364 | } |
||
365 | |||
366 | /** |
||
367 | * @throws Exception|NotSupportedException |
||
368 | */ |
||
369 | public function dropDefaultValue(string $name, string $table): self |
||
370 | { |
||
371 | $sql = $this->queryBuilder()->dropDefaultValue($name, $table); |
||
372 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
373 | } |
||
374 | |||
375 | public function dropForeignKey(string $name, string $table): self |
||
376 | { |
||
377 | $sql = $this->queryBuilder()->dropForeignKey($name, $table); |
||
378 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
379 | } |
||
380 | |||
381 | public function dropIndex(string $name, string $table): self |
||
382 | { |
||
383 | $sql = $this->queryBuilder()->dropIndex($name, $table); |
||
384 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
385 | } |
||
386 | |||
387 | public function dropPrimaryKey(string $name, string $table): self |
||
388 | { |
||
389 | $sql = $this->queryBuilder()->dropPrimaryKey($name, $table); |
||
390 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
391 | } |
||
392 | |||
393 | public function dropTable(string $table): self |
||
394 | { |
||
395 | $sql = $this->queryBuilder()->dropTable($table); |
||
396 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
397 | } |
||
398 | |||
399 | public function dropUnique(string $name, string $table): self |
||
400 | { |
||
401 | $sql = $this->queryBuilder()->dropUnique($name, $table); |
||
402 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
403 | } |
||
404 | |||
405 | public function dropView(string $viewName): self |
||
406 | { |
||
407 | $sql = $this->queryBuilder()->dropView($viewName); |
||
408 | return $this->setSql($sql)->requireTableSchemaRefresh($viewName); |
||
409 | } |
||
410 | |||
411 | /** |
||
412 | * Executes the SQL statement. |
||
413 | * |
||
414 | * This method should only be used for executing non-query SQL statement, such as `INSERT`, `DELETE`, `UPDATE` SQLs. |
||
415 | * No result set will be returned. |
||
416 | * |
||
417 | * @throws Throwable |
||
418 | * @throws Exception execution failed. |
||
419 | * |
||
420 | * @return int number of rows affected by the execution. |
||
421 | */ |
||
422 | public function execute(): int |
||
423 | { |
||
424 | $sql = $this->getSql(); |
||
425 | |||
426 | [, $rawSql] = $this->logQuery(__METHOD__); |
||
427 | |||
428 | if ($sql === '') { |
||
429 | return 0; |
||
430 | } |
||
431 | |||
432 | $this->prepare(false); |
||
433 | |||
434 | try { |
||
435 | $this->profiler?->begin((string)$rawSql, [__METHOD__]); |
||
436 | |||
437 | $this->internalExecute($rawSql); |
||
438 | $n = $this->pdoStatement->rowCount(); |
||
439 | |||
440 | $this->profiler?->end((string)$rawSql, [__METHOD__]); |
||
441 | |||
442 | $this->refreshTableSchema(); |
||
443 | |||
444 | return $n; |
||
445 | } catch (Exception $e) { |
||
446 | $this->profiler?->end((string)$rawSql, [__METHOD__]); |
||
447 | throw $e; |
||
448 | } |
||
449 | } |
||
450 | |||
451 | /** |
||
452 | * @throws Exception|NotSupportedException |
||
453 | */ |
||
454 | public function executeResetSequence(string $table, mixed $value = null): self |
||
455 | { |
||
456 | return $this->resetSequence($table, $value); |
||
457 | } |
||
458 | |||
459 | public function getFetchMode(): int |
||
460 | { |
||
461 | return $this->fetchMode; |
||
462 | } |
||
463 | |||
464 | public function getParams(): array |
||
465 | { |
||
466 | return $this->params; |
||
467 | } |
||
468 | |||
469 | public function getPdoStatement(): ?PDOStatement |
||
470 | { |
||
471 | return $this->pdoStatement; |
||
472 | } |
||
473 | |||
474 | /** |
||
475 | * @throws \Exception |
||
476 | */ |
||
477 | public function getRawSql(): string |
||
478 | { |
||
479 | if (empty($this->params)) { |
||
480 | return $this->sql; |
||
481 | } |
||
482 | |||
483 | $params = []; |
||
484 | |||
485 | foreach ($this->params as $name => $value) { |
||
486 | if (is_string($name) && strncmp(':', $name, 1)) { |
||
487 | $name = ':' . $name; |
||
488 | } |
||
489 | |||
490 | if (is_string($value)) { |
||
491 | $params[$name] = $this->queryBuilder()->quoter()->quoteValue($value); |
||
492 | } elseif (is_bool($value)) { |
||
493 | $params[$name] = ($value ? 'TRUE' : 'FALSE'); |
||
494 | } elseif ($value === null) { |
||
495 | $params[$name] = 'NULL'; |
||
496 | } elseif ((!is_object($value) && !is_resource($value)) || $value instanceof Expression) { |
||
497 | $params[$name] = $value; |
||
498 | } |
||
499 | } |
||
500 | |||
501 | if (!isset($params[1])) { |
||
502 | return strtr($this->sql, $params); |
||
503 | } |
||
504 | |||
505 | $sql = ''; |
||
506 | |||
507 | foreach (explode('?', $this->sql) as $i => $part) { |
||
508 | $sql .= ($params[$i] ?? '') . $part; |
||
509 | } |
||
510 | |||
511 | return $sql; |
||
512 | } |
||
513 | |||
514 | public function getSql(): ?string |
||
515 | { |
||
516 | return $this->sql; |
||
517 | } |
||
518 | |||
519 | /** |
||
520 | * @throws Exception|InvalidArgumentException|InvalidConfigException|NotSupportedException |
||
521 | */ |
||
522 | public function insert(string $table, QueryInterface|array $columns): self |
||
523 | { |
||
524 | $params = []; |
||
525 | $sql = $this->queryBuilder()->insert($table, $columns, $params); |
||
526 | return $this->setSql($sql)->bindValues($params); |
||
527 | } |
||
528 | |||
529 | public function noCache(): self |
||
530 | { |
||
531 | $this->queryCacheDuration = -1; |
||
532 | return $this; |
||
533 | } |
||
534 | |||
535 | public function query(): DataReader |
||
536 | { |
||
537 | return $this->queryInternal(''); |
||
538 | } |
||
539 | |||
540 | public function queryAll(?int $fetchMode = null): array |
||
541 | { |
||
542 | return $this->queryInternal('fetchAll', $fetchMode); |
||
543 | } |
||
544 | |||
545 | public function queryColumn(): array |
||
546 | { |
||
547 | return $this->queryInternal('fetchAll', PDO::FETCH_COLUMN); |
||
548 | } |
||
549 | |||
550 | public function queryOne(array|int $fetchMode = null): mixed |
||
551 | { |
||
552 | return $this->queryInternal('fetch', $fetchMode); |
||
553 | } |
||
554 | |||
555 | public function queryScalar(): bool|string|null|int |
||
556 | { |
||
557 | $result = $this->queryInternal('fetchColumn', 0); |
||
558 | |||
559 | if (is_resource($result) && get_resource_type($result) === 'stream') { |
||
560 | return stream_get_contents($result); |
||
561 | } |
||
562 | |||
563 | return $result; |
||
564 | } |
||
565 | |||
566 | public function renameColumn(string $table, string $oldName, string $newName): self |
||
567 | { |
||
568 | $sql = $this->queryBuilder()->renameColumn($table, $oldName, $newName); |
||
569 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
570 | } |
||
571 | |||
572 | public function renameTable(string $table, string $newName): self |
||
573 | { |
||
574 | $sql = $this->queryBuilder()->renameTable($table, $newName); |
||
575 | return $this->setSql($sql)->requireTableSchemaRefresh($table); |
||
576 | } |
||
577 | |||
578 | /** |
||
579 | * @throws Exception|NotSupportedException |
||
580 | */ |
||
581 | public function resetSequence(string $table, mixed $value = null): self |
||
582 | { |
||
583 | $sql = $this->queryBuilder()->resetSequence($table, $value); |
||
584 | return $this->setSql($sql); |
||
585 | } |
||
586 | |||
587 | public function setFetchMode(int $value): void |
||
588 | { |
||
589 | $this->fetchMode = $value; |
||
590 | } |
||
591 | |||
592 | public function setParams(array $value): void |
||
593 | { |
||
594 | $this->params = $value; |
||
595 | } |
||
596 | |||
597 | public function setRawSql(string $sql): self |
||
598 | { |
||
599 | if ($sql !== $this->sql) { |
||
600 | $this->cancel(); |
||
601 | $this->reset(); |
||
602 | $this->sql = $sql; |
||
603 | } |
||
604 | |||
605 | return $this; |
||
606 | } |
||
607 | |||
608 | public function setSql(string $sql): self |
||
609 | { |
||
610 | $this->cancel(); |
||
611 | $this->reset(); |
||
612 | $this->sql = $this->queryBuilder()->quoter()->quoteSql($sql); |
||
613 | |||
614 | return $this; |
||
615 | } |
||
616 | |||
617 | public function truncateTable(string $table): self |
||
618 | { |
||
619 | $sql = $this->queryBuilder()->truncateTable($table); |
||
620 | return $this->setSql($sql); |
||
621 | } |
||
622 | |||
623 | /** |
||
624 | * @throws Exception|InvalidArgumentException |
||
625 | */ |
||
626 | public function update(string $table, array $columns, array|string $condition = '', array $params = []): self |
||
627 | { |
||
628 | $sql = $this->queryBuilder()->update($table, $columns, $condition, $params); |
||
629 | return $this->setSql($sql)->bindValues($params); |
||
630 | } |
||
631 | |||
632 | /** |
||
633 | * @throws Exception|InvalidConfigException|JsonException|NotSupportedException |
||
634 | */ |
||
635 | public function upsert( |
||
636 | string $table, |
||
637 | QueryInterface|array $insertColumns, |
||
638 | bool|array $updateColumns = true, |
||
639 | array $params = [] |
||
640 | ): self { |
||
641 | $sql = $this->queryBuilder()->upsert($table, $insertColumns, $updateColumns, $params); |
||
642 | return $this->setSql($sql)->bindValues($params); |
||
643 | } |
||
644 | |||
645 | /** |
||
646 | * Binds pending parameters that were registered via {@see bindValue()} and {@see bindValues()}. |
||
647 | * |
||
648 | * Note that this method requires an active {@see pdoStatement}. |
||
649 | */ |
||
650 | protected function bindPendingParams(): void |
||
651 | { |
||
652 | foreach ($this->pendingParams as $name => $value) { |
||
653 | $this->pdoStatement->bindValue($name, $value[0], $value[1]); |
||
654 | } |
||
655 | |||
656 | $this->pendingParams = []; |
||
657 | } |
||
658 | |||
659 | /** |
||
660 | * Logs the current database query if query logging is enabled and returns the profiling token if profiling is |
||
661 | * enabled. |
||
662 | * |
||
663 | * @param string $category The log category. |
||
664 | * |
||
665 | * @throws \Exception |
||
666 | * |
||
667 | * @return array Two elements, the first is boolean of whether profiling is enabled or not. The second is |
||
668 | * the rawSql if it has been created. |
||
669 | */ |
||
670 | protected function logQuery(string $category): array |
||
682 | } |
||
683 | |||
684 | /** |
||
685 | * Performs the actual DB query of a SQL statement. |
||
686 | * |
||
687 | * @param string $method Method of PDOStatement to be called. |
||
688 | * @param array|int|null $fetchMode The result fetch mode. |
||
689 | * |
||
690 | * Please refer to [PHP manual](http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php) for valid fetch |
||
691 | * modes. If this parameter is null, the value set in {@see fetchMode} will be used. |
||
692 | * |
||
693 | * @throws Exception|Throwable If the query causes any problem. |
||
694 | * |
||
695 | * @return mixed The method execution result. |
||
696 | */ |
||
697 | protected function queryInternal(string $method, array|int $fetchMode = null): mixed |
||
698 | { |
||
699 | [, $rawSql] = $this->logQuery(__CLASS__ . '::query'); |
||
700 | |||
701 | if ($method !== '') { |
||
702 | $info = $this->queryCache->info($this->queryCacheDuration, $this->queryCacheDependency); |
||
703 | |||
704 | if (is_array($info)) { |
||
705 | /* @var $cache CacheInterface */ |
||
706 | $cache = $info[0]; |
||
707 | $rawSql = $rawSql ?: $this->getRawSql(); |
||
708 | $cacheKey = $this->getCacheKey($method, $fetchMode, $rawSql); |
||
709 | $result = $cache->getOrSet( |
||
710 | $cacheKey, |
||
711 | static fn () => null, |
||
712 | ); |
||
713 | |||
714 | if (is_array($result) && isset($result[0])) { |
||
715 | $this->logger?->log(LogLevel::DEBUG, 'Query result served from cache', [__CLASS__ . '::query']); |
||
716 | |||
717 | return $result[0]; |
||
718 | } |
||
719 | } |
||
720 | } |
||
721 | |||
722 | $this->prepare(true); |
||
723 | |||
724 | try { |
||
725 | $this->profiler?->begin((string)$rawSql, [__CLASS__ . '::query']); |
||
726 | |||
727 | $this->internalExecute($rawSql); |
||
728 | |||
729 | if ($method === '') { |
||
730 | $result = new DataReader($this); |
||
731 | } else { |
||
732 | if ($fetchMode === null) { |
||
733 | $fetchMode = $this->fetchMode; |
||
734 | } |
||
735 | |||
736 | $result = call_user_func_array([$this->pdoStatement, $method], (array) $fetchMode); |
||
737 | |||
738 | $this->pdoStatement->closeCursor(); |
||
739 | } |
||
740 | |||
741 | $this->profiler?->end((string)$rawSql, [__CLASS__ . '::query']); |
||
742 | } catch (Exception $e) { |
||
743 | $this->profiler?->end((string)$rawSql, [__CLASS__ . '::query']); |
||
744 | throw $e; |
||
745 | } |
||
746 | |||
747 | if (isset($cache, $cacheKey, $info)) { |
||
748 | $cache->getOrSet( |
||
749 | $cacheKey, |
||
750 | static fn (): array => [$result], |
||
751 | $info[1], |
||
752 | $info[2] |
||
753 | ); |
||
754 | |||
755 | $this->logger?->log(LogLevel::DEBUG, 'Saved query result in cache', [__CLASS__ . '::query']); |
||
756 | } |
||
757 | |||
758 | return $result; |
||
759 | } |
||
760 | |||
761 | /** |
||
762 | * Refreshes table schema, which was marked by {@see requireTableSchemaRefresh()}. |
||
763 | */ |
||
764 | protected function refreshTableSchema(): void |
||
768 | } |
||
769 | } |
||
770 | |||
771 | /** |
||
772 | * Marks a specified table schema to be refreshed after command execution. |
||
773 | * |
||
774 | * @param string $name Name of the table, which schema should be refreshed. |
||
775 | * |
||
776 | * @return static |
||
777 | */ |
||
778 | protected function requireTableSchemaRefresh(string $name): self |
||
779 | { |
||
780 | $this->refreshTableName = $name; |
||
781 | return $this; |
||
782 | } |
||
783 | |||
784 | /** |
||
785 | * Marks the command to be executed in transaction. |
||
786 | * |
||
787 | * @param string|null $isolationLevel The isolation level to use for this transaction. |
||
788 | * |
||
789 | * {@see TransactionInterface::begin()} for details. |
||
790 | * |
||
791 | * @return static |
||
792 | */ |
||
793 | protected function requireTransaction(?string $isolationLevel = null): self |
||
794 | { |
||
795 | $this->isolationLevel = $isolationLevel; |
||
796 | return $this; |
||
797 | } |
||
798 | |||
799 | protected function reset(): void |
||
800 | { |
||
801 | $this->sql = null; |
||
802 | $this->pendingParams = []; |
||
803 | $this->params = []; |
||
804 | $this->refreshTableName = null; |
||
805 | $this->isolationLevel = null; |
||
806 | $this->retryHandler = null; |
||
807 | } |
||
808 | |||
809 | /** |
||
810 | * Sets a callable (e.g. anonymous function) that is called when {@see Exception} is thrown when executing the |
||
811 | * command. The signature of the callable should be:. |
||
812 | * |
||
813 | * ```php |
||
814 | * function (Exceptions $e, $attempt) |
||
815 | * { |
||
816 | * // return true or false (whether to retry the command or rethrow $e) |
||
817 | * } |
||
818 | * ``` |
||
819 | * |
||
820 | * The callable will receive a database exception thrown and a current attempt (to execute the command) number |
||
821 | * starting from 1. |
||
822 | * |
||
823 | * @param callable|null $handler A PHP callback to handle database exceptions. |
||
824 | * |
||
825 | * @return static |
||
826 | */ |
||
827 | protected function setRetryHandler(?callable $handler): self |
||
831 | } |
||
832 | } |
||
833 |
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.
This is most likely a typographical error or the method has been renamed.