Completed
Push — master ( 6166f0...a6b7bc )
by Thomas
02:39
created

Connection::setDepth()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
ccs 6
cts 6
cp 1
rs 9.6666
cc 3
eloc 6
nc 2
nop 1
crap 3
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
     * Set current depth.
70
     *
71
     * @param int $newDepth
72
     *   The new depth.
73
     *
74
     * @return int
75
     *   The old depth.
76
     */
77 3
    public function setDepth($newDepth)
78
    {
79 3
        if (!isset($newDepth) || $newDepth < 0) {
80 2
            throw new \RuntimeException('Trying to commit non-existant transaction.');
81
        }
82 3
        $oldDepth = $this->depth;
83 3
        $this->depth = $newDepth;
84 3
        return $oldDepth;
85
    }
86
87
    /**
88
     * Remove save points to and acquire index of latest active savepoint.
89
     *
90
     * @param int $oldDepth
91
     *   The old depth.
92
     * @param int $newDepth
93
     *   The new depth.
94
     *
95
     * @return int
96
     *   The index of the last open save point.
97
     */
98 2
    protected function closeSavePoints($oldDepth, $newDepth)
99
    {
100 2
        $idx = null;
101 2
        for ($depth = $newDepth + 1; $depth <= $oldDepth; $depth++) {
102 2
            if (isset($this->savePoints[$depth])) {
103 2
                $idx = isset($idx) ? $idx : $this->savePoints[$depth];
104 2
                unset($this->savePoints[$depth]);
105 2
            }
106 2
        }
107 2
        return $idx;
108
    }
109
110
    /**
111
     * Collection operations from the specified index.
112
     *
113
     * @param int $idx
114
     *   The starting index.
115
     *
116
     * @return Operation[]
117
     *   The operations from the specified index (included)
118
     */
119 2
    protected function collectOperations($idx)
120
    {
121
        // Collect the operations.
122 2
        $operations = [];
123 2
        end($this->operations);
124 2
        $lastIdx = key($this->operations);
125 2
        for ($removeIdx = $idx; $removeIdx <= $lastIdx; $removeIdx++) {
126 2
            if (isset($this->operations[$removeIdx])) {
127 2
                $operations[$removeIdx] = $this->operations[$removeIdx];
128 2
            }
129 2
        }
130 2
        reset($this->operations);
131 2
        return $operations;
132
    }
133
134
    /**
135
     * Run commit on operations and remove them from the buffer.
136
     *
137
     * @param Operation[] $operations
138
     *   The operations to commit.
139
     */
140 1
    protected function commitOperations($operations)
141
    {
142 1
        foreach ($operations as $operation) {
143 1
            $operation->commit($this);
144 1
            $this->removeOperation($operation);
145 1
        }
146 1
    }
147
148
    /**
149
     * Run commit on operations and remove them from the buffer.
150
     *
151
     * @param Operation[] $operations
152
     *   The operations to commit.
153
     */
154 1
    protected function rollbackOperations($operations)
155
    {
156 1
        foreach ($operations as $operation) {
157 1
            $operation->rollback($this);
158 1
            $this->removeOperation($operation);
159 1
        }
160 1
    }
161
162
    /**
163
     * Start transaction.
164
     *
165
     * @param int $newDepth
166
     *   (optional) If specified, use as new depth, otherwise increment current depth.
167
     *
168
     */
169 1
    public function startTransaction($newDepth = null)
170
    {
171 1
        $this->setDepth(isset($newDepth) ? $newDepth : $this->depth + 1);
172 1
        $this->savePoints[$this->depth] = $this->idx;
173 1
    }
174
175
    /**
176
     * Commit transaction.
177
     *
178
     * @param int $newDepth
179
     *   (optional) If specified, use as new depth, otherwise decrement current depth.
180
     *
181
     */
182 2
    public function commitTransaction($newDepth = null)
183
    {
184 2
        $oldDepth = $this->setDepth(isset($newDepth) ? $newDepth : $this->depth - 1);
185
186
        // Close save points and acquire last known open index.
187 2
        $idx = $this->closeSavePoints($oldDepth, $this->depth);
188
189
        // Is this a real commit.
190 2
        if ($this->depth == 0 && isset($idx)) {
191 2
            $operations = $this->collectOperations($idx);
192 2
            $this->commitOperations($operations);
193 2
        }
194 2
    }
195
196
    /**
197
     * Rollback transaction.
198
     *
199
     * @param int $newDepth
200
     *   (optional) If specified, use as new depth, otherwise decrement current depth.
201
     *
202
     */
203 2
    public function rollbackTransaction($newDepth = null)
204
    {
205 2
        $oldDepth = $this->setDepth(isset($newDepth) ? $newDepth : $this->depth - 1);
206
207
        // Close save points and acquire last known open index.
208 2
        $idx = $this->closeSavePoints($oldDepth, $this->depth);
209 2
        $operations = $this->collectOperations($idx);
210 2
        $this->rollbackOperations($operations);
211 2
    }
212
213
    /**
214
     * Add operation.
215
     *
216
     * @param Operation $operation
217
     *   The operation to add to the connection.
218
     *
219
     * @return Operation
220
     *   The operation added.
221
     */
222 1
    public function addOperation(Operation $operation)
223
    {
224 1
        if ($this->depth <= 0) {
225 1
            $operation->commit();
226 1
            return $operation;
227
        }
228 1
        $idx = $this->idx;
229 1
        $this->idx++;
230 1
        $this->operations[$idx] = $operation;
231 1
        $operation->setIdx($this, $idx);
232 1
        return $operation;
233
    }
234
235
    /**
236
     * Check if the connection has an operation.
237
     *
238
     * @param Operation $operation
239
     *   The operation to check for.
240
     *
241
     * @return bool
242
     *   TRUE if the operation exists.
243
     */
244 2
    public function hasOperation(Operation $operation)
245
    {
246 2
        return isset($this->operations[$operation->idx($this)]);
247
    }
248
249
    /**
250
     * Remove operation.
251
     *
252
     * @param Operation $operation
253
     *   The operation to remove from the connection.
254
     */
255 1
    public function removeOperation(Operation $operation)
256
    {
257 1
        unset($this->operations[$operation->idx($this)]);
258 1
    }
259
260
    /**
261
     * Short-hand notation for adding code to be run on commit.
262
     *
263
     * @param callable $callback
264
     *   The code to run on commit.
265
     *
266
     * @return Operation
267
     *   The operation created.
268
     */
269 1
    public function onCommit(callable $callback)
270
    {
271 1
        return $this->addOperation((new Operation())
272 1
            ->onCommit($callback));
273
    }
274
275
    /**
276
     * Short-hand notation for adding code to be run on rollback.
277
     *
278
     * @param callable $callback
279
     *   The code to run on rollback.
280
     *
281
     * @return Operation
282
     *   The operation created.
283
     */
284 1
    public function onRollback(callable $callback)
285
    {
286 1
        return $this->addOperation((new Operation())
287 1
            ->onRollback($callback));
288
    }
289
290
    /**
291
     * Short-hand notation for adding code to be run on rollback.
292
     *
293
     * @param mixed $value
294
     *   The value to add.
295
     *
296
     * @return Operation
297
     *   The operation created.
298
     */
299 1
    public function addValue($value)
300
    {
301 1
        return $this->addOperation((new Operation())
302 1
            ->setValue($value));
303
    }
304
}
305