CRM/Report add missing comment blocks
[civicrm-core.git] / CRM / Core / Transaction.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
06b69b18 4 | CiviCRM version 4.5 |
6a488035 5 +--------------------------------------------------------------------+
06b69b18 6 | Copyright CiviCRM LLC (c) 2004-2014 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
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. |
13 | |
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. |
18 | |
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 +--------------------------------------------------------------------+
26*/
27
28/**
29 *
30 * @package CRM
06b69b18 31 * @copyright CiviCRM LLC (c) 2004-2014
6a488035
TO
32 * @copyright David Strauss <david@fourkitchens.com> (c) 2007
33 * $Id$
34 *
35 * This file has its origins in Donald Lobo's conversation with David
36 * Strauss over IRC and the CRM_Core_DAO::transaction() function.
37 *
38 * David went on and abstracted this into a class which can be used in PHP 5
39 * (since destructors are called automagically at the end of the script).
40 * Lobo modified the code and used CiviCRM coding standards. David's
41 * PressFlow Transaction module is available at
42 * http://drupal.org/project/pressflow_transaction
43 */
44class CRM_Core_Transaction {
45
46 /**
47 * These constants represent phases at which callbacks can be invoked
48 */
49 CONST PHASE_PRE_COMMIT = 1;
50 CONST PHASE_POST_COMMIT = 2;
51 CONST PHASE_PRE_ROLLBACK = 4;
52 CONST PHASE_POST_ROLLBACK = 8;
53
54 /**
55 * Keep track of the number of opens and close
56 *
57 * @var int
58 */
59 private static $_count = 0;
60
61 /**
62 * Keep track if we need to commit or rollback
63 *
64 * @var boolean
65 */
66 private static $_doCommit = TRUE;
67
68 /**
69 * hold a dao singleton for query operations
70 *
71 * @var object
72 */
73 private static $_dao = NULL;
74
75 /**
76 * Array of callbacks to invoke when the transaction commits or rolls back.
77 * Array keys are phase constants.
78 * Array values are arrays of callbacks.
79 */
80 private static $_callbacks = NULL;
81
82 /**
83 * Whether commit() has been called on this instance
84 * of CRM_Core_Transaction
85 */
86 private $_pseudoCommitted = FALSE;
87 function __construct() {
88 if (!self::$_dao) {
89 self::$_dao = new CRM_Core_DAO();
90 }
91
92 if (self::$_count == 0) {
93 self::$_dao->query('BEGIN');
94 self::$_callbacks = array(
95 self::PHASE_PRE_COMMIT => array(),
96 self::PHASE_POST_COMMIT => array(),
97 self::PHASE_PRE_ROLLBACK => array(),
98 self::PHASE_POST_ROLLBACK => array(),
99 );
100 }
101
102 self::$_count++;
103 }
104
105 function __destruct() {
106 $this->commit();
107 }
108
109 function commit() {
110 if (self::$_count > 0 && !$this->_pseudoCommitted) {
111 $this->_pseudoCommitted = TRUE;
112 self::$_count--;
113
114 if (self::$_count == 0) {
115
116 // It's possible that, say, a POST_COMMIT callback creates another
117 // transaction. That transaction will need its own list of callbacks.
118 $oldCallbacks = self::$_callbacks;
119 self::$_callbacks = NULL;
120
121 if (self::$_doCommit) {
122 self::invokeCallbacks(self::PHASE_PRE_COMMIT, $oldCallbacks);
123 self::$_dao->query('COMMIT');
124 self::invokeCallbacks(self::PHASE_POST_COMMIT, $oldCallbacks);
125 }
126 else {
127 self::invokeCallbacks(self::PHASE_PRE_ROLLBACK, $oldCallbacks);
128 self::$_dao->query('ROLLBACK');
129 self::invokeCallbacks(self::PHASE_POST_ROLLBACK, $oldCallbacks);
130 }
131 // this transaction is complete, so reset doCommit flag
132 self::$_doCommit = TRUE;
133 }
134 }
135 }
136
137 static public function rollbackIfFalse($flag) {
138 if ($flag === FALSE) {
139 self::$_doCommit = FALSE;
140 }
141 }
142
143 public function rollback() {
144 self::$_doCommit = FALSE;
145 }
146
147 /**
148 * Force an immediate rollback, regardless of how many any
149 * CRM_Core_Transaction objects are waiting for
150 * pseudo-commits.
151 *
152 * Only rollback if the transaction API has been called.
153 *
154 * This is only appropriate when it is _certain_ that the
155 * callstack will not wind-down normally -- e.g. before
156 * a call to exit().
157 */
158 static public function forceRollbackIfEnabled() {
159 if (self::$_count > 0) {
160 $oldCallbacks = self::$_callbacks;
161 self::$_callbacks = NULL;
162 self::invokeCallbacks(self::PHASE_PRE_ROLLBACK, $oldCallbacks);
163 self::$_dao->query('ROLLBACK');
164 self::invokeCallbacks(self::PHASE_POST_ROLLBACK, $oldCallbacks);
165 self::$_count = 0;
166 self::$_doCommit = TRUE;
167 }
168 }
169
170 static public function willCommit() {
171 return self::$_doCommit;
172 }
173
174 /**
175 * Determine whether there is a pending transaction
176 */
177 static public function isActive() {
178 return (self::$_count > 0);
179 }
180
181 /**
182 * Add a transaction callback
183 *
184 * Pre-condition: isActive()
185 *
186 * @param $phase A constant; one of: self::PHASE_{PRE,POST}_{COMMIT,ROLLBACK}
187 * @param $callback A PHP callback
188 * @param mixed $params Optional values to pass to callback.
189 * See php manual call_user_func_array for details.
190 */
191 static public function addCallback($phase, $callback, $params = null) {
192 self::$_callbacks[$phase][] = array(
193 'callback' => $callback,
194 'parameters' => (is_array($params) ? $params : array($params))
195 );
196 }
197
198 static protected function invokeCallbacks($phase, $callbacks) {
199 if (is_array($callbacks[$phase])) {
200 foreach ($callbacks[$phase] as $cb) {
201 call_user_func_array($cb['callback'], $cb['parameters']);
202 }
203 }
204 }
205}
206