Completed
Push — master ( dcf8c5...fc4d1a )
by Thomas
02:39
created

Connection::collectOperations()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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