Passed
Pull Request — master (#347)
by Def
04:44 queued 02:40
created

Connection   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 135
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 48
dl 0
loc 135
rs 10
c 2
b 0
f 0
ccs 0
cts 0
cp 0
wmc 24

14 Methods

Rating   Name   Duplication   Size   Complexity  
A noCache() 0 9 1
A cache() 0 10 1
A isSavepointEnabled() 0 3 1
A createBatchQueryResult() 0 3 1
A transaction() 0 20 4
A beginTransaction() 0 16 3
A getTransaction() 0 3 3
A getTablePrefix() 0 3 1
A rollbackTransactionOnLevel() 0 10 4
A getTableSchema() 0 3 1
A setEnableSavepoint() 0 3 1
A createTableName() 0 3 1
A setTablePrefix() 0 3 1
A __construct() 0 2 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Connection;
6
7
use Psr\Log\LoggerAwareTrait;
8
use Psr\Log\LogLevel;
9
use Throwable;
10
use Yiisoft\Cache\Dependency\Dependency;
11
use Yiisoft\Db\AwareTrait\ProfilerAwareTrait;
12
use Yiisoft\Db\Cache\QueryCache;
13
use Yiisoft\Db\Query\BatchQueryResult;
14
use Yiisoft\Db\Query\BatchQueryResultInterface;
15
use Yiisoft\Db\Query\QueryInterface;
16
use Yiisoft\Db\Schema\TableName;
17
use Yiisoft\Db\Schema\TableNameInterface;
18
use Yiisoft\Db\Schema\TableSchemaInterface;
19
use Yiisoft\Db\Transaction\TransactionInterface;
20
21
abstract class Connection implements ConnectionInterface
22
{
23
    use LoggerAwareTrait;
24
    use ProfilerAwareTrait;
25
26
    protected ?TransactionInterface $transaction = null;
27
    private bool $enableSavepoint = true;
28
    private int $serverRetryInterval = 600;
0 ignored issues
show
introduced by
The private property $serverRetryInterval is not used, and could be removed.
Loading history...
29
    private string $tablePrefix = '';
30
31
    public function __construct(private QueryCache $queryCache)
32
    {
33
    }
34
35
    public function beginTransaction(string $isolationLevel = null): TransactionInterface
36
    {
37
        $this->open();
38
        $this->transaction = $this->getTransaction();
39
40
        if ($this->transaction === null) {
41
            $this->transaction = $this->createTransaction();
42
        }
43
44
        if ($this->logger !== null) {
45
            $this->transaction->setLogger($this->logger);
46
        }
47
48
        $this->transaction->begin($isolationLevel);
49
50
        return $this->transaction;
51
    }
52
53
    public function cache(callable $callable, int $duration = null, Dependency $dependency = null): mixed
54
    {
55
        $this->queryCache->setInfo(
56
            [$duration ?? $this->queryCache->getDuration(), $dependency]
57
        );
58
        /** @var mixed */
59
        $result = $callable($this);
60
        $this->queryCache->removeLastInfo();
61
62
        return $result;
63
    }
64
65
    public function createBatchQueryResult(QueryInterface $query, bool $each = false): BatchQueryResultInterface
66
    {
67
        return new BatchQueryResult($query, $each);
68
    }
69
70
    public function createTableName(string $name, string $schemaName = null, string $catalogName = null, string $serverName = null): TableNameInterface
71
    {
72
        return (new TableName($name, $schemaName, $catalogName, $serverName))->withPrefix($this->tablePrefix);
73
    }
74
75
    public function getTablePrefix(): string
76
    {
77
        return $this->tablePrefix;
78
    }
79
80
    public function getTableSchema(string $name, bool $refresh = false): ?TableSchemaInterface
81
    {
82
        return $this->getSchema()->getTableSchema($name, $refresh);
83
    }
84
85
    public function getTransaction(): ?TransactionInterface
86
    {
87
        return $this->transaction && $this->transaction->isActive() ? $this->transaction : null;
88
    }
89
90
    public function isSavepointEnabled(): bool
91
    {
92
        return $this->enableSavepoint;
93
    }
94
95
    public function noCache(callable $callable): mixed
96
    {
97
        $queryCache = $this->queryCache;
98
        $queryCache->setInfo(false);
99
        /** @var mixed */
100
        $result = $callable($this);
101
        $queryCache->removeLastInfo();
102
103
        return $result;
104
    }
105
106
    public function setEnableSavepoint(bool $value): void
107
    {
108
        $this->enableSavepoint = $value;
109
    }
110
111
    public function setTablePrefix(string $value): void
112
    {
113
        $this->tablePrefix = $value;
114
    }
115
116
    public function transaction(callable $callback, string $isolationLevel = null): mixed
117
    {
118
        $transaction = $this->beginTransaction($isolationLevel);
119
120
        $level = $transaction->getLevel();
121
122
        try {
123
            /** @var mixed */
124
            $result = $callback($this);
125
126
            if ($transaction->isActive() && $transaction->getLevel() === $level) {
127
                $transaction->commit();
128
            }
129
        } catch (Throwable $e) {
130
            $this->rollbackTransactionOnLevel($transaction, $level);
131
132
            throw $e;
133
        }
134
135
        return $result;
136
    }
137
138
    /**
139
     * Rolls back given {@see TransactionInterface} object if it's still active and level match. In some cases rollback
140
     * can fail, so this method is fail-safe. Exceptions thrown from rollback will be caught and just logged with
141
     * {@see logger->log()}.
142
     *
143
     * @param TransactionInterface $transaction TransactionInterface object given from {@see beginTransaction()}.
144
     * @param int $level TransactionInterface level just after {@see beginTransaction()} call.
145
     */
146
    private function rollbackTransactionOnLevel(TransactionInterface $transaction, int $level): void
147
    {
148
        if ($transaction->isActive() && $transaction->getLevel() === $level) {
149
            /**
150
             * {@see https://github.com/yiisoft/yii2/pull/13347}
151
             */
152
            try {
153
                $transaction->rollBack();
154
            } catch (\Exception $e) {
155
                $this->logger?->log(LogLevel::ERROR, (string) $e, [__METHOD__]);
156
                /** hide this exception to be able to continue throwing original exception outside */
157
            }
158
        }
159
    }
160
}
161