Transaction.php 9.75 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
<?php
/*
 *  $Id$
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.phpdoctrine.com>.
 */
21
Doctrine::autoload('Doctrine_Connection_Module');
22 23 24 25 26 27 28 29 30 31
/**
 *
 * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
 * @package     Doctrine
 * @category    Object Relational Mapping
 * @link        www.phpdoctrine.com
 * @since       1.0
 * @version     $Revision$
 */
32
class Doctrine_Transaction extends Doctrine_Connection_Module {
33
    /**
34
     * Doctrine_Transaction is in sleep state when it has no active transactions
35
     */
36
    const STATE_SLEEP       = 0;
37 38 39
    /**
     * Doctrine_Transaction is in active state when it has one active transaction
     */
40
    const STATE_ACTIVE      = 1;
41 42 43
    /**
     * Doctrine_Transaction is in busy state when it has multiple active transactions
     */
44
    const STATE_BUSY        = 2;
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
    /**
     * @var integer $transaction_level      the nesting level of transactions, used by transaction methods
     */
    protected $transactionLevel  = 0;
    /**
     * getState
     * returns the state of this connection
     *
     * @see Doctrine_Connection_Transaction::STATE_* constants
     * @return integer          the connection state
     */
    public function getState() {
        switch($this->transactionLevel) {
            case 0:
                return Doctrine_Transaction::STATE_SLEEP;
            break;
            case 1:
                return Doctrine_Transaction::STATE_ACTIVE;
            break;
            default:
                return Doctrine_Transaction::STATE_BUSY;
        }
    }
    /**
     * getTransactionLevel
     * get the current transaction nesting level
     *
     * @return integer
     */
    public function getTransactionLevel() {
        return $this->transactionLevel;
    }
    /**
     * beginTransaction
     * Start a transaction or set a savepoint.
     *
81 82
     * Listeners: onPreTransactionBegin, onTransactionBegin
     *
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
     * @param string $savepoint                 name of a savepoint to set
     * @throws Doctrine_Transaction_Exception   if trying to create a savepoint and there
     *                                          are no active transactions
     *
     * @return integer                          current transaction nesting level
     */
    public function beginTransaction($savepoint = null) {
        $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionBegin($this->conn);

        if( ! is_null($savepoint)) {
            if($this->transactionLevel == 0)
                throw new Doctrine_Transaction_Exception('Savepoint cannot be created when changes are auto committed');

            $this->createSavePoint($savepoint);
        } else {
            if($this->transactionLevel == 0) 
                $this->conn->getDbh()->beginTransaction();
        }

        $level = ++$this->transactionLevel;

        $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionBegin($this->conn);

        return $level;
    }
    /**
zYne's avatar
zYne committed
109
     * commit
110 111
     * Commit the database changes done during a transaction that is in
     * progress or release a savepoint. This function may only be called when
112 113 114
     * auto-committing is disabled, otherwise it will fail. 
     *
     * Listeners: onPreTransactionCommit, onTransactionCommit
115 116 117 118 119 120 121
     *
     * @param string $savepoint                 name of a savepoint to release
     * @throws Doctrine_Transaction_Exception   if the transaction fails at PDO level
     * @throws Doctrine_Transaction_Exception   if there are no active transactions
     * @throws Doctrine_Validator_Exception     if the transaction fails due to record validations
     * @return void
     */
zYne's avatar
zYne committed
122
    public function commit($savepoint = null) {
123 124
        if($this->transactionLevel == 0)
            throw new Doctrine_Transaction_Exception('Commit/release savepoint cannot be done. There is no active transaction.');
zYne's avatar
zYne committed
125

126
        if ( ! is_null($savepoint)) {
zYne's avatar
zYne committed
127
            $this->releaseSavePoint($savepoint);
128
        } else {
zYne's avatar
zYne committed
129 130 131 132 133 134 135
            $this->transactionLevel--;
    
            if($this->transactionLevel == 0) {
                $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionCommit($this->conn);
    
                try {
                    $this->bulkDelete();
136

zYne's avatar
zYne committed
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
                } catch(Exception $e) {
                    $this->rollback();
    
                    throw new Doctrine_Connection_Transaction_Exception($e->__toString());
                }
    
                if($tmp = $this->unitOfWork->getInvalid()) {
                    $this->rollback();
    
                    throw new Doctrine_Validator_Exception($tmp);
                }
    
                $this->conn->getDbh()->commit();
    
                $this->unitOfWork->reset();
    
                $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionCommit($this->conn);
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
            }
        }
    }
    /**
     * rollback
     * Cancel any database changes done during a transaction or since a specific
     * savepoint that is in progress. This function may only be called when
     * auto-committing is disabled, otherwise it will fail. Therefore, a new
     * transaction is implicitly started after canceling the pending changes.
     *
     * this method listens to onPreTransactionRollback and onTransactionRollback
     * eventlistener methods
     *
     * @param string $savepoint                 name of a savepoint to rollback to
     * @throws Doctrine_Transaction_Exception   if there are no active transactions
     * @return void
     */
    public function rollback($savepoint = null) {
        if($this->transactionLevel == 0)
            throw new Doctrine_Transaction_Exception('Rollback cannot be done. There is no active transaction.');

        $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionRollback($this->conn);

        if ( ! is_null($savepoint)) {
            $this->rollbackSavePoint($savepoint);
        } else {
            $this->unitOfWork->reset();

            $this->transactionLevel = 0;

            $this->conn->getDbh()->rollback();
        }
        $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionRollback($this->conn);
    }
    /**
     * releaseSavePoint
     * creates a new savepoint
     *
     * @param string $savepoint     name of a savepoint to create
     * @return void
     */
    public function createSavePoint($savepoint) {
        throw new Doctrine_Transaction_Exception('Savepoints not supported by this driver.');
    }
    /**
     * releaseSavePoint
     * releases given savepoint
     *
     * @param string $savepoint     name of a savepoint to release
     * @return void
     */
    public function releaseSavePoint($savepoint) {
        throw new Doctrine_Transaction_Exception('Savepoints not supported by this driver.');
    }
    /**
     * rollbackSavePoint
     * releases given savepoint
     *
     * @param string $savepoint     name of a savepoint to rollback to
     * @return void
     */
    public function rollbackSavePoint($savepoint) {
        throw new Doctrine_Transaction_Exception('Savepoints not supported by this driver.');
    }
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
    /**
     * setIsolation
     *
     * Set the transacton isolation level.
     * (implemented by the connection drivers)
     *
     * example:
     *
     * <code>
     * $tx->setIsolation('READ UNCOMMITTED');
     * </code>
     *
     * @param   string  standard isolation level
     *                  READ UNCOMMITTED (allows dirty reads)
     *                  READ COMMITTED (prevents dirty reads)
     *                  REPEATABLE READ (prevents nonrepeatable reads)
     *                  SERIALIZABLE (prevents phantom reads)
     *
     * @throws Doctrine_Connection_Exception            if the feature is not supported by the driver
     * @throws PDOException                             if something fails at the PDO level
     * @return void
     */
    public function setIsolation($isolation) {
        throw new Doctrine_Connection_Exception('Transaction isolation levels not supported by this driver.');
    }

    /**
     * getTransactionIsolation
     *
     * fetches the current session transaction isolation level
     *
     * note: some drivers may support setting the transaction isolation level 
     * but not fetching it
     * 
     * @throws Doctrine_Connection_Exception            if the feature is not supported by the driver
     * @throws PDOException                             if something fails at the PDO level
     * @return string                                   returns the current session transaction isolation level
     */
    public function getIsolation() {
        throw new Doctrine_Connection_Exception('Fetching transaction isolation level not supported by this driver.');
    }
259
}