Transaction.php 9.85 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
            $this->transactionLevel--;
    
            if($this->transactionLevel == 0) {
                $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionCommit($this->conn);
133 134
                
                /**
zYne's avatar
zYne committed
135 136
                try {
                    $this->bulkDelete();
137

zYne's avatar
zYne committed
138 139
                } catch(Exception $e) {
                    $this->rollback();
140

zYne's avatar
zYne committed
141 142
                    throw new Doctrine_Connection_Transaction_Exception($e->__toString());
                }
143 144 145
                */
                /**
                if($tmp = $this->conn->unitOfWork->getInvalid()) {
zYne's avatar
zYne committed
146
                    $this->rollback();
147

zYne's avatar
zYne committed
148 149
                    throw new Doctrine_Validator_Exception($tmp);
                }
150
                */
zYne's avatar
zYne committed
151 152 153
    
                $this->conn->getDbh()->commit();
    
154
                //$this->conn->unitOfWork->reset();
zYne's avatar
zYne committed
155 156
    
                $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionCommit($this->conn);
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 218 219 220
            }
        }
    }
    /**
     * 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.');
    }
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
    /**
     * 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)
     *
239
     * @throws Doctrine_Transaction_Exception           if the feature is not supported by the driver
240 241 242 243
     * @throws PDOException                             if something fails at the PDO level
     * @return void
     */
    public function setIsolation($isolation) {
244
        throw new Doctrine_Transaction_Exception('Transaction isolation levels not supported by this driver.');
245 246 247 248 249 250 251 252 253
    }

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