Skip to content

Instantly share code, notes, and snippets.

@jm42
Created August 16, 2014 17:40
Show Gist options
  • Save jm42/ce3441d9017e89e79f93 to your computer and use it in GitHub Desktop.
Save jm42/ce3441d9017e89e79f93 to your computer and use it in GitHub Desktop.
Vago Database Abstraction
<?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();
}
<?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();
}
}
<?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
{
}
<?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