| Total Complexity | 92 | 
| Total Lines | 660 | 
| Duplicated Lines | 0 % | 
| Changes | 0 | ||
Complex classes like AbstractCommand 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 AbstractCommand, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 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  | 
            ||
| 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  | 
            ||
| 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 noCache(): static  | 
            ||
| 369 |     { | 
            ||
| 370 | $this->queryCacheDuration = -1;  | 
            ||
| 371 | return $this;  | 
            ||
| 372 | }  | 
            ||
| 373 | |||
| 374 | /**  | 
            ||
| 375 | * @psalm-suppress MixedReturnStatement  | 
            ||
| 376 | * @psalm-suppress MixedInferredReturnType  | 
            ||
| 377 | */  | 
            ||
| 378 | public function execute(): int  | 
            ||
| 379 |     { | 
            ||
| 380 | $sql = $this->getSql();  | 
            ||
| 381 | |||
| 382 |         if ($sql === '') { | 
            ||
| 383 | return 0;  | 
            ||
| 384 | }  | 
            ||
| 385 | |||
| 386 | return $this->queryInternal((int) static::QUERY_MODE_NONE);  | 
            ||
| 387 | }  | 
            ||
| 388 | |||
| 389 | /**  | 
            ||
| 390 | * @psalm-suppress MixedReturnStatement  | 
            ||
| 391 | * @psalm-suppress MixedInferredReturnType  | 
            ||
| 392 | */  | 
            ||
| 393 | public function query(): DataReaderInterface  | 
            ||
| 394 |     { | 
            ||
| 395 | return $this->queryInternal((int) static::QUERY_MODE_CURSOR);  | 
            ||
| 396 | }  | 
            ||
| 397 | |||
| 398 | public function queryAll(): array  | 
            ||
| 399 |     { | 
            ||
| 400 | /** @psalm-var array<array-key, array>|null $results */  | 
            ||
| 401 | $results = $this->queryInternal((int) static::QUERY_MODE_ALL);  | 
            ||
| 402 | |||
| 403 | return $results ?? [];  | 
            ||
| 404 | }  | 
            ||
| 405 | |||
| 406 | public function queryColumn(): array  | 
            ||
| 407 |     { | 
            ||
| 408 | /** @psalm-var mixed $results */  | 
            ||
| 409 | $results = $this->queryInternal(self::QUERY_MODE_COLUMN);  | 
            ||
| 410 | |||
| 411 | return is_array($results) ? $results : [];  | 
            ||
| 412 | }  | 
            ||
| 413 | |||
| 414 | public function queryOne(): array|null  | 
            ||
| 415 |     { | 
            ||
| 416 | /** @psalm-var mixed $results */  | 
            ||
| 417 | $results = $this->queryInternal(self::QUERY_MODE_ROW);  | 
            ||
| 418 | |||
| 419 | return is_array($results) ? $results : null;  | 
            ||
| 420 | }  | 
            ||
| 421 | |||
| 422 | /**  | 
            ||
| 423 | * @psalm-suppress MixedReturnStatement  | 
            ||
| 424 | * @psalm-suppress MixedInferredReturnType  | 
            ||
| 425 | */  | 
            ||
| 426 | public function queryScalar(): bool|string|null|int|float  | 
            ||
| 427 |     { | 
            ||
| 428 | $firstRow = $this->queryInternal((int) static::QUERY_MODE_ROW);  | 
            ||
| 429 | |||
| 430 |         if (!is_array($firstRow)) { | 
            ||
| 431 | return false;  | 
            ||
| 432 | }  | 
            ||
| 433 | |||
| 434 | /** @psalm-var mixed $result */  | 
            ||
| 435 | $result = current($firstRow);  | 
            ||
| 436 | |||
| 437 |         if (is_resource($result) && get_resource_type($result) === 'stream') { | 
            ||
| 438 | return stream_get_contents($result);  | 
            ||
| 439 | }  | 
            ||
| 440 | |||
| 441 | return $result;  | 
            ||
| 442 | }  | 
            ||
| 443 | |||
| 444 | public function renameColumn(string $table, string $oldName, string $newName): static  | 
            ||
| 445 |     { | 
            ||
| 446 | $sql = $this->queryBuilder()->renameColumn($table, $oldName, $newName);  | 
            ||
| 447 | return $this->setSql($sql)->requireTableSchemaRefresh($table);  | 
            ||
| 448 | }  | 
            ||
| 449 | |||
| 450 | public function renameTable(string $table, string $newName): static  | 
            ||
| 451 |     { | 
            ||
| 452 | $sql = $this->queryBuilder()->renameTable($table, $newName);  | 
            ||
| 453 | return $this->setSql($sql)->requireTableSchemaRefresh($table);  | 
            ||
| 454 | }  | 
            ||
| 455 | |||
| 456 | public function resetSequence(string $table, int|string $value = null): static  | 
            ||
| 457 |     { | 
            ||
| 458 | $sql = $this->queryBuilder()->resetSequence($table, $value);  | 
            ||
| 459 | return $this->setSql($sql);  | 
            ||
| 460 | }  | 
            ||
| 461 | |||
| 462 | public function setRawSql(string $sql): static  | 
            ||
| 463 |     { | 
            ||
| 464 |         if ($sql !== $this->sql) { | 
            ||
| 465 | $this->cancel();  | 
            ||
| 466 | $this->reset();  | 
            ||
| 467 | $this->sql = $sql;  | 
            ||
| 468 | }  | 
            ||
| 469 | |||
| 470 | return $this;  | 
            ||
| 471 | }  | 
            ||
| 472 | |||
| 473 | public function setSql(string $sql): static  | 
            ||
| 474 |     { | 
            ||
| 475 | $this->cancel();  | 
            ||
| 476 | $this->reset();  | 
            ||
| 477 | $this->sql = $this->queryBuilder()->quoter()->quoteSql($sql);  | 
            ||
| 478 | |||
| 479 | return $this;  | 
            ||
| 480 | }  | 
            ||
| 481 | |||
| 482 | public function setRetryHandler(Closure|null $handler): static  | 
            ||
| 483 |     { | 
            ||
| 484 | $this->retryHandler = $handler;  | 
            ||
| 485 | return $this;  | 
            ||
| 486 | }  | 
            ||
| 487 | |||
| 488 | public function truncateTable(string $table): static  | 
            ||
| 489 |     { | 
            ||
| 490 | $sql = $this->queryBuilder()->truncateTable($table);  | 
            ||
| 491 | return $this->setSql($sql);  | 
            ||
| 492 | }  | 
            ||
| 493 | |||
| 494 | public function update(string $table, array $columns, array|string $condition = '', array $params = []): static  | 
            ||
| 495 |     { | 
            ||
| 496 | $sql = $this->queryBuilder()->update($table, $columns, $condition, $params);  | 
            ||
| 497 | return $this->setSql($sql)->bindValues($params);  | 
            ||
| 498 | }  | 
            ||
| 499 | |||
| 500 | public function upsert(  | 
            ||
| 501 | string $table,  | 
            ||
| 502 | QueryInterface|array $insertColumns,  | 
            ||
| 503 | bool|array $updateColumns = true,  | 
            ||
| 504 | array $params = []  | 
            ||
| 505 |     ): static { | 
            ||
| 506 | $sql = $this->queryBuilder()->upsert($table, $insertColumns, $updateColumns, $params);  | 
            ||
| 507 | return $this->setSql($sql)->bindValues($params);  | 
            ||
| 508 | }  | 
            ||
| 509 | |||
| 510 | /**  | 
            ||
| 511 | * Returns the cache key for the query.  | 
            ||
| 512 | *  | 
            ||
| 513 | * @param string $rawSql the raw SQL with parameter values inserted into the corresponding placeholders.  | 
            ||
| 514 | *  | 
            ||
| 515 | * @throws JsonException  | 
            ||
| 516 | *  | 
            ||
| 517 | * @return array The cache key.  | 
            ||
| 518 | */  | 
            ||
| 519 | abstract protected function getCacheKey(int $queryMode, string $rawSql): array;  | 
            ||
| 520 | |||
| 521 | /**  | 
            ||
| 522 | * Executes a prepared statement.  | 
            ||
| 523 | *  | 
            ||
| 524 | * @param string|null $rawSql the rawSql if it has been created.  | 
            ||
| 525 | *  | 
            ||
| 526 | * @throws Exception  | 
            ||
| 527 | * @throws Throwable  | 
            ||
| 528 | */  | 
            ||
| 529 | abstract protected function internalExecute(string|null $rawSql): void;  | 
            ||
| 530 | |||
| 531 | /**  | 
            ||
| 532 | * Returns the query result.  | 
            ||
| 533 | *  | 
            ||
| 534 | * @param int $queryMode One from modes QUERY_MODE_*.  | 
            ||
| 535 | *  | 
            ||
| 536 | * @throws Exception  | 
            ||
| 537 | * @throws Throwable  | 
            ||
| 538 | */  | 
            ||
| 539 | abstract protected function internalGetQueryResult(int $queryMode): mixed;  | 
            ||
| 540 | |||
| 541 | /**  | 
            ||
| 542 |      * Refreshes table schema, which was marked by {@see requireTableSchemaRefresh()}. | 
            ||
| 543 | */  | 
            ||
| 544 | abstract protected function refreshTableSchema(): void;  | 
            ||
| 545 | |||
| 546 | /**  | 
            ||
| 547 | * The method is called after the query is executed.  | 
            ||
| 548 | *  | 
            ||
| 549 | * @param int $queryMode One from modes QUERY_MODE_*.  | 
            ||
| 550 | *  | 
            ||
| 551 | * @throws Exception  | 
            ||
| 552 | * @throws Throwable  | 
            ||
| 553 | */  | 
            ||
| 554 | protected function queryInternal(int $queryMode): mixed  | 
            ||
| 555 |     { | 
            ||
| 556 |         if ($queryMode === static::QUERY_MODE_NONE || $queryMode === static::QUERY_MODE_CURSOR) { | 
            ||
| 557 | return $this->queryWithoutCache($this->getRawSql(), $queryMode);  | 
            ||
| 558 | }  | 
            ||
| 559 | |||
| 560 | return $this->queryWithCache($queryMode);  | 
            ||
| 561 | }  | 
            ||
| 562 | |||
| 563 | /**  | 
            ||
| 564 | * Performs the actual DB query of a SQL statement.  | 
            ||
| 565 | *  | 
            ||
| 566 | * @param int $queryMode One from modes QUERY_MODE_*.  | 
            ||
| 567 | *  | 
            ||
| 568 | * @throws Exception  | 
            ||
| 569 | * @throws Throwable If the query causes any problem.  | 
            ||
| 570 | *  | 
            ||
| 571 | * @return mixed The method execution result.  | 
            ||
| 572 | */  | 
            ||
| 573 | protected function queryWithCache(int $queryMode): mixed  | 
            ||
| 574 |     { | 
            ||
| 575 | $rawSql = $this->getRawSql();  | 
            ||
| 576 | |||
| 577 | $cacheKey = $this->getCacheKey($queryMode, $rawSql);  | 
            ||
| 578 |         /** @psalm-var array{CacheInterface, DateInterval|int|null, Dependency|null} $info */ | 
            ||
| 579 | $info = $this->queryCache->info($this->queryCacheDuration, $this->queryCacheDependency);  | 
            ||
| 580 | /** @psalm-var mixed $cacheResult */  | 
            ||
| 581 | $cacheResult = $this->getFromCacheInfo($info, $cacheKey);  | 
            ||
| 582 | |||
| 583 |         if ($cacheResult) { | 
            ||
| 584 | $this->logger?->log(LogLevel::DEBUG, 'Get query result from cache', [self::class . '::query']);  | 
            ||
| 585 | return $cacheResult;  | 
            ||
| 586 | }  | 
            ||
| 587 | |||
| 588 | /** @psalm-var mixed $result */  | 
            ||
| 589 | $result = $this->queryWithoutCache($rawSql, $queryMode);  | 
            ||
| 590 | $this->setToCacheInfo($info, $cacheKey, $result);  | 
            ||
| 591 | |||
| 592 | return $result;  | 
            ||
| 593 | }  | 
            ||
| 594 | |||
| 595 | /**  | 
            ||
| 596 | * Performs the actual DB query of a SQL statement without caching.  | 
            ||
| 597 | *  | 
            ||
| 598 | * @param int $queryMode One from modes QUERY_MODE_*.  | 
            ||
| 599 | *  | 
            ||
| 600 | * @throws Exception  | 
            ||
| 601 | * @throws Throwable If the query causes any problem.  | 
            ||
| 602 | *  | 
            ||
| 603 | * @return mixed The method execution result.  | 
            ||
| 604 | */  | 
            ||
| 605 | protected function queryWithoutCache(string $rawSql, int $queryMode): mixed  | 
            ||
| 633 | }  | 
            ||
| 634 | |||
| 635 | /**  | 
            ||
| 636 | * Logs the current database query if query logging is enabled and returns the profiling token if profiling is  | 
            ||
| 637 | * enabled.  | 
            ||
| 638 | */  | 
            ||
| 639 | protected function logQuery(string $rawSql, string $category): void  | 
            ||
| 640 |     { | 
            ||
| 641 | $this->logger?->log(LogLevel::INFO, $rawSql, [$category]);  | 
            ||
| 642 | }  | 
            ||
| 643 | |||
| 644 | /**  | 
            ||
| 645 | * Marks a specified table schema to be refreshed after command execution.  | 
            ||
| 646 | *  | 
            ||
| 647 | * @param string $name Name of the table, which schema should be refreshed.  | 
            ||
| 648 | *  | 
            ||
| 649 | * @return static The command object itself.  | 
            ||
| 650 | */  | 
            ||
| 651 | protected function requireTableSchemaRefresh(string $name): static  | 
            ||
| 652 |     { | 
            ||
| 653 | $this->refreshTableName = $name;  | 
            ||
| 654 | return $this;  | 
            ||
| 655 | }  | 
            ||
| 656 | |||
| 657 | /**  | 
            ||
| 658 | * Marks the command to be executed in transaction.  | 
            ||
| 659 | *  | 
            ||
| 660 | * @param string|null $isolationLevel The isolation level to use for this transaction.  | 
            ||
| 661 | *  | 
            ||
| 662 |      * {@see TransactionInterface::begin()} for details. | 
            ||
| 663 | *  | 
            ||
| 664 | * @return static The command object itself.  | 
            ||
| 665 | */  | 
            ||
| 666 | protected function requireTransaction(string $isolationLevel = null): static  | 
            ||
| 667 |     { | 
            ||
| 668 | $this->isolationLevel = $isolationLevel;  | 
            ||
| 669 | return $this;  | 
            ||
| 670 | }  | 
            ||
| 671 | |||
| 672 | /**  | 
            ||
| 673 | * Resets the command object, so it can be reused to build another SQL statement.  | 
            ||
| 674 | */  | 
            ||
| 675 | protected function reset(): void  | 
            ||
| 676 |     { | 
            ||
| 677 | $this->sql = '';  | 
            ||
| 678 | $this->params = [];  | 
            ||
| 679 | $this->refreshTableName = null;  | 
            ||
| 680 | $this->isolationLevel = null;  | 
            ||
| 681 | $this->retryHandler = null;  | 
            ||
| 682 | }  | 
            ||
| 683 | |||
| 684 | /**  | 
            ||
| 685 |      * @psalm-param array{CacheInterface, DateInterval|int|null, Dependency|null} $info | 
            ||
| 686 | */  | 
            ||
| 687 | private function getFromCacheInfo(array|null $info, array $cacheKey): mixed  | 
            ||
| 688 |     { | 
            ||
| 689 |         if (!is_array($info)) { | 
            ||
| 
                                                                                                    
                        
                         | 
                |||
| 690 | return null;  | 
            ||
| 691 | }  | 
            ||
| 692 | |||
| 693 | $cache = $info[0];  | 
            ||
| 694 | |||
| 695 | /** @psalm-var mixed $result */  | 
            ||
| 696 | $result = $cache->getOrSet(  | 
            ||
| 697 | $cacheKey,  | 
            ||
| 698 | static fn () => null,  | 
            ||
| 699 | );  | 
            ||
| 700 | |||
| 701 |         if (is_array($result) && isset($result[0])) { | 
            ||
| 702 | $this->logger?->log(LogLevel::DEBUG, 'Query result served from cache', [self::class . '::query']);  | 
            ||
| 703 | |||
| 704 | return $result[0];  | 
            ||
| 705 | }  | 
            ||
| 706 | |||
| 707 | return null;  | 
            ||
| 708 | }  | 
            ||
| 709 | |||
| 710 | /**  | 
            ||
| 711 |      * @psalm-param array{CacheInterface, DateInterval|int|null, Dependency|null} $info | 
            ||
| 712 | */  | 
            ||
| 713 | private function setToCacheInfo(array|null $info, array $cacheKey, mixed $result): void  | 
            ||
| 729 | }  | 
            ||
| 730 | |||
| 731 | private function isReadMode(int $queryMode): bool  | 
            ||
| 732 |     { | 
            ||
| 733 | return $queryMode !== static::QUERY_MODE_NONE;  | 
            ||
| 734 | }  | 
            ||
| 735 | }  | 
            ||
| 736 |