3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.3 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
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-2013
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;
87 function __construct() {
89 self
::$_dao = new CRM_Core_DAO();
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(),
105 function __destruct() {
110 if (self
::$_count > 0 && !$this->_pseudoCommitted
) {
111 $this->_pseudoCommitted
= TRUE;
114 if (self
::$_count == 0) {
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;
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);
127 self
::invokeCallbacks(self
::PHASE_PRE_ROLLBACK
, $oldCallbacks);
128 self
::$_dao->query('ROLLBACK');
129 self
::invokeCallbacks(self
::PHASE_POST_ROLLBACK
, $oldCallbacks);
131 // this transaction is complete, so reset doCommit flag
132 self
::$_doCommit = TRUE;
137 static public function rollbackIfFalse($flag) {
138 if ($flag === FALSE) {
139 self
::$_doCommit = FALSE;
143 public function rollback() {
144 self
::$_doCommit = FALSE;
148 * Force an immediate rollback, regardless of how many any
149 * CRM_Core_Transaction objects are waiting for
152 * Only rollback if the transaction API has been called.
154 * This is only appropriate when it is _certain_ that the
155 * callstack will not wind-down normally -- e.g. before
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);
166 self
::$_doCommit = TRUE;
170 static public function willCommit() {
171 return self
::$_doCommit;
175 * Determine whether there is a pending transaction
177 static public function isActive() {
178 return (self
::$_count > 0);
182 * Add a transaction callback
184 * Pre-condition: isActive()
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.
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))
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']);