<?php
namespace queasy\db;
use Exception;
use BadMethodCallException;
use InvalidArgumentException;
use PDOException;
use PDO;
use ArrayAccess;
use Psr\Log\NullLogger;
use Psr\Log\LoggerInterface;
use Psr\Log\LoggerAwareInterface;
use queasy\db\query\Query;
use queasy\db\query\CustomQuery;
class Db extends PDO implements ArrayAccess, LoggerAwareInterface
{
const DEFAULT_FETCH_MODE = PDO::FETCH_ASSOC;
const RETURN_STATEMENT = 1;
const RETURN_ONE = 2;
const RETURN_ALL = 3;
const RETURN_VALUE = 4;
private $tables = array();
private $queries = array();
/**
* @var array|ArrayAccess Database config
*/
protected $config;
/**
* @var LoggerInterface Logger instance
*/
protected $logger;
/**
* Create Db instance
*
* @param string|array|ArrayAccess $configOrDsn DSN string or config array
* @param string $user Database user name
* @param string $password Database user password
* @param array $options Key-value array of driver-specific options
*
*/
public function __construct($configOrDsn = null, $user = null, $password = null, array $options = null)
{
$this->logger = new NullLogger();
$config = array();
if (null === $configOrDsn) {
$connectionConfig = null;
} elseif (is_string($configOrDsn)) {
$connectionConfig = array(
'dsn' => $configOrDsn,
'user' => $user,
'password' => $password,
'options' => $options
);
} elseif (is_array($configOrDsn) || ($configOrDsn instanceof ArrayAccess)) {
$config = $configOrDsn;
$connectionConfig = isset($config['connection'])? $config['connection']: null;
} else {
throw new InvalidArgumentException('Wrong constructor arguments.');
}
$this->config = $config;
$connectionString = new Connection($connectionConfig);
parent::__construct(
$connectionString(),
isset($connectionConfig['user'])? $connectionConfig['user']: $user,
isset($connectionConfig['password'])? $connectionConfig['password']: $password,
isset($connectionConfig['options'])? $connectionConfig['options']: $options
);
if (isset($config['queries'])) {
$this->queries = $config['queries'];
}
$errorMode = isset($config['errorMode'])? $config['errorMode']: self::ERRMODE_EXCEPTION;
if (!$this->setAttribute(self::ATTR_ERRMODE, $errorMode)) {
throw new DbException('Cannot set error mode.');
}
if (isset($config['fetchMode'])) {
if (!$this->setAttribute(self::ATTR_DEFAULT_FETCH_MODE, $config['fetchMode'])) {
throw new DbException('Cannot set fetch mode.');
}
}
}
/**
* Set a logger.
*
* @param LoggerInterface $logger
*/
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function __get($name)
{
return $this->table($name);
}
public function __call($name, array $args = array())
{
if (!isset($this->queries[$name])) {
throw new BadMethodCallException(sprintf('No method "%s" found.', $name));
}
$query = new CustomQuery($this, $this->queries[$name]);
$query->setLogger($this->logger);
$params = array_shift($args);
$options = array_shift($args);
return $query(
empty($params)? array(): $params,
empty($options)? array(): $options
);
}
public function __invoke($sql, array $params = array(), array $options = array())
{
return $this->run($sql, $params, $options);
}
public function offsetGet($name)
{
return $this->table($name);
}
public function offsetSet($offset, $value)
{
throw new BadMethodCallException(sprintf('Not implemented.', $offset, $value));
}
public function offsetExists($offset)
{
throw new BadMethodCallException(sprintf('Not implemented.', $offset));
}
public function offsetUnset($offset)
{
throw new BadMethodCallException(sprintf('Not implemented.', $offset));
}
public function table($name)
{
if (!isset($this->tables[$name])) {
$tablesConfig = isset($this->config['tables'])
? $this->config['tables']
: array();
$tableConfig = isset($tablesConfig[$name])
? $tablesConfig[$name]
: array();
$this->tables[$name] = new Table($this, $name, $tableConfig);
$this->tables[$name]->setLogger($this->logger);
}
return $this->tables[$name];
}
public function run($sql, array $params = array(), array $options = array())
{
$query = new Query($this, $sql);
$query->setLogger($this->logger);
return $query($params, $options);
}
public function id($sequence = null)
{
return $this->lastInsertId($sequence);
}
public function trans($func)
{
if (!is_callable($func)) {
throw new InvalidArgumentException('Argument is not callable.');
}
$this->beginTransaction();
try {
$func($this);
$this->commit();
} catch (Exception $e) {
$this->rollBack();
throw $e;
}
}
protected function logger()
{
return $this->logger;
}
}
|