Completed
Push — master ( eb2a9a...0ad9ec )
by Thomas
02:47
created

Connection::commitOperations()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
ccs 6
cts 6
cp 1
rs 9.4285
cc 2
eloc 4
nc 2
nop 1
crap 2
1
<?php
2
3
namespace Gielfeldt\TransactionalPHP;
4
5
/**
6
 * Class Connection
7
 *
8
 * @package Gielfeldt\TransactionalPHP
9
 */
10
class Connection
11
{
12
    /**
13
     * @var Operation[]
14
     */
15
    protected $operations = [];
16
17
    /**
18
     * @var int
19
     */
20
    protected $idx = 0;
21
22
    /**
23
     * @var int[]
24
     */
25
    protected $savePoints = [];
26
27
    /**
28
     * @var int
29
     */
30
    protected $depth = 0;
31
32
    /**
33
     * @var null|string
34
     */
35
    protected $connectionId;
36
37
    /**
38
     * Connection constructor.
39
     *
40
     * @param null|string $connectionId
41
     *   (optional) The id of the connection.
42
     */
43 1
    public function __construct($connectionId = null)
44
    {
45 1
        $this->connectionId = isset($connectionId) ? $connectionId : uniqid();
46 1
    }
47
48
    /**
49
     * Get connection id.
50
     *
51
     * @return null|string
52
     */
53 1
    public function connectionId()
54
    {
55 1
        return $this->connectionId;
56
    }
57
58
    /**
59
     * Get current depth.
60
     *
61
     * @return int
62
     */
63 1
    public function getDepth()
64
    {
65 1
        return $this->depth;
66
    }
67
68
    /**
69
     * Remove save points to and acquire index of latest active savepoint.
70
     *
71
     * @param int $oldDepth
72
     *   The old depth.
73
     * @param $newDepth
74
     *   The new depth.
75
     *
76
     * @return Operation[]
77
     *   The operations found when closing save points.
78
     */
79 2
    protected function closeSavePoints($oldDepth, $newDepth)
80
    {
81 2
        $idx = null;
82 2
        for ($depth = $newDepth + 1; $depth <= $oldDepth; $depth++) {
83 2
            if (isset($this->savePoints[$depth])) {
84 2
                $idx = isset($idx) ? $idx : $this->savePoints[$depth];
85 2
                unset($this->savePoints[$depth]);
86 2
            }
87 2
        }
88
89 2
        $operations = [];
90
91
        // Collect the operations.
92 2
        if (isset($idx)) {
93 2
            end($this->operations);
94 2
            $lastIdx = key($this->operations);
95 2
            for ($removeIdx = $idx; $removeIdx <= $lastIdx; $removeIdx++) {
96 2
                if (isset($this->operations[$removeIdx])) {
97 2
                    $operations[$removeIdx] = $this->operations[$removeIdx];
98 2
                }
99 2
            }
100 2
            reset($this->operations);
101 2
        }
102 2
        return $operations;
103
    }
104
105
    /**
106
     * Run commit on operations and remove them from the buffer.
107
     *
108
     * @param Operation[]
109
     *   The operations to commit.
110
     */
111 1
    protected function commitOperations($operations) {
112 1
        foreach ($operations as $operation) {
113 1
            $operation->commit($this);
114 1
            $this->removeOperation($operation);
115 1
        }
116 1
    }
117
118
    /**
119
     * Run commit on operations and remove them from the buffer.
120
     *
121
     * @param Operation[]
122
     *   The operations to commit.
123
     */
124 1
    protected function rollbackOperations($operations) {
125 1
        foreach ($operations as $operation) {
126 1
            $operation->rollback($this);
127 1
            $this->removeOperation($operation);
128 1
        }
129 1
    }
130
131
    /**
132
     * Start transaction.
133
     *
134
     * @param int $newDepth
135
     *   (optional) If specified, use as new depth, otherwise increment current depth.
136
     *
137
     */
138 1
    public function startTransaction($newDepth = null)
139
    {
140 1
        $this->depth = isset($newDepth) ? $newDepth : $this->depth + 1;
141 1
        $this->savePoints[$this->depth] = $this->idx;
142 1
    }
143
144
    /**
145
     * Commit transaction.
146
     *
147
     * @param int $newDepth
148
     *   (optional) If specified, use as new depth, otherwise decrement current depth.
149
     *
150
     */
151 2
    public function commitTransaction($newDepth = null)
152
    {
153 2
        $oldDepth = $this->depth;
154 2
        $this->depth = isset($newDepth) ? $newDepth : $oldDepth - 1;
155 2
        if ($this->depth < 0) {
156 1
            throw new \RuntimeException('Trying to commit non-existant transaction.');
157
        }
158
159
        // Collect operations and commit if applicable.
160 2
        $operations = $this->closeSavePoints($oldDepth, $this->depth);
161
162
        // Is this a real commit.
163 2
        if ($this->depth == 0 && $operations) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $operations of type Gielfeldt\TransactionalPHP\Operation[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
164 1
            $this->commitOperations($operations);
165 1
        }
166 2
    }
167
168
    /**
169
     * Rollback transaction.
170
     *
171
     * @param int $newDepth
172
     *   (optional) If specified, use as new depth, otherwise decrement current depth.
173
     *
174
     */
175 2
    public function rollbackTransaction($newDepth = null)
176
    {
177 2
        $oldDepth = $this->depth;
178 2
        $this->depth = isset($newDepth) ? $newDepth : $oldDepth - 1;
179 2
        if ($this->depth < 0) {
180 1
            throw new \RuntimeException('Trying to rollback non-existant transaction.');
181
        }
182
183
        // Collect operations and rollback.
184 2
        $operations = $this->closeSavePoints($oldDepth, $this->depth);
185 2
        $this->rollbackOperations($operations);
186 2
    }
187
188
    /**
189
     * Add operation.
190
     *
191
     * @param Operation $operation
192
     *   The operation to add to the connection.
193
     *
194
     * @return Operation
195
     *   The operation added.
196
     */
197 1
    public function addOperation(Operation $operation)
198
    {
199 1
        if ($this->depth <= 0) {
200 1
            $operation->commit();
201 1
            return $operation;
202
        }
203 1
        $idx = $this->idx;
204 1
        $this->idx++;
205 1
        $this->operations[$idx] = $operation;
206 1
        $operation->setIdx($this, $idx);
207 1
        return $operation;
208
    }
209
210
    /**
211
     * Check if the connection has an operation.
212
     *
213
     * @param Operation $operation
214
     *   The operation to check for.
215
     *
216
     * @return bool
217
     *   TRUE if the operation exists.
218
     */
219 2
    public function hasOperation(Operation $operation)
220
    {
221 2
        return isset($this->operations[$operation->idx($this)]);
222
    }
223
224
    /**
225
     * Remove operation.
226
     *
227
     * @param Operation $operation
228
     *   The operation to remove from the connection.
229
     */
230 1
    public function removeOperation(Operation $operation)
231
    {
232 1
        unset($this->operations[$operation->idx($this)]);
233 1
    }
234
235
    /**
236
     * Short-hand notation for adding code to be run on commit.
237
     *
238
     * @param callable $callback
239
     *   The code to run on commit.
240
     *
241
     * @return Operation
242
     *   The operation created.
243
     */
244 1
    public function onCommit(callable $callback)
245
    {
246 1
        return $this->addOperation((new Operation())
247 1
            ->onCommit($callback));
248
    }
249
250
    /**
251
     * Short-hand notation for adding code to be run on rollback.
252
     *
253
     * @param callable $callback
254
     *   The code to run on rollback.
255
     *
256
     * @return Operation
257
     *   The operation created.
258
     */
259 1
    public function onRollback(callable $callback)
260
    {
261 1
        return $this->addOperation((new Operation())
262 1
            ->onRollback($callback));
263
    }
264
265
    /**
266
     * Short-hand notation for adding code to be run on rollback.
267
     *
268
     * @param mixed $value
269
     *   The value to add.
270
     *
271
     * @return Operation
272
     *   The operation created.
273
     */
274 1
    public function addValue($value)
275
    {
276 1
        return $this->addOperation((new Operation())
277 1
            ->setValue($value));
278
    }
279
}
280