| 
<?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;
 }
 }
 
 
 |