3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
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 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2014
32 * @copyright David Strauss <david@fourkitchens.com> (c) 2007
35 * This file has its origins in Donald Lobo's conversation with David
36 * Strauss over IRC and the CRM_Core_DAO::transaction() function.
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
44 class CRM_Core_Transaction
{
47 * These constants represent phases at which callbacks can be invoked
49 CONST PHASE_PRE_COMMIT
= 1;
50 CONST PHASE_POST_COMMIT
= 2;
51 CONST PHASE_PRE_ROLLBACK
= 4;
52 CONST PHASE_POST_ROLLBACK
= 8;
55 * Keep track of the number of opens and close
59 private static $_count = 0;
62 * Keep track if we need to commit or rollback
66 private static $_doCommit = TRUE;
69 * hold a dao singleton for query operations
73 private static $_dao = NULL;
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.
80 private static $_callbacks = NULL;
83 * Whether commit() has been called on this instance
84 * of CRM_Core_Transaction
86 private $_pseudoCommitted = FALSE;
91 function __construct() {
93 self
::$_dao = new CRM_Core_DAO();
96 if (self
::$_count == 0) {
97 self
::$_dao->query('BEGIN');
98 self
::$_callbacks = array(
99 self
::PHASE_PRE_COMMIT
=> array(),
100 self
::PHASE_POST_COMMIT
=> array(),
101 self
::PHASE_PRE_ROLLBACK
=> array(),
102 self
::PHASE_POST_ROLLBACK
=> array(),
109 function __destruct() {
114 if (self
::$_count > 0 && !$this->_pseudoCommitted
) {
115 $this->_pseudoCommitted
= TRUE;
118 if (self
::$_count == 0) {
120 // It's possible that, say, a POST_COMMIT callback creates another
121 // transaction. That transaction will need its own list of callbacks.
122 $oldCallbacks = self
::$_callbacks;
123 self
::$_callbacks = NULL;
125 if (self
::$_doCommit) {
126 self
::invokeCallbacks(self
::PHASE_PRE_COMMIT
, $oldCallbacks);
127 self
::$_dao->query('COMMIT');
128 self
::invokeCallbacks(self
::PHASE_POST_COMMIT
, $oldCallbacks);
131 self
::invokeCallbacks(self
::PHASE_PRE_ROLLBACK
, $oldCallbacks);
132 self
::$_dao->query('ROLLBACK');
133 self
::invokeCallbacks(self
::PHASE_POST_ROLLBACK
, $oldCallbacks);
135 // this transaction is complete, so reset doCommit flag
136 self
::$_doCommit = TRUE;
144 static public function rollbackIfFalse($flag) {
145 if ($flag === FALSE) {
146 self
::$_doCommit = FALSE;
150 public function rollback() {
151 self
::$_doCommit = FALSE;
155 * Force an immediate rollback, regardless of how many any
156 * CRM_Core_Transaction objects are waiting for
159 * Only rollback if the transaction API has been called.
161 * This is only appropriate when it is _certain_ that the
162 * callstack will not wind-down normally -- e.g. before
165 static public function forceRollbackIfEnabled() {
166 if (self
::$_count > 0) {
167 $oldCallbacks = self
::$_callbacks;
168 self
::$_callbacks = NULL;
169 self
::invokeCallbacks(self
::PHASE_PRE_ROLLBACK
, $oldCallbacks);
170 self
::$_dao->query('ROLLBACK');
171 self
::invokeCallbacks(self
::PHASE_POST_ROLLBACK
, $oldCallbacks);
173 self
::$_doCommit = TRUE;
180 static public function willCommit() {
181 return self
::$_doCommit;
185 * Determine whether there is a pending transaction
187 static public function isActive() {
188 return (self
::$_count > 0);
192 * Add a transaction callback
194 * Pre-condition: isActive()
196 * @param $phase A constant; one of: self::PHASE_{PRE,POST}_{COMMIT,ROLLBACK}
197 * @param $callback A PHP callback
198 * @param mixed $params Optional values to pass to callback.
199 * See php manual call_user_func_array for details.
201 static public function addCallback($phase, $callback, $params = null, $id = NULL) {
203 self
::$_callbacks[$phase][$id] = array(
204 'callback' => $callback,
205 'parameters' => (is_array($params) ?
$params : array($params))
208 self
::$_callbacks[$phase][] = array(
209 'callback' => $callback,
210 'parameters' => (is_array($params) ?
$params : array($params))
219 static protected function invokeCallbacks($phase, $callbacks) {
220 if (is_array($callbacks[$phase])) {
221 foreach ($callbacks[$phase] as $cb) {
222 call_user_func_array($cb['callback'], $cb['parameters']);