Completed
Push — master ( 14a10b...459243 )
by Thomas
02:42
created

Connection::onRemove()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
ccs 3
cts 3
cp 1
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Gielfeldt\TransactionalPHP;
4
5
/**
6
 * Class Connection
7
 *
8
 * @package Gielfeldt\TransactionalPHP
9
 */
10
class Connection
11
{
12
    /**
13
     * Operations buffer.
14
     *
15
     * @var Operation[]
16
     */
17
    protected $operations = [];
18
19
    /**
20
     * Current index in operations buffer.
21
     *
22
     * @var int
23
     */
24
    protected $idx = 0;
25
26
    /**
27
     * Save points in the buffer, indexed by depth.
28
     *
29
     * @var int[]
30
     */
31
    protected $savePoints = [];
32
33
    /**
34
     * Current depth.
35
     *
36
     * @var int
37
     */
38
    protected $depth = 0;
39
40
    /**
41
     * The id of the connection.
42
     *
43
     * @var null|string
44
     */
45
    protected $connectionId;
46
47
    /**
48
     * Connection constructor.
49
     *
50
     * @param null|string $connectionId
51
     *   (optional) The id of the connection.
52
     */
53 1
    public function __construct($connectionId = null)
54
    {
55 1
        $this->connectionId = isset($connectionId) ? $connectionId : uniqid();
56 1
    }
57
58
    /**
59
     * Get connection id.
60
     *
61
     * @return null|string
62
     */
63 9
    public function connectionId()
64
    {
65 9
        return $this->connectionId;
66
    }
67
68
    /**
69
     * Get current depth.
70
     *
71
     * @return int
72
     */
73 2
    public function getDepth()
74
    {
75 2
        return $this->depth;
76
    }
77
78
    /**
79
     * Set current depth.
80
     *
81
     * @param int $newDepth
82
     *   The new depth.
83
     *
84
     * @return int
85
     *   The old depth.
86
     */
87 11
    public function setDepth($newDepth)
88
    {
89 11
        if (!isset($newDepth) || $newDepth < 0) {
90 2
            throw new \RuntimeException('Trying to commit nonexistent transaction.');
91
        }
92 11
        $oldDepth = $this->depth;
93 11
        $this->depth = $newDepth;
94 11
        return $oldDepth;
95
    }
96
97
98
    /**
99
     * Add operation.
100
     *
101
     * @param Operation $operation
102
     *   The operation to add to the connection.
103
     *
104
     * @return Operation
105
     *   The operation added.
106
     */
107 9
    public function addOperation(Operation $operation)
108
    {
109 9
        if ($this->depth <= 0) {
110 5
            $operation->commit($this);
111 5
            return $operation;
112
        }
113 8
        $idx = $this->idx;
114 8
        $this->idx++;
115 8
        $this->operations[$idx] = $operation;
116 8
        $operation->setIdx($this, $idx);
117 8
        $operation->buffer($this);
118 8
        return $operation;
119
    }
120
121
    /**
122
     * Check if the connection has an operation.
123
     *
124
     * @param Operation $operation
125
     *   The operation to check for.
126
     *
127
     * @return bool
128
     *   TRUE if the operation exists.
129
     */
130 4
    public function hasOperation(Operation $operation)
131
    {
132 4
        return isset($this->operations[$operation->idx($this)]);
133
    }
134
135
    /**
136
     * Remove operation.
137
     *
138
     * @param Operation $operation
139
     *   The operation to remove from the connection.
140
     */
141 7
    public function removeOperation(Operation $operation)
142
    {
143 7
        $operation->remove($this);
144 7
        unset($this->operations[$operation->idx($this)]);
145 7
    }
146
147
    /**
148
     * Short-hand notation for adding code to be run on commit.
149
     *
150
     * @param callable $callback
151
     *   The code to run on commit.
152
     *
153
     * @return Operation
154
     *   The operation created.
155
     */
156 1
    public function onCommit(callable $callback)
157
    {
158 1
        return $this->addOperation((new Operation())
159 1
            ->onCommit($callback));
160
    }
161
162
    /**
163
     * Short-hand notation for adding code to be run on rollback.
164
     *
165
     * @param callable $callback
166
     *   The code to run on rollback.
167
     *
168
     * @return Operation
169
     *   The operation created.
170
     */
171 1
    public function onRollback(callable $callback)
172
    {
173 1
        return $this->addOperation((new Operation())
174 1
            ->onRollback($callback));
175
    }
176
177
    /**
178
     * Short-hand notation for adding code to be run when removed from buffer.
179
     *
180
     * @param callable $callback
181
     *   The code to run on removal from buffer.
182
     *
183
     * @return Operation
184
     *   The operation created.
185
     */
186 1
    public function onRemove(callable $callback)
187
    {
188 1
        return $this->addOperation((new Operation())
189 1
            ->onRemove($callback));
190
    }
191
192
    /**
193
     * Short-hand notation for adding metadata to operation.
194
     *
195
     * @param string $key
196
     *   The key of the metadata to add.
197
     * @param mixed $value
198
     *   The value to add.
199
     *
200
     * @return Operation
201
     *   The operation created.
202
     */
203 1
    public function addMetadata($key, $value)
204
    {
205 1
        return $this->addOperation((new Operation())
206 1
            ->setMetadata($key, $value));
207
    }
208
209
    /**
210
     * Start transaction.
211
     *
212
     * @param int $newDepth
213
     *   (optional) If specified, use as new depth, otherwise increment current depth.
214
     *
215
     */
216 11
    public function startTransaction($newDepth = null)
217
    {
218 11
        $this->setDepth(isset($newDepth) ? $newDepth : $this->depth + 1);
219 11
        $this->savePoints[$this->depth] = $this->idx;
220 11
    }
221
222
    /**
223
     * Commit transaction.
224
     *
225
     * @param int $newDepth
226
     *   (optional) If specified, use as new depth, otherwise decrement current depth.
227
     *
228
     */
229 6
    public function commitTransaction($newDepth = null)
230
    {
231 6
        $oldDepth = $this->setDepth(isset($newDepth) ? $newDepth : $this->depth - 1);
232
233
        // Close save points and acquire last known open index.
234 6
        $idx = $this->closeSavePoints($oldDepth, $this->depth);
235
236
        // Is this a real commit.
237 6
        if ($this->depth == 0 && isset($idx)) {
238 6
            $operations = $this->collectOperations($idx);
239 6
            $this->commitOperations($operations);
240 6
        }
241 6
    }
242
243
    /**
244
     * Rollback transaction.
245
     *
246
     * @param int $newDepth
247
     *   (optional) If specified, use as new depth, otherwise decrement current depth.
248
     *
249
     */
250 6
    public function rollbackTransaction($newDepth = null)
251
    {
252 6
        $oldDepth = $this->setDepth(isset($newDepth) ? $newDepth : $this->depth - 1);
253
254
        // Close save points and acquire last known open index.
255 6
        $idx = $this->closeSavePoints($oldDepth, $this->depth);
256 6
        $operations = $this->collectOperations($idx);
257 6
        $this->rollbackOperations($operations);
258 6
    }
259
260
    /**
261
     * Remove save points to and acquire index of latest active save point.
262
     *
263
     * @param int $oldDepth
264
     *   The old depth.
265
     * @param int $newDepth
266
     *   The new depth.
267
     *
268
     * @return int
269
     *   The index of the latest active save point.
270
     */
271 9
    protected function closeSavePoints($oldDepth, $newDepth)
272
    {
273 9
        $idx = null;
274 9
        for ($depth = $newDepth + 1; $depth <= $oldDepth; $depth++) {
275 9
            if (isset($this->savePoints[$depth])) {
276 9
                $idx = isset($idx) ? $idx : $this->savePoints[$depth];
277 9
                unset($this->savePoints[$depth]);
278 9
            }
279 9
        }
280 9
        return $idx;
281
    }
282
283
    /**
284
     * Collection operations from the specified index.
285
     *
286
     * @param int $idx
287
     *   The starting index.
288
     *
289
     * @return Operation[]
290
     *   The operations from the specified index (included)
291
     */
292 9
    protected function collectOperations($idx)
293
    {
294
        // Collect the operations.
295 9
        $operations = [];
296 9
        end($this->operations);
297 9
        $lastIdx = key($this->operations);
298 9
        for ($removeIdx = $idx; $removeIdx <= $lastIdx; $removeIdx++) {
299 9
            if (isset($this->operations[$removeIdx])) {
300 6
                $operations[$removeIdx] = $this->operations[$removeIdx];
301 6
            }
302 9
        }
303 9
        reset($this->operations);
304 9
        return $operations;
305
    }
306
307
    /**
308
     * Run commit on operations and remove them from the buffer.
309
     *
310
     * @param Operation[] $operations
311
     *   The operations to commit.
312
     */
313 6
    protected function commitOperations($operations)
314
    {
315 6
        foreach ($operations as $operation) {
316 3
            $operation->commit($this);
317 3
            $this->removeOperation($operation);
318 6
        }
319 6
    }
320
321
    /**
322
     * Run rollback on operations and remove them from the buffer.
323
     *
324
     * @param Operation[] $operations
325
     *   The operations to rollback.
326
     */
327 6
    protected function rollbackOperations($operations)
328
    {
329 6
        foreach ($operations as $operation) {
330 4
            $operation->rollback($this);
331 4
            $this->removeOperation($operation);
332 6
        }
333 6
    }
334
}
335