. */ /** * * @author Konsta Vesterinen * @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$ */ class Doctrine_Transaction { /** * Doctrine_Transaction is in sleep state when it has no active transactions */ const STATE_SLEEP = 0; /** * Doctrine_Transaction is in active state when it has one active transaction */ const STATE_ACTIVE = 1; /** * Doctrine_Transaction is in busy state when it has multiple active transactions */ const STATE_BUSY = 2; /** * @var Doctrine_Connection $conn the connection object */ protected $conn; /** * @var integer $transaction_level the nesting level of transactions, used by transaction methods */ protected $transactionLevel = 0; /** * the constructor * * @param Doctrine_Connection $conn Doctrine_Connection object */ public function __construct(Doctrine_Connection $conn) { $this->conn = $conn; } /** * 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. * * Listeners: onPreTransactionBegin, onTransactionBegin * * @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; } /** * commit * Commit the database changes done during a transaction that is in * progress or release a savepoint. This function may only be called when * auto-committing is disabled, otherwise it will fail. * * Listeners: onPreTransactionCommit, onTransactionCommit * * @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 */ public function commit($savepoint = null) { if($this->transactionLevel == 0) throw new Doctrine_Transaction_Exception('Commit/release savepoint cannot be done. There is no active transaction.'); if ( ! is_null($savepoint)) { $this->releaseSavePoint($savepoint); } else { $this->transactionLevel--; if($this->transactionLevel == 0) { $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionCommit($this->conn); try { $this->bulkDelete(); } 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); } } } /** * 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.'); } /** * setIsolation * * Set the transacton isolation level. * (implemented by the connection drivers) * * example: * * * $tx->setIsolation('READ UNCOMMITTED'); * * * @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.'); } }