Passed
Push — master ( 3492e3...5e09ea )
by Ondřej
04:41 queued 10s
created

TxHandle::ensureStringSerializer()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 6
rs 10
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A TxHandle::isOpen() 0 3 1
1
<?php
2
declare(strict_types=1);
3
namespace Ivory\Connection;
4
5
use Ivory\Exception\InvalidStateException;
6
use Ivory\Lang\Sql\Types;
7
use Ivory\Utils\StringUtils;
8
9
/**
10
 * {@inheritdoc}
11
 *
12
 * This implementation checks, upon destruction, whether the transaction has properly been closed. If not, i.e., when
13
 * the transaction handle gets lost and thus no further means of controlling the transaction are available, a warning is
14
 * emitted.
15
 */
16
class TxHandle implements ITxHandle
17
{
18
    private const PREPARED_TX_RANDOM_NAME_PREFIX = 'IvoryTx';
19
    /** Total length (including prefix) of random names generated for prepared transactions without an explicit name. */
20
    private const PREPARED_TX_RANDOM_NAME_LEN = 16;
21
22
    private $open = true;
23
    private $stmtExec;
24
    private $txCtl;
25
    private $sessionCtl;
26
27
    public function __construct(
28
        IStatementExecution $stmtExec,
29
        IObservableTransactionControl $observableTxCtl,
30
        ISessionControl $sessionCtl
31
    ) {
32
        $this->stmtExec = $stmtExec;
33
        $this->txCtl = $observableTxCtl;
34
        $this->sessionCtl = $sessionCtl;
35
    }
36
37
    public function __destruct()
38
    {
39
        if ($this->open) {
40
            trigger_error($this->makeReleasedOpenHandleWarning(), E_USER_WARNING);
41
        }
42
    }
43
44
    protected function makeReleasedOpenHandleWarning(): string
45
    {
46
        return 'An open transaction handle has been released from memory. Has the transaction been closed properly?';
47
    }
48
49
    private function assertOpen(): void
50
    {
51
        if (!$this->open) {
52
            if ($this->txCtl->inTransaction()) {
53
                throw new InvalidStateException(
54
                    'Controlling the transaction using a wrong handle - this has already been closed'
55
                );
56
            } else {
57
                throw new InvalidStateException('The transaction is not open anymore');
58
            }
59
        }
60
    }
61
62
    public function isOpen(): bool
63
    {
64
        return $this->open;
65
    }
66
67
    public function getTxConfig(): TxConfig
68
    {
69
        $this->assertOpen();
70
        $connConfig = $this->sessionCtl->getConfig();
71
        return TxConfig::createFromParams(
72
            $connConfig->get('transaction_isolation'),
0 ignored issues
show
Bug introduced by
It seems like $connConfig->get('transaction_isolation') can also be of type boolean; however, parameter $isolationLevel of Ivory\Connection\TxConfig::createFromParams() does only seem to accept null|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

72
            /** @scrutinizer ignore-type */ $connConfig->get('transaction_isolation'),
Loading history...
73
            $connConfig->get('transaction_read_only'),
74
            $connConfig->get('transaction_deferrable')
75
        );
76
    }
77
78
    public function setupTransaction($transactionOptions): void
79
    {
80
        $this->assertOpen();
81
        $txConfig = TxConfig::create($transactionOptions);
82
        $this->stmtExec->rawCommand('SET TRANSACTION ' . $txConfig->toSql());
83
    }
84
85
    public function setTransactionSnapshot(string $snapshotId): void
86
    {
87
        $this->assertOpen();
88
        $str = Types::serializeString($snapshotId);
89
        $this->stmtExec->rawCommand("SET TRANSACTION SNAPSHOT $str");
90
    }
91
92
    public function exportTransactionSnapshot(): string
93
    {
94
        $this->assertOpen();
95
96
        $r = $this->stmtExec->rawQuery('SELECT pg_export_snapshot()');
97
        return $r->value();
98
    }
99
100
    public function commit(): void
101
    {
102
        $this->assertOpen();
103
        $this->stmtExec->rawCommand('COMMIT');
104
        $this->open = false;
105
        $this->txCtl->notifyTransactionCommit();
106
    }
107
108
    public function rollback(): void
109
    {
110
        $this->assertOpen();
111
        $this->stmtExec->rawCommand('ROLLBACK');
112
        $this->open = false;
113
        $this->txCtl->notifyTransactionRollback();
114
    }
115
116
    public function rollbackIfOpen(): void
117
    {
118
        if ($this->open) {
119
            $this->stmtExec->rawCommand('ROLLBACK');
120
            $this->open = false;
121
            $this->txCtl->notifyTransactionRollback();
122
        }
123
    }
124
125
    public function savepoint(string $name): void
126
    {
127
        $this->assertOpen();
128
        $this->stmtExec->rawCommand('SAVEPOINT ' . Types::serializeIdent($name));
129
        $this->txCtl->notifySavepointSaved($name);
130
    }
131
132
    public function rollbackToSavepoint(string $name): void
133
    {
134
        $this->assertOpen();
135
        $this->stmtExec->rawCommand('ROLLBACK TO SAVEPOINT ' . Types::serializeIdent($name));
136
        $this->txCtl->notifyRollbackToSavepoint($name);
137
    }
138
139
    public function releaseSavepoint(string $name): void
140
    {
141
        $this->assertOpen();
142
        $this->stmtExec->rawCommand('RELEASE SAVEPOINT ' . Types::serializeIdent($name));
143
        $this->txCtl->notifySavepointReleased($name);
144
    }
145
146
    public function prepareTransaction(?string $name = null): string
147
    {
148
        $this->assertOpen();
149
150
        if ($name === null) {
151
            $name = self::PREPARED_TX_RANDOM_NAME_PREFIX;
152
            $name .= StringUtils::randomHexString(self::PREPARED_TX_RANDOM_NAME_LEN - strlen($name));
153
        }
154
155
        $str = Types::serializeString($name);
156
        $this->stmtExec->rawCommand("PREPARE TRANSACTION $str");
157
        $this->open = false;
158
        $this->txCtl->notifyTransactionPrepared($name);
159
160
        return $name;
161
    }
162
}
163