-
-
Save gorobey/0afdc3e1de781e0c289bb7ed18ff1868 to your computer and use it in GitHub Desktop.
Web SQL Database API for PHP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* usage: | |
$db = new Database("mysql:host=" . Config::$dbHost . ";dbname=" . Config::$dbName . ";", Config::$dbName, Config::$dbUser, Config::$dbPass); | |
$db->changeVersion("", "1", function ($tx) { | |
$tx->executeSql("CREATE TABLE IF NOT EXISTS `examples` ( | |
`id` int(11) NOT NULL AUTO_INCREMENT, | |
`example` mediumtext, | |
PRIMARY KEY (`id`) | |
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=121 ;"); | |
}, function ($error) { | |
if ($error->getCode() !== SQLError::VERSION_ERR) { | |
throw $error; | |
} | |
}); | |
$db->transaction(function ($tx) { | |
$id = 1; | |
$tx->executeSql("SELECT * FROM `examples` WHERE `id` = ?", array($id), function ($tx, $result) { | |
foreach ($result->rows as $row) { | |
echo $row->id; | |
echo ":"; | |
echo $row->example; | |
echo "\n"; | |
} | |
}); | |
}); | |
*/ | |
class SQLTransaction { | |
private $db = null; | |
private $queue = null; | |
public $state = 0; | |
public function __construct($db, $queue) { | |
$this->db = $db; | |
$this->queue = $queue; | |
} | |
public function executeSql($sqlStatement, $args = null, $successCallback = null, $errorCallback = null) { | |
// $sqlStatement should not contain COMMIT, ROLLBACK, CREATE, ... | |
if ($this->state !== 0) { | |
throw new Exception(); | |
} | |
$statement = $this->db->prepare($sqlStatement); | |
if ($args !== null) { | |
foreach ($args as $index => $v) { | |
$statement->bindValue($index + 1, $v); | |
} | |
} | |
$x = new SQLTransactionQueueItem(); | |
$x->statement = $statement; | |
$x->successCallback = $successCallback; | |
$x->errorCallback = $errorCallback; | |
$this->queue->enqueue($x); | |
} | |
} | |
class SQLTransactionQueueItem { | |
public $statement = null; | |
public $successCallback = null; | |
public $errorCallback = null; | |
} | |
class SQLError extends Exception { | |
const UNKNOWN_ERR = 0; | |
const DATABASE_ERR = 1; | |
const VERSION_ERR = 2; | |
const TOO_LARGE_ERR = 3; | |
const QUOTA_ERR = 4; | |
const SYNTAX_ERR = 5; | |
const CONSTRAINT_ERR = 6; | |
const TIMEOUT_ERR = 7; | |
public function __construct($message, $code = 0, Exception $previous = null) { | |
parent::__construct($message, $code, $previous); | |
} | |
} | |
class SQLResultSetRowListIterator implements Iterator { | |
private $rowList = null; | |
private $position = 0; | |
public function __construct($rowList) { | |
$this->rowList = $rowList; | |
$this->rewind(); | |
} | |
function rewind() { | |
$this->position = 0; | |
} | |
function current() { | |
return $this->rowList->item($this->position); | |
} | |
function key() { | |
return $this->position; | |
} | |
function next() { | |
++$this->position; | |
} | |
function valid() { | |
return $this->current() !== null; | |
} | |
} | |
class SQLResultSetRowList implements IteratorAggregate { | |
private $statement = null; | |
private $rows = null; | |
public function __construct($statement) { | |
$this->statement = $statement; | |
$this->rows = array(); | |
} | |
public function item($index) { | |
$i = count($this->rows); | |
while ($i < $index + 1 && ($c = $this->statement->fetchObject()) !== false) { | |
$this->rows[] = $c; | |
++$i; | |
} | |
return $index < $i ? $this->rows[$index] : null; | |
} | |
public function getIterator() { | |
return new SQLResultSetRowListIterator($this); | |
} | |
} | |
class SQLResultSet { | |
public $insertId = 0; | |
public $rowsAffected = 0; | |
public $rows = null; | |
public function __construct($insertId, $rowsAffected, $rows) { | |
$this->insertId = $insertId; | |
$this->rowsAffected = $rowsAffected; | |
$this->rows = $rows; | |
} | |
} | |
class Database { | |
private $dbName = null; | |
private $db = null; | |
private $expectedVersion = ""; | |
private $informationTableName = "webdatabase_information"; | |
public function __construct($dsn, $dbName, $user, $password) { | |
$db = new PDO($dsn, $user, $password); | |
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); | |
$db->exec("SET NAMES utf8"); | |
$db->exec("CREATE TABLE IF NOT EXISTS `" . $this->informationTableName . "` (`key` VARCHAR(255), `value` VARCHAR(255), PRIMARY KEY (`key`))"); | |
$this->dbName = $dbName; | |
$this->db = $db; | |
$this->expectedVersion = ""; | |
} | |
public function readTransaction($callback, $errorCallback = null, $successCallback = null) { | |
$this->transactionSteps($callback, $errorCallback, $successCallback, null, null); | |
} | |
public function transaction($callback, $errorCallback = null, $successCallback = null) { | |
$this->transactionSteps($callback, $errorCallback, $successCallback, null, null); | |
} | |
public function changeVersion($oldVersion, $newVersion, $callback, $errorCallback = null, $successCallback = null) { | |
$this->transactionSteps($callback, $errorCallback, $successCallback, $oldVersion, $newVersion); | |
} | |
private function transactionSteps($callback, $errorCallback, $successCallback, $oldVersion, $newVersion) { | |
$db = $this->db; | |
$expectedVersion = $this->expectedVersion; | |
$queue = new SplQueue(); | |
$db->beginTransaction(); | |
$tx = new SQLTransaction($db, $queue); | |
$error = null; | |
$currentVersion = ""; | |
$s = $db->prepare("SELECT value FROM `" . $this->informationTableName . "` WHERE `key` = ?"); | |
$s->bindValue(1, $this->dbName); | |
$s->execute(); | |
$c = $s->fetchObject(); | |
if ($c !== false) { | |
$currentVersion = $c->value; | |
} | |
if ($oldVersion !== null && $newVersion !== null) { | |
if ($oldVersion !== $currentVersion) { | |
$error = new SQLError("", SQLError::VERSION_ERR); | |
} | |
} | |
if ($expectedVersion !== "" && $expectedVersion !== $currentVersion) { | |
$error = new SQLError("", SQLError::VERSION_ERR); | |
} | |
if ($error === null) { | |
if ($callback !== null) { | |
$callback($tx); | |
} | |
$flag = true; | |
while ($flag && !$queue->isEmpty()) { | |
$x = $queue->dequeue(); | |
try { | |
$x->statement->execute(); | |
} catch (PDOException $e) { | |
$code = $e->getCode(); | |
$c = SQLError::DATABASE_ERR; | |
if ($code === "23000") { | |
$c = SQLError::CONSTRAINT_ERR; | |
} | |
if ($code === "2A000" || $code === "37000" || $code === "42000") { | |
$c = SQLError::SYNTAX_ERR; | |
} | |
$error = new SQLError($e->getMessage(), $c); | |
} | |
if ($error === null) { | |
if ($x->successCallback !== null) { | |
$insertId = $db->lastInsertId(); | |
$rowsAffected = $x->statement->rowCount(); | |
$rowList = new SQLResultSetRowList($x->statement); | |
$resultSet = new SQLResultSet($insertId, $rowsAffected, $rowList); | |
$z = $x->successCallback; | |
$z($tx, $resultSet); | |
} | |
} | |
if ($error !== null) { | |
if ($x->errorCallback !== null) { | |
$z = $x->errorCallback; | |
$flag = $z($tx, $error) === false; | |
} else { | |
$flag = false; | |
} | |
} | |
} | |
$tx->state = 1; | |
if ($error === null) { | |
if ($oldVersion !== null && $newVersion !== null) { | |
$s = $db->prepare("REPLACE INTO `" . $this->informationTableName . "`(`key`, `value`) VALUES (?, ?)"); | |
$s->bindValue(1, $this->dbName); | |
$s->bindValue(2, $newVersion); | |
$s->execute(); | |
$expectedVersion = $newVersion; | |
} | |
$db->commit(); | |
if ($successCallback !== null) { | |
$successCallback(); | |
} | |
} | |
} | |
if ($error !== null) { | |
$db->rollBack(); | |
if ($errorCallback !== null) { | |
$errorCallback($error); | |
} else { | |
throw $error;//? | |
} | |
} | |
} | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment