Created
August 16, 2014 17:40
-
-
Save jm42/ce3441d9017e89e79f93 to your computer and use it in GitHub Desktop.
Vago Database Abstraction
This file contains 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 | |
namespace Vago\Driver; | |
/** | |
* Connection interface. | |
*/ | |
interface Connection | |
{ | |
/** | |
* Executes a result-less query. | |
* | |
* @param string $query | |
*/ | |
public function exec($query); | |
/** | |
* Prepares a query for execution and returns a statement object. | |
* | |
* @param string $query | |
*/ | |
public function prepare($query); | |
/** | |
* Executes a query, returning a result set object. | |
* | |
* @param string $query | |
*/ | |
public function query($query); | |
} | |
/** | |
* Statement interface. | |
*/ | |
interface Statement | |
{ | |
const TYPE_JSON = 'json'; | |
const TYPE_BIGINT = 'bigint'; | |
const TYPE_BOOLEAN = 'boolean'; | |
const TYPE_DATETIME = 'datetime'; | |
const TYPE_DATETIMETZ = 'datetimetz'; | |
const TYPE_DATE = 'date'; | |
const TYPE_TIME = 'time'; | |
const TYPE_DECIMAL = 'decimal'; | |
const TYPE_INTEGER = 'integer'; | |
const TYPE_OBJECT = 'object'; | |
const TYPE_SMALLINT = 'smallint'; | |
const TYPE_STRING = 'string'; | |
const TYPE_TEXT = 'text'; | |
const TYPE_BINARY = 'binary'; | |
const TYPE_BLOB = 'blob'; | |
const TYPE_FLOAT = 'float'; | |
const TYPE_GUID = 'guid'; | |
/** | |
* Binds the value of a parameter to a statement variable. | |
* | |
* @param mixed $param | |
* @param mixed $value | |
* @param integer $type | |
*/ | |
public function bindValue($param, $value, $type = null); | |
/** | |
* Binds a parameter to the specified variable name. | |
* | |
* @param mixed $column | |
* @param mixed $variable | |
* @param integer $type | |
* @param integer $length | |
*/ | |
public function bindParam($column, &$variable, $type = null, $length = null); | |
/** | |
* Executes a prepared statement and returns a result set object. | |
* | |
* @return \Vago\Driver\Result | |
*/ | |
public function execute(); | |
} | |
/** | |
* Result interface. | |
*/ | |
interface Result | |
{ | |
const FETCH_ASSOC = 1; | |
const FETCH_NUM = 2; | |
const FETCH_BOTH = 3; | |
/** | |
* Sets the fetch mode to use while iterating this statement. | |
* | |
* @param integer $mode | |
*/ | |
public function setFetchMode($mode); | |
/** | |
* Returns the next row of a result set. | |
* | |
* @param integer|null $mode | |
* | |
* @return mixed | |
*/ | |
public function fetch($mode = null); | |
/** | |
* Returns the number of columns in the result set. | |
* | |
* @return integer | |
*/ | |
public function columnCount(); | |
} |
This file contains 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 | |
namespace Vago\Driver\SQLite3; | |
use Vago\Driver\Connection as BaseConnection; | |
use Vago\Driver\Statement as BaseStatement; | |
use Vago\Driver\Result as BaseResult; | |
use Vago\Driver\Exception; | |
use Vago\Driver\Exception as E; | |
/** | |
* SQLite3 connection. | |
*/ | |
class Connection implements BaseConnection | |
{ | |
/** | |
* @var \SQLite3 | |
*/ | |
protected $conn; | |
/** | |
* Constructor. | |
* | |
* @param array $config | |
*/ | |
public function __construct($config) | |
{ | |
$filename = isset($config['path']) ? $config['path'] : ':memory:'; | |
try { | |
$this->conn = new \SQLite3($filename); | |
} catch (\Exception $e) { | |
if (strpos($e->getMessage(), 'unable to open database file') !== false) { | |
throw new E\ConnectionException($e->getMessage(), $e->getCode()); | |
} | |
throw $e; | |
} | |
} | |
/** | |
* @return \SQLite3 | |
*/ | |
public function getWrappedConnection() | |
{ | |
return $this->conn; | |
} | |
public function lastError() | |
{ | |
$errorMsg = $this->conn->lastErrorMsg(); | |
$errorCode = $this->conn->lastErrorCode(); | |
if ($errorCode === 0) { | |
return; | |
} | |
if (strpos($errorMsg, 'attempt to write a readonly database') !== false) { | |
return new E\ReadOnlyException($errorMsg, $errorCode); | |
} | |
if (strpos($errorMsg, 'syntax error') !== false) { | |
return new E\SyntaxErrorException($errorMsg, $errorCode); | |
} | |
return new Exception($errorMsg, $errorCode); | |
} | |
/** | |
* (non-PHPdoc) | |
* @see \Vago\Driver\Connection::exec() | |
*/ | |
public function exec($query) | |
{ | |
$result = $this->conn->exec($query); | |
if ($result === false) { | |
throw $this->lastError(); | |
} | |
return $this->conn->changes(); | |
} | |
/** | |
* (non-PHPdoc) | |
* @see \Vago\Driver\Connection::prepare() | |
*/ | |
public function prepare($query) | |
{ | |
$stmt = new Statement($this, $query); | |
return $stmt; | |
} | |
/** | |
* (non-PHPdoc) | |
* @see \Vago\Driver\Connection::query() | |
*/ | |
public function query($query) | |
{ | |
$stmt = new Statement($this, $query); | |
$result = $stmt->execute(); | |
return $result; | |
} | |
} | |
/** | |
* SQLite3 statement. | |
*/ | |
class Statement implements BaseStatement | |
{ | |
protected static $types = array( | |
'json' => SQLITE3_TEXT, | |
'bigint' => SQLITE3_INTEGER, | |
'boolean' => SQLITE3_INTEGER, | |
'datetime' => SQLITE3_TEXT, | |
'datetimetz' => SQLITE3_TEXT, | |
'date' => SQLITE3_TEXT, | |
'time' => SQLITE3_TEXT, | |
'decimal' => SQLITE3_FLOAT, | |
'integer' => SQLITE3_INTEGER, | |
'object' => SQLITE3_TEXT, | |
'smallint' => SQLITE3_INTEGER, | |
'string' => SQLITE3_TEXT, | |
'text' => SQLITE3_TEXT, | |
'binary' => SQLITE3_BLOB, | |
'blob' => SQLITE3_BLOB, | |
'float' => SQLITE3_FLOAT, | |
'guid' => SQLITE3_TEXT, | |
); | |
/** | |
* @var \SQLite3Stmt | |
*/ | |
protected $stmt; | |
/** | |
* Constructor. | |
* | |
* @param \Vago\Driver\SQLite3\Connection $conn | |
* @param string $query | |
*/ | |
public function __construct(Connection $conn, $query) | |
{ | |
$this->stmt = $conn->getWrappedConnection()->prepare($query); | |
if ($this->stmt === false) { | |
throw $conn->lastError(); | |
} | |
} | |
/** | |
* @return \SQLite3Stmt | |
*/ | |
public function getWrappedStatement() | |
{ | |
return $this->stmt; | |
} | |
/** | |
* (non-PHPdoc) | |
* @see \Vago\Driver\Statement::bindValue() | |
*/ | |
public function bindValue($param, $value, $type = null) | |
{ | |
if ($type === null) { | |
$this->stmt->bindValue($param, $value); | |
} else { | |
if (is_string($type)) { | |
if (!isset(self::$types[$type])) { | |
throw new \InvalidArgumentException( | |
"Type '$type' not recognized." | |
); | |
} | |
$type = self::$types[$type]; | |
} | |
$this->stmt->bindValue($param, $value, $type); | |
} | |
} | |
/** | |
* (non-PHPdoc) | |
* @see \Vago\Driver\Statement::bindParam() | |
*/ | |
public function bindParam($column, &$variable, $type = null, $length = null) | |
{ | |
$this->stmt->bindParam($param, $variable); | |
} | |
/** | |
* (non-PHPdoc) | |
* @see \Vago\Driver\Statement::execute() | |
*/ | |
public function execute() | |
{ | |
$result = new Result($this); | |
return $result; | |
} | |
} | |
/** | |
* SQLite3 result. | |
*/ | |
class Result implements BaseResult | |
{ | |
/** | |
* @var \SQLite3Result | |
*/ | |
protected $result; | |
/** | |
* @var integer | |
*/ | |
protected $fetchMode; | |
/** | |
* Constructor. | |
* | |
* @param \Vago\Driver\SQLite3\Statement $stmt | |
*/ | |
public function __construct(Statement $stmt) | |
{ | |
$this->result = $stmt->getWrappedStatement()->execute(); | |
if ($this->result === false) { | |
throw $conn->lastError(); | |
} | |
} | |
/** | |
* Translate fetch mode. | |
* | |
* @param integer $mode | |
* | |
* @throws \InvalidArgumentException | |
* @return integer | |
*/ | |
protected function translateFetchMode($mode) | |
{ | |
switch ($mode) { | |
case BaseResult::FETCH_NUM: | |
return SQLITE3_NUM; | |
case BaseResult::FETCH_ASSOC: | |
return SQLITE3_ASSOC; | |
case BaseResult::FETCH_BOTH: | |
return SQLITE3_BOTH; | |
default: | |
throw new \InvalidArgumentException( | |
"Invalid fetch mode '$mode'." | |
); | |
} | |
} | |
/** | |
* (non-PHPdoc) | |
* @see \Vago\Driver\Result::setFetchMode() | |
*/ | |
public function setFetchMode($mode) | |
{ | |
$this->fetchMode = $mode; | |
} | |
/** | |
* (non-PHPdoc) | |
* @see \Vago\Driver\Result::fetch() | |
*/ | |
public function fetch($mode = null) | |
{ | |
if (null === $mode) { | |
$mode = $this->fetchMode; | |
} | |
if (null !== $mode) { | |
$fetchMode = $this->translateFetchMode($mode); | |
$row = $this->result->fetchArray($fetchMode); | |
return $row; | |
} | |
return $this->result->fetchArray(); | |
} | |
/** | |
* (non-PHPdoc) | |
* @see \Vago\Driver\Result::columnCount() | |
*/ | |
public function columnCount() | |
{ | |
return $this->result->numColumns(); | |
} | |
} |
This file contains 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 | |
namespace Vago\Driver; | |
class Exception extends \Exception | |
{ | |
} | |
namespace Vago\Driver\Exception; | |
use Vago\Driver\Exception; | |
/** | |
* Unable to connect. | |
*/ | |
class ConnectionException extends Exception | |
{ | |
} | |
/** | |
* Attempt to write a readonly database. | |
*/ | |
class ReadOnlyException extends Exception | |
{ | |
} | |
/** | |
* Syntax error in a statement. | |
*/ | |
class SyntaxErrorException extends Exception | |
{ | |
} |
This file contains 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 | |
namespace Vago; | |
class Vago | |
{ | |
protected static $drivers = array( | |
'sqlite3' => 'Vago\\Driver\\SQLite3\\Connection', | |
); | |
protected $config; | |
protected $connection; | |
/** | |
* Constructor. | |
* | |
* @param array $config | |
* | |
* @throws \InvalidArgumentException | |
*/ | |
public function __construct($config) | |
{ | |
if (!isset($config['driver']) || !isset(self::$drivers[$config['driver']])) { | |
throw new \InvalidArgumentException("Invalid option 'driver'."); | |
} | |
$this->config = $config; | |
} | |
public function getConnection() | |
{ | |
if (null === $this->connection) { | |
$driverName = $this->config['driver']; | |
$driverClass = self::$drivers[$driverName]; | |
$this->connection = new $driverClass($this->config); | |
} | |
return $this->connection; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment