Completed
Push — master ( acafb9...b81c81 )
by Thomas
02:06
created

Connection::call()   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 1
Metric Value
c 1
b 0
f 1
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
     * @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
     * Remove savepoints to and acquire index of latest active savepoint.
60
     *
61
     * @param int $oldDepth
62
     *   The old depth.
63
     * @param $newDepth
64
     *   The new depth.
65
     *
66
     * @return int|null
67
     *   The last index, if found.
68
     */
69 2
    public function closeSavepoints($oldDepth, $newDepth)
70
    {
71 2
        $idx = null;
72 2
        for ($depth = $newDepth + 1; $depth <= $oldDepth; $depth++) {
73 2
            if (isset($this->savePoints[$depth])) {
74 2
                $idx = isset($idx) ? $idx : $this->savePoints[$depth];
75 2
                unset($this->savePoints[$depth]);
76
            }
77
        }
78 2
        return $idx;
79
    }
80
81
    /**
82
     * {@inheritdoc}
83
     */
84 1
    public function startTransaction($newDepth = null)
85
    {
86 1
        $this->depth = isset($newDepth) ? $newDepth : $this->depth + 1;
87 1
        $this->savePoints[$this->depth] = $this->idx;
88 1
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93 2
    public function commitTransaction($newDepth = null)
94
    {
95 2
        $oldDepth = $this->depth;
96 2
        $this->depth = isset($newDepth) ? $newDepth : $oldDepth - 1;
97 2
        if ($this->depth < 0) {
98 1
            throw new \RuntimeException('Trying to commit non-existant transaction.');
99
        }
100
101
        // Remove savepoints to and acquire index of latest active savepoint.
102 2
        $idx = $this->closeSavepoints($oldDepth, $this->depth);
103
104
        // Is this a real commit.
105 2
        if ($this->depth == 0 && isset($idx)) {
106
            // Perform the operations if any found.
107 2
            end($this->operations);
108 2
            $lastIdx = key($this->operations);
109 2
            for ($removeIdx = $idx; $removeIdx <= $lastIdx; $removeIdx++) {
110 2
                $this->performOperation($removeIdx);
111 2
                $this->removeOperation($removeIdx);
112
            }
113 2
            $this->idx = $idx;
114
        }
115 2
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120 2
    public function rollbackTransaction($newDepth = null)
121
    {
122 2
        $oldDepth = $this->depth;
123 2
        $this->depth = isset($newDepth) ? $newDepth : $oldDepth - 1;
124 2
        if ($this->depth < 0) {
125 1
            throw new \RuntimeException('Trying to rollback non-existant transaction.');
126
        }
127
128
        // Remove savepoints to and acquire index of latest active savepoint.
129 2
        $idx = $this->closeSavepoints($oldDepth, $this->depth);
130
131
        // Remove operations up until latest active savepoint.
132 2
        if (isset($idx)) {
133 2
            end($this->operations);
134 2
            $lastIdx = key($this->operations);
135 2
            for ($removeIdx = $idx; $removeIdx <= $lastIdx; $removeIdx++) {
136 2
                $this->removeOperation($removeIdx);
137
            }
138 2
            reset($this->operations);
139
        }
140 2
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145 1
    public function addOperation(Operation $operation)
146
    {
147 1
        if ($this->depth <= 0) {
148 1
            return $operation->execute();
149
        }
150
151 1
        $idx = $this->idx;
152 1
        $this->idx++;
153 1
        $this->operations[$idx] = $operation;
154 1
        $operation->setIdx($this, $idx);
155 1
        return $idx;
156
    }
157
158
    /**
159
     * {@inheritdoc}
160
     */
161 1
    public function getOperation($idx)
162
    {
163 1
        return isset($this->operations[$idx]) ? $this->operations[$idx] : false;
164
    }
165
166
    /**
167
     * {@inheritdoc}
168
     */
169 1
    public function performOperation($idx)
170
    {
171 1
        return isset($this->operations[$idx]) ? $this->operations[$idx]->execute() : null;
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177 1
    public function removeOperation($idx)
178
    {
179 1
        unset($this->operations[$idx]);
180 1
    }
181
182
    /**
183
     * Short-hand notation for adding running code.
184
     *
185
     * @param callable $callback
186
     *   The code to run.
187
     *
188
     * @return int|mixed
189
     */
190 1
    public function call(callable $callback)
191
    {
192 1
        return $this->addOperation((new Operation())
193 1
            ->setCallback($callback));
194
    }
195
}
196