Test Failed
Pull Request — master (#44)
by Wilmer
13:17 queued 09:47
created

TransactionPDOOracle::begin()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 33
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 21
nc 4
nop 1
dl 0
loc 33
rs 9.2728
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Oracle\PDO;
6
7
use Psr\Log\LogLevel;
8
use Throwable;
9
use Yiisoft\Db\AwareTrait\LoggerAwareTrait;
10
use Yiisoft\Db\Connection\ConnectionPDOInterface;
0 ignored issues
show
Bug introduced by
The type Yiisoft\Db\Connection\ConnectionPDOInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
11
use Yiisoft\Db\Exception\Exception;
12
use Yiisoft\Db\Exception\InvalidConfigException;
13
use Yiisoft\Db\Exception\NotSupportedException;
14
use Yiisoft\Db\Transaction\TransactionInterface;
0 ignored issues
show
Bug introduced by
The type Yiisoft\Db\Transaction\TransactionInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
16
final class TransactionPDOOracle implements TransactionInterface
17
{
18
    use LoggerAwareTrait;
19
20
    /**
21
     * A constant representing the transaction isolation level `READ UNCOMMITTED`.
22
     *
23
     * @link http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels}
24
     */
25
    public const READ_UNCOMMITTED = 'READ UNCOMMITTED';
26
27
    /**
28
     * A constant representing the transaction isolation level `READ COMMITTED`.
29
     *
30
     * @link http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels}
31
     */
32
    public const READ_COMMITTED = 'READ COMMITTED';
33
34
    /**
35
     * A constant representing the transaction isolation level `REPEATABLE READ`.
36
     *
37
     * @link http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels}
38
     */
39
    public const REPEATABLE_READ = 'REPEATABLE READ';
40
41
    /**
42
     * A constant representing the transaction isolation level `SERIALIZABLE`.
43
     *
44
     * {@see http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels}
45
     */
46
    public const SERIALIZABLE = 'SERIALIZABLE';
47
48
    private int $level = 0;
49
50
    public function __construct(private ConnectionPDOInterface $db)
51
    {
52
    }
53
54
    public function begin(?string $isolationLevel = null): void
55
    {
56
        $this->db->open();
57
58
        if ($this->level === 0) {
59
            if ($isolationLevel !== null) {
60
                $this->db->getSchema()->setTransactionIsolationLevel($isolationLevel);
61
            }
62
63
            $this->logger?->log(
64
                LogLevel::DEBUG,
65
                'Begin transaction' . ($isolationLevel ? ' with isolation level ' . $isolationLevel : '')
66
                . ' ' . __METHOD__
67
            );
68
            $this->db->getPDO()->beginTransaction();
69
            $this->level = 1;
70
            return;
71
        }
72
73
        $schema = $this->db->getSchema();
74
75
        if ($schema->supportsSavepoint()) {
76
            $this->logger?->log(LogLevel::DEBUG, 'Set savepoint ' . $this->level . ' ' . __METHOD__);
77
            $schema->createSavepoint('LEVEL' . $this->level);
78
        } else {
79
            $this->logger?->log(
80
                LogLevel::DEBUG,
81
                'Transaction not started: nested transaction not supported ' . __METHOD__
82
            );
83
            throw new NotSupportedException('Transaction not started: nested transaction not supported.');
84
        }
85
86
        $this->level++;
87
    }
88
89
    /**
90
     * Commits a transaction.
91
     *
92
     * @throws Exception|Throwable if the transaction is not active
93
     */
94
    public function commit(): void
95
    {
96
        if (!$this->isActive()) {
97
            throw new Exception('Failed to commit transaction: transaction was inactive.');
98
        }
99
100
        $this->level--;
101
102
        if ($this->level === 0) {
103
            $this->logger?->log(LogLevel::DEBUG, 'Commit transaction ' . __METHOD__);
104
            $this->db->getPDO()->commit();
105
            return;
106
        }
107
108
        $schema = $this->db->getSchema();
109
110
        if ($schema->supportsSavepoint()) {
111
            $this->logger?->log(LogLevel::DEBUG, 'Release savepoint ' . $this->level . ' ' . __METHOD__);
112
            $schema->releaseSavepoint('LEVEL' . $this->level);
113
        } else {
114
            $this->logger?->log(
115
                LogLevel::INFO,
116
                'Transaction not committed: nested transaction not supported ' . __METHOD__
117
            );
118
        }
119
    }
120
121
    public function getLevel(): int
122
    {
123
        return $this->level;
124
    }
125
126
    public function isActive(): bool
127
    {
128
        return $this->level > 0 && $this->db->isActive();
129
    }
130
131
    /**
132
     * @throws Exception|InvalidConfigException|Throwable
133
     */
134
    public function rollBack(): void
135
    {
136
        if (!$this->isActive()) {
137
            /**
138
             * do nothing if transaction is not active: this could be the transaction is committed but the event handler
139
             * to "commitTransaction" throw an exception
140
             */
141
            return;
142
        }
143
144
        $this->level--;
145
        if ($this->level === 0) {
146
            $this->logger?->log(LogLevel::INFO, 'Roll back transaction ' . __METHOD__);
147
            $this->db->getPDO()->rollBack();
148
            return;
149
        }
150
151
        $schema = $this->db->getSchema();
152
        if ($schema->supportsSavepoint()) {
153
            $this->logger?->log(LogLevel::DEBUG, 'Roll back to savepoint ' . $this->level . ' ' . __METHOD__);
154
            $schema->rollBackSavepoint('LEVEL' . $this->level);
155
        } else {
156
            $this->logger?->log(
157
                LogLevel::INFO,
158
                'Transaction not rolled back: nested transaction not supported ' . __METHOD__
159
            );
160
        }
161
    }
162
163
    public function setIsolationLevel(string $level): void
164
    {
165
        if (!$this->isActive()) {
166
            throw new Exception('Failed to set isolation level: transaction was inactive.');
167
        }
168
169
        $this->logger?->log(
170
            LogLevel::DEBUG,
171
            'Setting transaction isolation level to ' . $this->level . ' ' . __METHOD__
172
        );
173
        $this->db->getSchema()->setTransactionIsolationLevel($level);
174
    }
175
}
176