Completed
Push — master ( c27d2d...a49ccc )
by Ondřej
03:50
created

TransactionControl::addObserver()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
namespace Ivory\Connection;
3
4
use Ivory\Exception\InternalException;
5
use Ivory\Exception\InvalidStateException;
6
use Ivory\Result\IQueryResult;
7
use Ivory\Type\Std\StringType;
8
9
class TransactionControl implements IObservableTransactionControl
10
{
11
    private $connCtl;
12
    private $stmtExec;
13
    /** @var ITransactionControlObserver[] */
14
    private $observers = [];
15
16
17
    public function __construct(ConnectionControl $connCtl, IStatementExecution $stmtExec)
18
    {
19
        $this->connCtl = $connCtl;
20
        $this->stmtExec = $stmtExec;
21
    }
22
23
    public function inTransaction(): bool
24
    {
25
        $connHandler = $this->connCtl->requireConnection();
26
        $txStat = pg_transaction_status($connHandler);
27
        return ($txStat == PGSQL_TRANSACTION_INTRANS || $txStat == PGSQL_TRANSACTION_INERROR);
28
    }
29
30
    public function startTransaction($transactionOptions = 0): ITxHandle
31
    {
32
        if ($this->inTransaction()) {
33
            throw new InvalidStateException('A transaction is already active, cannot start a new one.');
34
        }
35
36
        $txConfig = TxConfig::create($transactionOptions);
37
        $txSql = $txConfig->toSql();
38
39
        $command = 'START TRANSACTION';
40
        if (strlen($txSql) > 0) {
41
            $command .= ' ' . $txSql;
42
        }
43
44
        $this->stmtExec->rawCommand($command);
45
        $this->notifyTransactionStart();
46
47
        return new TxHandle($this->stmtExec, $this); // TODO: use a factory method, abstracting from the specific class
48
    }
49
50
    public function setupSubsequentTransactions($transactionOptions)
51
    {
52
        $txConfig = TxConfig::create($transactionOptions);
53
        $this->stmtExec->rawCommand('SET SESSION CHARACTERISTICS AS TRANSACTION ' . $txConfig->toSql());
54
    }
55
56 View Code Duplication
    public function commitPreparedTransaction(string $name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
57
    {
58
        if ($this->inTransaction()) {
59
            throw new InvalidStateException('Cannot commit a prepared transaction while inside another transaction.');
60
        }
61
62
        $this->stmtExec->rawCommand("COMMIT PREPARED {$this->quoteString($name)}");
63
        $this->notifyPreparedTransactionCommit($name);
64
    }
65
66 View Code Duplication
    public function rollbackPreparedTransaction(string $name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
67
    {
68
        if ($this->inTransaction()) {
69
            throw new InvalidStateException('Cannot rollback a prepared transaction while inside another transaction.');
70
        }
71
72
        $this->stmtExec->rawCommand("ROLLBACK PREPARED {$this->quoteString($name)}");
73
        $this->notifyPreparedTransactionRollback($name);
74
    }
75
76
    public function listPreparedTransactions(): IQueryResult
77
    {
78
        return $this->stmtExec->rawQuery('SELECT * FROM pg_catalog.pg_prepared_xacts');
79
    }
80
81
    private function quoteString(string $str)
82
    {
83
        static $stringSerializer = null;
84
        if ($stringSerializer === null) {
85
            $stringSerializer = new StringType('pg_catalog', 'text');
86
        }
87
        return $stringSerializer->serializeValue($str);
88
    }
89
90
91
    //region IObservableTransactionControl
92
93
    public function addObserver(ITransactionControlObserver $observer)
94
    {
95
        $hash = spl_object_hash($observer);
96
        $this->observers[$hash] = $observer;
97
    }
98
99
    public function removeObserver(ITransactionControlObserver $observer)
100
    {
101
        $hash = spl_object_hash($observer);
102
        unset($this->observers[$hash]);
103
    }
104
105
    public function removeAllObservers()
106
    {
107
        $this->observers = [];
108
    }
109
110
    public function notifyTransactionStart()
111
    {
112
        foreach ($this->observers as $observer) {
113
            $observer->handleTransactionStart();
114
        }
115
    }
116
117
    public function notifyTransactionCommit()
118
    {
119
        foreach ($this->observers as $observer) {
120
            $observer->handleTransactionCommit();
121
        }
122
    }
123
124
    public function notifyTransactionRollback()
125
    {
126
        foreach ($this->observers as $observer) {
127
            $observer->handleTransactionRollback();
128
        }
129
    }
130
131
    public function notifySavepointSaved(string $name)
132
    {
133
        foreach ($this->observers as $observer) {
134
            $observer->handleSavepointSaved($name);
135
        }
136
    }
137
138
    public function notifySavepointReleased(string $name)
139
    {
140
        foreach ($this->observers as $observer) {
141
            $observer->handleSavepointReleased($name);
142
        }
143
    }
144
145
    public function notifyRollbackToSavepoint(string $name)
146
    {
147
        foreach ($this->observers as $observer) {
148
            $observer->handleRollbackToSavepoint($name);
149
        }
150
    }
151
152
    public function notifyTransactionPrepared(string $name)
153
    {
154
        foreach ($this->observers as $observer) {
155
            $observer->handleTransactionPrepared($name);
156
        }
157
    }
158
159
    public function notifyPreparedTransactionCommit(string $name)
160
    {
161
        foreach ($this->observers as $observer) {
162
            $observer->handlePreparedTransactionCommit($name);
163
        }
164
    }
165
166
    public function notifyPreparedTransactionRollback(string $name)
167
    {
168
        foreach ($this->observers as $observer) {
169
            $observer->handlePreparedTransactionRollback($name);
170
        }
171
    }
172
173
    //endregion
174
}
175