Passed
Pull Request — master (#464)
by Def
02:52
created

AbstractCommand::is()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Command;
6
7
use Closure;
8
use DateInterval;
9
use JsonException;
10
use Psr\Log\LoggerAwareTrait;
11
use Psr\Log\LogLevel;
12
use Throwable;
13
use Yiisoft\Cache\CacheInterface;
14
use Yiisoft\Cache\Dependency\Dependency;
15
use Yiisoft\Db\Cache\QueryCache;
16
use Yiisoft\Db\Connection\ConnectionInterface;
17
use Yiisoft\Db\Exception\Exception;
18
use Yiisoft\Db\Expression\Expression;
19
use Yiisoft\Db\Query\Data\DataReaderInterface;
20
use Yiisoft\Db\Query\QueryInterface;
21
use Yiisoft\Db\Transaction\TransactionInterface;
22
use Yiisoft\Profiler\ProfilerAwareInterface;
23
use Yiisoft\Profiler\ProfilerAwareTrait;
24
25
use function explode;
26
use function get_resource_type;
27
use function is_array;
28
use function is_bool;
29
use function is_object;
30
use function is_resource;
31
use function is_string;
32
use function stream_get_contents;
33
use function strncmp;
34
use function strtr;
35
36
/**
37
 * Command represents a SQL statement to be executed against a database.
38
 *
39
 * A command object is usually created by calling {@see ConnectionInterface::createCommand()}.
40
 *
41
 * The SQL statement it represents can be set via the {@see sql} property.
42
 *
43
 * To execute a non-query SQL (such as INSERT, DELETE, UPDATE), call {@see execute()}.
44
 * To execute a SQL statement that returns a result data set (such as SELECT), use {@see queryAll()}, {@see queryOne()},
45
 * {@see queryColumn()}, {@see queryScalar()}, or {@see query()}.
46
 *
47
 * For example,
48
 *
49
 * ```php
50
 * $users = $connectionInterface->createCommand('SELECT * FROM user')->queryAll();
51
 * ```
52
 *
53
 * Command supports SQL statement preparation and parameter binding.
54
 *
55
 * Call {@see bindValue()} to bind a value to a SQL parameter;
56
 * Call {@see bindParam()} to bind a PHP variable to a SQL parameter.
57
 *
58
 * When binding a parameter, the SQL statement is automatically prepared. You may also call {@see prepare()} explicitly
59
 * to prepare a SQL statement.
60
 *
61
 * Command also supports building SQL statements by providing methods such as {@see insert()}, {@see update()}, etc.
62
 *
63
 * For example, the following code will create and execute an INSERT SQL statement:
64
 *
65
 * ```php
66
 * $connectionInterface->createCommand()->insert(
67
 *     'user',
68
 *     ['name' => 'Sam', 'age' => 30],
69
 * )->execute();
70
 * ```
71
 *
72
 * To build SELECT SQL statements, please use {@see QueryInterface} instead.
73
 */
74
abstract class AbstractCommand implements CommandInterface, ProfilerAwareInterface
75
{
76
    use LoggerAwareTrait;
77
    use ProfilerAwareTrait;
78
79
    protected string|null $isolationLevel = null;
80
    protected string|null $refreshTableName = null;
81
    protected Closure|null $retryHandler = null;
82
    protected int|null $queryCacheDuration = null;
83
    private string $sql = '';
84
    protected Dependency|null $queryCacheDependency = null;
85
    /** * @var ParamInterface[] */
86
    protected array $params = [];
87
88
    public function __construct(protected QueryCache $queryCache)
89
    {
90
    }
91
92
    public function addCheck(string $name, string $table, string $expression): static
93
    {
94
        $sql = $this->queryBuilder()->addCheck($name, $table, $expression);
95
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
96
    }
97
98
    public function addColumn(string $table, string $column, string $type): static
99
    {
100
        $sql = $this->queryBuilder()->addColumn($table, $column, $type);
101
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
102
    }
103
104
    public function addCommentOnColumn(string $table, string $column, string $comment): static
105
    {
106
        $sql = $this->queryBuilder()->addCommentOnColumn($table, $column, $comment);
107
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
108
    }
109
110
    public function addCommentOnTable(string $table, string $comment): static
111
    {
112
        $sql = $this->queryBuilder()->addCommentOnTable($table, $comment);
113
        return $this->setSql($sql);
114
    }
115
116
    public function addDefaultValue(string $name, string $table, string $column, mixed $value): static
117
    {
118
        $sql = $this->queryBuilder()->addDefaultValue($name, $table, $column, $value);
119
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
120
    }
121
122
    public function addForeignKey(
123
        string $name,
124
        string $table,
125
        array|string $columns,
126
        string $refTable,
127
        array|string $refColumns,
128
        string $delete = null,
129
        string $update = null
130
    ): static {
131
        $sql = $this->queryBuilder()->addForeignKey(
132
            $name,
133
            $table,
134
            $columns,
135
            $refTable,
136
            $refColumns,
137
            $delete,
138
            $update
139
        );
140
141
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
142
    }
143
144
    public function addPrimaryKey(string $name, string $table, array|string $columns): static
145
    {
146
        $sql = $this->queryBuilder()->addPrimaryKey($name, $table, $columns);
147
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
148
    }
149
150
    public function addUnique(string $name, string $table, array|string $columns): static
151
    {
152
        $sql = $this->queryBuilder()->addUnique($name, $table, $columns);
153
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
154
    }
155
156
    public function alterColumn(string $table, string $column, string $type): static
157
    {
158
        $sql = $this->queryBuilder()->alterColumn($table, $column, $type);
159
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
160
    }
161
162
    public function batchInsert(string $table, array $columns, iterable $rows): static
163
    {
164
        $table = $this->queryBuilder()->quoter()->quoteSql($table);
165
166
        /** @psalm-var string[] $columns */
167
        foreach ($columns as &$column) {
168
            $column = $this->queryBuilder()->quoter()->quoteSql($column);
169
        }
170
171
        unset($column);
172
173
        $params = [];
174
        $sql = $this->queryBuilder()->batchInsert($table, $columns, $rows, $params);
175
176
        $this->setRawSql($sql);
177
        $this->bindValues($params);
178
        return $this;
179
    }
180
181
    abstract public function bindValue(int|string $name, mixed $value, int $dataType = null): static;
182
183
    abstract public function bindValues(array $values): static;
184
185
    public function cache(int $duration = null, Dependency $dependency = null): static
186
    {
187
        $this->queryCacheDuration = $duration ?? $this->queryCache->getDuration();
188
        $this->queryCacheDependency = $dependency;
189
        return $this;
190
    }
191
192
    public function checkIntegrity(string $schema, string $table, bool $check = true): static
193
    {
194
        $sql = $this->queryBuilder()->checkIntegrity($schema, $table, $check);
195
        return $this->setSql($sql);
196
    }
197
198
    public function createIndex(
199
        string $name,
200
        string $table,
201
        array|string $columns,
202
        string $indexType = null,
203
        string $indexMethod = null
204
    ): static {
205
        $sql = $this->queryBuilder()->createIndex($name, $table, $columns, $indexType, $indexMethod);
206
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
207
    }
208
209
    public function createTable(string $table, array $columns, string $options = null): static
210
    {
211
        $sql = $this->queryBuilder()->createTable($table, $columns, $options);
212
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
213
    }
214
215
    public function createView(string $viewName, QueryInterface|string $subquery): static
216
    {
217
        $sql = $this->queryBuilder()->createView($viewName, $subquery);
218
        return $this->setSql($sql)->requireTableSchemaRefresh($viewName);
219
    }
220
221
    public function delete(string $table, array|string $condition = '', array $params = []): static
222
    {
223
        $sql = $this->queryBuilder()->delete($table, $condition, $params);
224
        return $this->setSql($sql)->bindValues($params);
225
    }
226
227
    public function dropCheck(string $name, string $table): static
228
    {
229
        $sql = $this->queryBuilder()->dropCheck($name, $table);
230
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
231
    }
232
233
    public function dropColumn(string $table, string $column): static
234
    {
235
        $sql = $this->queryBuilder()->dropColumn($table, $column);
236
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
237
    }
238
239
    public function dropCommentFromColumn(string $table, string $column): static
240
    {
241
        $sql = $this->queryBuilder()->dropCommentFromColumn($table, $column);
242
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
243
    }
244
245
    public function dropCommentFromTable(string $table): static
246
    {
247
        $sql = $this->queryBuilder()->dropCommentFromTable($table);
248
        return $this->setSql($sql);
249
    }
250
251
    public function dropDefaultValue(string $name, string $table): static
252
    {
253
        $sql = $this->queryBuilder()->dropDefaultValue($name, $table);
254
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
255
    }
256
257
    public function dropForeignKey(string $name, string $table): static
258
    {
259
        $sql = $this->queryBuilder()->dropForeignKey($name, $table);
260
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
261
    }
262
263
    public function dropIndex(string $name, string $table): static
264
    {
265
        $sql = $this->queryBuilder()->dropIndex($name, $table);
266
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
267
    }
268
269
    public function dropPrimaryKey(string $name, string $table): static
270
    {
271
        $sql = $this->queryBuilder()->dropPrimaryKey($name, $table);
272
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
273
    }
274
275
    public function dropTable(string $table): static
276
    {
277
        $sql = $this->queryBuilder()->dropTable($table);
278
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
279
    }
280
281
    public function dropUnique(string $name, string $table): static
282
    {
283
        $sql = $this->queryBuilder()->dropUnique($name, $table);
284
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
285
    }
286
287
    public function dropView(string $viewName): static
288
    {
289
        $sql = $this->queryBuilder()->dropView($viewName);
290
        return $this->setSql($sql)->requireTableSchemaRefresh($viewName);
291
    }
292
293
    public function getParams(bool $asValues = true): array
294
    {
295
        if (!$asValues) {
296
            return $this->params;
297
        }
298
299
        $buildParams = [];
300
301
        foreach ($this->params as $name => $value) {
302
            /** @psalm-var mixed */
303
            $buildParams[$name] = $value->getValue();
304
        }
305
306
        return $buildParams;
307
    }
308
309
    public function getRawSql(): string
310
    {
311
        if (empty($this->params)) {
312
            return $this->sql;
313
        }
314
315
        $params = [];
316
317
        /** @var mixed $value */
318
        foreach ($this->params as $name => $value) {
319
            if (is_string($name) && strncmp(':', $name, 1)) {
320
                $name = ':' . $name;
321
            }
322
323
            if ($value instanceof ParamInterface) {
324
                /** @psalm-var mixed $value */
325
                $value = $value->getValue();
326
            }
327
328
            if (is_string($value)) {
329
                /** @psalm-var mixed */
330
                $params[$name] = $this->queryBuilder()->quoter()->quoteValue($value);
331
            } elseif (is_bool($value)) {
332
                /** @psalm-var string */
333
                $params[$name] = $value ? 'TRUE' : 'FALSE';
334
            } elseif ($value === null) {
335
                $params[$name] = 'NULL';
336
            } elseif ((!is_object($value) && !is_resource($value)) || $value instanceof Expression) {
337
                /** @psalm-var mixed */
338
                $params[$name] = $value;
339
            }
340
        }
341
342
        if (!isset($params[0])) {
343
            return strtr($this->sql, $params);
344
        }
345
346
        // Support unnamed placeholders should be dropped
347
        $sql = '';
348
349
        foreach (explode('?', $this->sql) as $i => $part) {
350
            $sql .= $part . (string) ($params[$i] ?? '');
351
        }
352
353
        return $sql;
354
    }
355
356
    public function getSql(): string
357
    {
358
        return $this->sql;
359
    }
360
361
    public function insert(string $table, QueryInterface|array $columns): static
362
    {
363
        $params = [];
364
        $sql = $this->queryBuilder()->insert($table, $columns, $params);
365
        return $this->setSql($sql)->bindValues($params);
366
    }
367
368
    public function insertWithReturningPks(string $table, array $columns): bool|array
369
    {
370
        $params = [];
371
372
        $sql = $this->queryBuilder()->insertWithReturningPks($table, $columns, $params);
373
374
        $this->setSql($sql)->bindValues($params);
375
376
        /** @psalm-var array|bool $result */
377
        $result = $this->queryInternal(self::QUERY_MODE_ROW | self::QUERY_MODE_EXECUTE);
378
379
        return is_array($result) ? $result : false;
380
    }
381
382
    public function noCache(): static
383
    {
384
        $this->queryCacheDuration = -1;
385
        return $this;
386
    }
387
388
    /**
389
     * @psalm-suppress MixedReturnStatement
390
     * @psalm-suppress MixedInferredReturnType
391
     */
392
    public function execute(): int
393
    {
394
        $sql = $this->getSql();
395
396
        if ($sql === '') {
397
            return 0;
398
        }
399
400
        return $this->queryInternal(self::QUERY_MODE_EXECUTE);
401
    }
402
403
    /**
404
     * @psalm-suppress MixedReturnStatement
405
     * @psalm-suppress MixedInferredReturnType
406
     */
407
    public function query(): DataReaderInterface
408
    {
409
        return $this->queryInternal(self::QUERY_MODE_CURSOR);
410
    }
411
412
    public function queryAll(): array
413
    {
414
        /** @psalm-var array<array-key, array>|null $results */
415
        $results = $this->queryInternal(self::QUERY_MODE_ALL);
416
417
        return $results ?? [];
418
    }
419
420
    public function queryColumn(): array
421
    {
422
        /** @psalm-var mixed $results */
423
        $results = $this->queryInternal(self::QUERY_MODE_COLUMN);
424
425
        return is_array($results) ? $results : [];
426
    }
427
428
    public function queryOne(): array|null
429
    {
430
        /** @psalm-var mixed $results */
431
        $results = $this->queryInternal(self::QUERY_MODE_ROW);
432
433
        return is_array($results) ? $results : null;
434
    }
435
436
    /**
437
     * @psalm-suppress MixedReturnStatement
438
     * @psalm-suppress MixedInferredReturnType
439
     */
440
    public function queryScalar(): bool|string|null|int|float
441
    {
442
        $firstRow = $this->queryInternal(self::QUERY_MODE_ROW);
443
444
        if (!is_array($firstRow)) {
445
            return false;
446
        }
447
448
        /** @psalm-var mixed $result */
449
        $result = current($firstRow);
450
451
        if (is_resource($result) && get_resource_type($result) === 'stream') {
452
            return stream_get_contents($result);
453
        }
454
455
        return $result;
456
    }
457
458
    public function renameColumn(string $table, string $oldName, string $newName): static
459
    {
460
        $sql = $this->queryBuilder()->renameColumn($table, $oldName, $newName);
461
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
462
    }
463
464
    public function renameTable(string $table, string $newName): static
465
    {
466
        $sql = $this->queryBuilder()->renameTable($table, $newName);
467
        return $this->setSql($sql)->requireTableSchemaRefresh($table);
468
    }
469
470
    public function resetSequence(string $table, int|string $value = null): static
471
    {
472
        $sql = $this->queryBuilder()->resetSequence($table, $value);
473
        return $this->setSql($sql);
474
    }
475
476
    public function setRawSql(string $sql): static
477
    {
478
        if ($sql !== $this->sql) {
479
            $this->cancel();
480
            $this->reset();
481
            $this->sql = $sql;
482
        }
483
484
        return $this;
485
    }
486
487
    public function setSql(string $sql): static
488
    {
489
        $this->cancel();
490
        $this->reset();
491
        $this->sql = $this->queryBuilder()->quoter()->quoteSql($sql);
492
493
        return $this;
494
    }
495
496
    public function setRetryHandler(Closure|null $handler): static
497
    {
498
        $this->retryHandler = $handler;
499
        return $this;
500
    }
501
502
    public function truncateTable(string $table): static
503
    {
504
        $sql = $this->queryBuilder()->truncateTable($table);
505
        return $this->setSql($sql);
506
    }
507
508
    public function update(string $table, array $columns, array|string $condition = '', array $params = []): static
509
    {
510
        $sql = $this->queryBuilder()->update($table, $columns, $condition, $params);
511
        return $this->setSql($sql)->bindValues($params);
512
    }
513
514
    public function upsert(
515
        string $table,
516
        QueryInterface|array $insertColumns,
517
        bool|array $updateColumns = true,
518
        array $params = []
519
    ): static {
520
        $sql = $this->queryBuilder()->upsert($table, $insertColumns, $updateColumns, $params);
521
        return $this->setSql($sql)->bindValues($params);
522
    }
523
524
    /**
525
     * Returns the cache key for the query.
526
     *
527
     * @param string $rawSql the raw SQL with parameter values inserted into the corresponding placeholders.
528
     *
529
     * @throws JsonException
530
     *
531
     * @return array The cache key.
532
     */
533
    abstract protected function getCacheKey(int $queryMode, string $rawSql): array;
534
535
    /**
536
     * Executes a prepared statement.
537
     *
538
     * @param string|null $rawSql the rawSql if it has been created.
539
     *
540
     * @throws Exception
541
     * @throws Throwable
542
     */
543
    abstract protected function internalExecute(string|null $rawSql): void;
544
545
    protected function is(int $value, int $flag): bool
546
    {
547
        return ($value & $flag) === $flag;
548
    }
549
550
    /**
551
     * Returns the query result.
552
     *
553
     * @param int $queryMode One from modes QUERY_MODE_*.
554
     *
555
     * @throws Exception
556
     * @throws Throwable
557
     */
558
    abstract protected function internalGetQueryResult(int $queryMode): mixed;
559
560
    /**
561
     * Refreshes table schema, which was marked by {@see requireTableSchemaRefresh()}.
562
     */
563
    abstract protected function refreshTableSchema(): void;
564
565
    /**
566
     * The method is called after the query is executed.
567
     *
568
     * @param int $queryMode One from modes QUERY_MODE_*.
569
     *
570
     * @throws Exception
571
     * @throws Throwable
572
     */
573
    protected function queryInternal(int $queryMode): mixed
574
    {
575
        if ($this->is($queryMode, self::QUERY_MODE_EXECUTE) || $this->is($queryMode, self::QUERY_MODE_CURSOR)) {
576
            return $this->queryWithoutCache($this->getRawSql(), $queryMode);
577
        }
578
579
        return $this->queryWithCache($queryMode);
580
    }
581
582
    /**
583
     * Performs the actual DB query of a SQL statement.
584
     *
585
     * @param int $queryMode One from modes QUERY_MODE_*.
586
     *
587
     * @throws Exception
588
     * @throws Throwable If the query causes any problem.
589
     *
590
     * @return mixed The method execution result.
591
     */
592
    protected function queryWithCache(int $queryMode): mixed
593
    {
594
        $rawSql = $this->getRawSql();
595
596
        $cacheKey = $this->getCacheKey($queryMode, $rawSql);
597
        /** @psalm-var array{CacheInterface, DateInterval|int|null, Dependency|null} $info */
598
        $info = $this->queryCache->info($this->queryCacheDuration, $this->queryCacheDependency);
599
        /** @psalm-var mixed $cacheResult */
600
        $cacheResult = $this->getFromCacheInfo($info, $cacheKey);
601
602
        if ($cacheResult) {
603
            $this->logger?->log(LogLevel::DEBUG, 'Get query result from cache', [self::class . '::query']);
604
            return $cacheResult;
605
        }
606
607
        /** @psalm-var mixed $result */
608
        $result = $this->queryWithoutCache($rawSql, $queryMode);
609
        $this->setToCacheInfo($info, $cacheKey, $result);
610
611
        return $result;
612
    }
613
614
    /**
615
     * Performs the actual DB query of a SQL statement without caching.
616
     *
617
     * @param int $queryMode One from modes QUERY_MODE_*.
618
     *
619
     * @throws Exception
620
     * @throws Throwable If the query causes any problem.
621
     *
622
     * @return mixed The method execution result.
623
     */
624
    protected function queryWithoutCache(string $rawSql, int $queryMode): mixed
625
    {
626
        $isReadMode = $this->isReadMode($queryMode);
627
        $logCategory = self::class . '::' . ($isReadMode ? 'query' : 'execute');
628
629
        $this->logQuery($rawSql, $logCategory);
630
631
        $this->prepare($isReadMode);
632
633
        try {
634
            $this->profiler?->begin($rawSql, [$logCategory]);
635
636
            $this->internalExecute($rawSql);
637
638
            /** @psalm-var mixed $result */
639
            $result = $this->internalGetQueryResult($queryMode);
640
641
            $this->profiler?->end($rawSql, [$logCategory]);
642
643
            if (!$isReadMode) {
644
                $this->refreshTableSchema();
645
            }
646
        } catch (Exception $e) {
647
            $this->profiler?->end($rawSql, [$logCategory]);
648
            throw $e;
649
        }
650
651
        return $result;
652
    }
653
654
    /**
655
     * Logs the current database query if query logging is enabled and returns the profiling token if profiling is
656
     * enabled.
657
     */
658
    protected function logQuery(string $rawSql, string $category): void
659
    {
660
        $this->logger?->log(LogLevel::INFO, $rawSql, [$category]);
661
    }
662
663
    /**
664
     * Marks a specified table schema to be refreshed after command execution.
665
     *
666
     * @param string $name Name of the table, which schema should be refreshed.
667
     *
668
     * @return static The command object itself.
669
     */
670
    protected function requireTableSchemaRefresh(string $name): static
671
    {
672
        $this->refreshTableName = $name;
673
        return $this;
674
    }
675
676
    /**
677
     * Marks the command to be executed in transaction.
678
     *
679
     * @param string|null $isolationLevel The isolation level to use for this transaction.
680
     *
681
     * {@see TransactionInterface::begin()} for details.
682
     *
683
     * @return static The command object itself.
684
     */
685
    protected function requireTransaction(string $isolationLevel = null): static
686
    {
687
        $this->isolationLevel = $isolationLevel;
688
        return $this;
689
    }
690
691
    /**
692
     * Resets the command object, so it can be reused to build another SQL statement.
693
     */
694
    protected function reset(): void
695
    {
696
        $this->sql = '';
697
        $this->params = [];
698
        $this->refreshTableName = null;
699
        $this->isolationLevel = null;
700
        $this->retryHandler = null;
701
    }
702
703
    /**
704
     * @psalm-param array{CacheInterface, DateInterval|int|null, Dependency|null} $info
705
     */
706
    private function getFromCacheInfo(array|null $info, array $cacheKey): mixed
707
    {
708
        if (!is_array($info)) {
0 ignored issues
show
introduced by
The condition is_array($info) is always true.
Loading history...
709
            return null;
710
        }
711
712
        $cache = $info[0];
713
714
        /** @psalm-var mixed $result */
715
        $result = $cache->getOrSet(
716
            $cacheKey,
717
            static fn () => null,
718
        );
719
720
        if (is_array($result) && isset($result[0])) {
721
            $this->logger?->log(LogLevel::DEBUG, 'Query result served from cache', [self::class . '::query']);
722
723
            return $result[0];
724
        }
725
726
        return null;
727
    }
728
729
    /**
730
     * @psalm-param array{CacheInterface, DateInterval|int|null, Dependency|null} $info
731
     */
732
    private function setToCacheInfo(array|null $info, array $cacheKey, mixed $result): void
733
    {
734
        if (!is_array($info)) {
0 ignored issues
show
introduced by
The condition is_array($info) is always true.
Loading history...
735
            return;
736
        }
737
738
        $cache = $info[0];
739
740
        $cache->getOrSet(
741
            $cacheKey,
742
            static fn (): array => [$result],
743
            $info[1],
744
            $info[2]
745
        );
746
747
        $this->logger?->log(LogLevel::DEBUG, 'Saved query result in cache', [self::class . '::query']);
748
    }
749
750
    private function isReadMode(int $queryMode): bool
751
    {
752
        return !$this->is($queryMode, self::QUERY_MODE_EXECUTE);
753
    }
754
}
755