DaPortal
<?php //$Id$
							//Copyright (c) 2016 Pierre Pronchery <khorben@defora.org>
							//This file is part of DeforaOS Web DaPortal
							//
							//This program is free software: you can redistribute it and/or modify
							//it under the terms of the GNU General Public License as published by
							//the Free Software Foundation, version 3 of the License.
							//
							//This program is distributed in the hope that it will be useful,
							//but WITHOUT ANY WARRANTY; without even the implied warranty of
							//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
							//GNU General Public License for more details.
							//
							//You should have received a copy of the GNU General Public License
							//along with this program.  If not, see <http://www.gnu.org/licenses/>.
							//DatabaseTransaction
							class DatabaseTransaction implements Observable
							{
								//public
								//properties
								static public $STATUS_INITIAL = 0;
								static public $STATUS_COMMIT_PENDING = 1;
								static public $STATUS_ROLLBACK_PENDING = 2;
								static public $STATUS_COMMIT = 3;
								static public $STATUS_ROLLBACK = 4;
								//methods
								//essential
								//DatabaseTransaction::DatabaseTransaction
								public function __construct(Database $database)
								{
									$this->database = $database;
									$this->observers = new \SplObjectStorage();
								}
								//DatabaseTransaction::~DatabaseTransaction
								public function __destruct()
								{
									//XXX this will silently revert any transaction not completed
									if($this->inTransaction())
										$this->flush();
								}
								//accessors
								//DatabaseTransaction::getStatus
								public function getStatus()
								{
									if($this->count < 0)
										return static::$STATUS_INITIAL;
									if($this->count >= 1)
										return $this->rollback
											? static::$STATUS_ROLLBACK_PENDING
											: static::$STATUS_COMMIT_PENDING;
									return $this->rollback
										? static::$STATUS_ROLLBACK
										: static::$STATUS_COMMIT;
								}
								//DatabaseTransaction::inTransaction
								public function inTransaction()
								{
									return $this->count > 0;
								}
								//Observable
								//DatabaseTransaction::addObserver
								public function addObserver(Observer $observer)
								{
									$this->observers->attach($observer);
								}
								//DatabaseTransaction::notifyObservers
								public function notifyObservers()
								{
									//work on a copy in case Observers remove themselves
									$observers = clone $this->observers;
									foreach($observers as $observer)
										$observer->notify($this);
								}
								//DatabaseTransaction::removeObserver
								public function removeObserver(Observer $observer)
								{
									$this->observers->detach($observer);
								}
								//useful
								//DatabaseTransaction::begin
								public function begin()
								{
									if($this->inTransaction())
									{
										$this->count++;
										return TRUE;
									}
									if($this->databaseBegin() === FALSE)
										return FALSE;
									$this->count = 1;
									return TRUE;
								}
								//DatabaseTransaction::commit
								public function commit()
								{
									if(!$this->inTransaction())
										return FALSE;
									if($this->count > 1)
									{
										$this->count--;
										return TRUE;
									}
									return $this->complete();
								}
								//DatabaseTransaction::complete
								public function complete()
								{
									if(!$this->inTransaction())
										return FALSE;
									$this->count = 0;
									if($this->rollback)
										$res = $this->databaseRollback();
									else if(($res = $this->databaseCommit()) === FALSE)
										$this->rollback = TRUE;
									$this->notifyObservers();
									$this->database->transactionComplete();
									return $res;
								}
								//DatabaseTransaction::flush
								public function flush()
								{
									$this->rollback = TRUE;
									return $this->complete();
								}
								//DatabaseTransaction::rollback
								public function rollback()
								{
									if(!$this->inTransaction())
										return FALSE;
									$this->rollback = TRUE;
									if($this->count > 1)
									{
										$this->count--;
										return TRUE;
									}
									return $this->complete();
								}
								//methods
								//accessors
								//DatabaseTransaction::getDatabase
								protected function getDatabase()
								{
									return $this->database;
								}
								//useful
								//DatabaseTransaction::databaseBegin
								protected function databaseBegin()
								{
									return $this->database->query(NULL, 'START TRANSACTION');
								}
								//DatabaseTransaction::databaseCommit
								protected function databaseCommit()
								{
									return $this->database->query(NULL, 'COMMIT');
								}
								//DatabaseTransaction::databaseRollback
								protected function databaseRollback()
								{
									return $this->database->query(NULL, 'ROLLBACK');
								}
								//private
								//properties
								//Observable
								private $observers;
								private $database;
								private $count = -1;
								private $rollback = FALSE;
							}
							?>
							