3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2017 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
28 namespace Civi\Core\Transaction
;
33 * @copyright CiviCRM LLC (c) 2004-2017
37 private static $singleton = NULL;
45 * @var array<Frame> stack of SQL transactions/savepoints
47 private $frames = array();
52 private $savePointCount = 0;
58 public static function singleton($fresh = FALSE) {
59 if (NULL === self
::$singleton ||
$fresh) {
60 self
::$singleton = new Manager(new \
CRM_Core_DAO());
62 return self
::$singleton;
66 * @param \CRM_Core_DAO $dao
67 * Handle for the DB connection that will execute transaction statements.
68 * (all we really care about is the query() function)
70 public function __construct($dao) {
75 * Increment the transaction count / add a new transaction level
78 * Determines what to do if there's currently an active transaction:.
79 * - If true, then make a new nested transaction ("SAVEPOINT")
80 * - If false, then attach to the existing transaction
82 public function inc($nest = FALSE) {
83 if (!isset($this->frames
[0])) {
84 $frame = $this->createBaseFrame();
85 array_unshift($this->frames
, $frame);
90 $frame = $this->createSavePoint();
91 array_unshift($this->frames
, $frame);
96 $this->frames
[0]->inc();
101 * Decrement the transaction count / close out a transaction level
103 * @throws \CRM_Core_Exception
105 public function dec() {
106 if (!isset($this->frames
[0]) ||
$this->frames
[0]->isEmpty()) {
107 throw new \
CRM_Core_Exception('Transaction integrity error: Expected to find active frame');
110 $this->frames
[0]->dec();
112 if ($this->frames
[0]->isEmpty()) {
113 // Callbacks may cause additional work (such as new transactions),
114 // and it would be confusing if the old frame was still active.
115 // De-register it before calling finish().
116 $oldFrame = array_shift($this->frames
);
122 * Force an immediate rollback, regardless of how many
123 * transaction or frame objects exist.
125 * This is only appropriate when it is _certain_ that the
126 * callstack will not wind-down normally -- e.g. before
129 public function forceRollback() {
130 // we take the long-way-round (rolling back each frame) so that the
131 // internal state of each frame is consistent with its outcome
133 $oldFrames = $this->frames
;
134 $this->frames
= array();
135 foreach ($oldFrames as $oldFrame) {
136 $oldFrame->forceRollback();
141 * Get the (innermost) SQL transaction.
143 * @return \Civi\Core\Transaction\Frame
145 public function getFrame() {
146 return isset($this->frames
[0]) ?
$this->frames
[0] : NULL;
150 * Get the (outermost) SQL transaction (i.e. the one
151 * demarcated by BEGIN/COMMIT/ROLLBACK)
153 * @return \Civi\Core\Transaction\Frame
155 public function getBaseFrame() {
156 if (empty($this->frames
)) {
159 return $this->frames
[count($this->frames
) - 1];
163 * @return \Civi\Core\Transaction\Frame
165 protected function createBaseFrame() {
166 return new Frame($this->dao
, 'BEGIN', 'COMMIT', 'ROLLBACK');
170 * @return \Civi\Core\Transaction\Frame
172 protected function createSavePoint() {
173 $spId = $this->savePointCount++
;
174 return new Frame($this->dao
, "SAVEPOINT civi_{$spId}", NULL, "ROLLBACK TO SAVEPOINT civi_{$spId}");