Commit | Line | Data |
---|---|---|
5bb8417a TO |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
41498ac5 | 4 | | Copyright CiviCRM LLC. All rights reserved. | |
5bb8417a | 5 | | | |
41498ac5 TO |
6 | | This work is published under the GNU AGPLv3 license with some | |
7 | | permitted exceptions and without any warranty. For full license | | |
8 | | and copyright information, see https://civicrm.org/licensing | | |
5bb8417a | 9 | +--------------------------------------------------------------------+ |
d25dd0ee | 10 | */ |
5bb8417a TO |
11 | |
12 | namespace Civi\Core\Transaction; | |
13 | ||
14 | /** | |
15 | * A "frame" is a layer in a series of nested transactions. Generally, | |
16 | * the outermost frame is a normal SQL transaction (BEGIN/ROLLBACK/COMMIT) | |
17 | * and any nested frames are SQL savepoints (SAVEPOINT foo/ROLLBACK TO SAVEPOINT). | |
18 | * | |
19 | * @package Civi | |
ca5cec67 | 20 | * @copyright CiviCRM LLC https://civicrm.org/licensing |
5bb8417a TO |
21 | */ |
22 | class Frame { | |
23 | ||
24 | const F_NEW = 0, F_ACTIVE = 1, F_DONE = 2, F_FORCED = 3; | |
25 | ||
26 | /** | |
27 | * @var \CRM_Core_DAO | |
28 | */ | |
29 | private $dao; | |
30 | ||
31 | /** | |
32 | * @var string|null e.g. "BEGIN" or "SAVEPOINT foo" | |
33 | */ | |
34 | private $beginStmt; | |
35 | ||
36 | /** | |
37 | * @var string|null e.g. "COMMIT" | |
38 | */ | |
39 | private $commitStmt; | |
40 | ||
41 | /** | |
42 | * @var string|null e.g. "ROLLBACK" or "ROLLBACK TO SAVEPOINT foo" | |
43 | */ | |
44 | private $rollbackStmt; | |
45 | ||
46 | /** | |
47 | * @var int | |
48 | */ | |
49 | private $refCount = 0; | |
50 | private $callbacks; | |
51 | private $doCommit = TRUE; | |
52 | ||
53 | /** | |
54 | * @var int | |
55 | */ | |
56 | private $state = self::F_NEW; | |
57 | ||
58 | /** | |
59 | * @param \CRM_Core_DAO $dao | |
60 | * @param string|null $beginStmt e.g. "BEGIN" or "SAVEPOINT foo" | |
61 | * @param string|null $commitStmt e.g. "COMMIT" | |
62 | * @param string|null $rollbackStmt e.g. "ROLLBACK" or "ROLLBACK TO SAVEPOINT foo" | |
63 | */ | |
64 | public function __construct($dao, $beginStmt, $commitStmt, $rollbackStmt) { | |
65 | $this->dao = $dao; | |
66 | $this->beginStmt = $beginStmt; | |
67 | $this->commitStmt = $commitStmt; | |
68 | $this->rollbackStmt = $rollbackStmt; | |
69 | ||
c64f69d9 CW |
70 | $this->callbacks = [ |
71 | \CRM_Core_Transaction::PHASE_PRE_COMMIT => [], | |
72 | \CRM_Core_Transaction::PHASE_POST_COMMIT => [], | |
73 | \CRM_Core_Transaction::PHASE_PRE_ROLLBACK => [], | |
74 | \CRM_Core_Transaction::PHASE_POST_ROLLBACK => [], | |
75 | ]; | |
5bb8417a TO |
76 | } |
77 | ||
78 | public function inc() { | |
79 | $this->refCount++; | |
80 | } | |
81 | ||
82 | public function dec() { | |
83 | $this->refCount--; | |
84 | } | |
85 | ||
7fe37828 EM |
86 | /** |
87 | * @return bool | |
88 | */ | |
5bb8417a TO |
89 | public function isEmpty() { |
90 | return ($this->refCount == 0); | |
91 | } | |
92 | ||
7fe37828 EM |
93 | /** |
94 | * @return bool | |
95 | */ | |
5bb8417a TO |
96 | public function isRollbackOnly() { |
97 | return !$this->doCommit; | |
98 | } | |
99 | ||
100 | public function setRollbackOnly() { | |
46bcf597 | 101 | $this->doCommit = FALSE; |
5bb8417a TO |
102 | } |
103 | ||
0dbbb8e7 | 104 | /** |
105 | * Begin frame processing. | |
106 | * | |
107 | * @throws \CRM_Core_Exception | |
108 | */ | |
5bb8417a | 109 | public function begin() { |
0dbbb8e7 | 110 | if ($this->state !== self::F_NEW) { |
111 | throw new \CRM_Core_Exception('State is not F_NEW'); | |
112 | }; | |
113 | ||
5bb8417a TO |
114 | $this->state = self::F_ACTIVE; |
115 | if ($this->beginStmt) { | |
116 | $this->dao->query($this->beginStmt); | |
117 | } | |
118 | } | |
119 | ||
120 | /** | |
0dbbb8e7 | 121 | * Finish frame processing. |
122 | * | |
2241036a | 123 | * @param int $newState |
0dbbb8e7 | 124 | * |
125 | * @throws \CRM_Core_Exception | |
5bb8417a TO |
126 | */ |
127 | public function finish($newState = self::F_DONE) { | |
128 | if ($this->state == self::F_FORCED) { | |
129 | return; | |
130 | } | |
0dbbb8e7 | 131 | if ($this->state !== self::F_ACTIVE) { |
132 | throw new \CRM_Core_Exception('State is not F_ACTIVE'); | |
133 | }; | |
134 | ||
5bb8417a TO |
135 | $this->state = $newState; |
136 | ||
137 | if ($this->doCommit) { | |
138 | $this->invokeCallbacks(\CRM_Core_Transaction::PHASE_PRE_COMMIT); | |
139 | if ($this->commitStmt) { | |
140 | $this->dao->query($this->commitStmt); | |
141 | } | |
142 | $this->invokeCallbacks(\CRM_Core_Transaction::PHASE_POST_COMMIT); | |
143 | } | |
144 | else { | |
145 | $this->invokeCallbacks(\CRM_Core_Transaction::PHASE_PRE_ROLLBACK); | |
146 | if ($this->rollbackStmt) { | |
147 | $this->dao->query($this->rollbackStmt); | |
148 | } | |
149 | $this->invokeCallbacks(\CRM_Core_Transaction::PHASE_POST_ROLLBACK); | |
150 | } | |
151 | } | |
152 | ||
153 | public function forceRollback() { | |
154 | $this->setRollbackOnly(); | |
155 | $this->finish(self::F_FORCED); | |
156 | } | |
157 | ||
158 | /** | |
fe482240 | 159 | * Add a transaction callback. |
5bb8417a TO |
160 | * |
161 | * Pre-condition: isActive() | |
162 | * | |
04855556 TO |
163 | * @param int $phase |
164 | * A constant; one of: self::PHASE_{PRE,POST}_{COMMIT,ROLLBACK}. | |
165 | * @param mixed $callback | |
166 | * A PHP callback. | |
5bb8417a TO |
167 | * @param array|NULL $params Optional values to pass to callback. |
168 | * See php manual call_user_func_array for details. | |
257e7666 | 169 | * @param null $id |
5bb8417a | 170 | */ |
46bcf597 | 171 | public function addCallback($phase, $callback, $params = NULL, $id = NULL) { |
5bb8417a | 172 | if ($id) { |
c64f69d9 | 173 | $this->callbacks[$phase][$id] = [ |
5bb8417a | 174 | 'callback' => $callback, |
c64f69d9 CW |
175 | 'parameters' => (is_array($params) ? $params : [$params]), |
176 | ]; | |
5bb8417a TO |
177 | } |
178 | else { | |
c64f69d9 | 179 | $this->callbacks[$phase][] = [ |
5bb8417a | 180 | 'callback' => $callback, |
c64f69d9 CW |
181 | 'parameters' => (is_array($params) ? $params : [$params]), |
182 | ]; | |
5bb8417a TO |
183 | } |
184 | } | |
185 | ||
186 | /** | |
187 | * @param int $phase | |
188 | */ | |
189 | public function invokeCallbacks($phase) { | |
190 | if (is_array($this->callbacks[$phase])) { | |
191 | foreach ($this->callbacks[$phase] as $cb) { | |
192 | call_user_func_array($cb['callback'], $cb['parameters']); | |
193 | } | |
194 | } | |
195 | } | |
196 | ||
197 | } |