<?php namespace Juju; require_once('Data/securestring.class.php'); require_once('PDO/transaction.class.php'); require_once('PDO/table.class.php'); use Juju\{Data\SecureString, PDO\Table, PDO\Transaction}; class PDO { public static function from(string $dsnstring){ $parts = explode(':', $dsnstring); $dsnstring = $parts[1]; $dsn = explode(';', $dsnstring); $dsn = array_reduce($dsn, function($dsn, $item){ $item = explode('=', $item); $dsn[$item[0]] = $item[1]; return $dsn; }); if(!isset($dsn['host'])){ throw new \Exception("DSN '{$dsnstring}' missing host"); } if(!isset($dsn['dbname'])){ throw new \Exception("DSN '{$dsnstring}' missing dbname"); } if(!isset($dsn['user'])){ $user = $dsn['dbname']; }else{ $user = $dsn['user']; } if(!isset($dsn['pass'])){ $dsn['pass'] = $user; } $pass = SecureString::from($dsn['pass']); unset($dsn['pass'], $dsn['user']); $dsn = array_reduce(array_keys($dsn), function($a, $key) use($dsn){ $a[] = "{$key}={$dsn[$key]}"; return $a; }); $dsnstring = $parts[0].':'.implode(';', $dsn); return new PDO($dsnstring, $user, $pass); } private $pdo; private function __construct(string $dsn, string $user, SecureString $pass){ $mysql = explode(':', $dsn)[0] === 'mysql'; $options = []; if($mysql){ $options[\PDO::MYSQL_ATTR_INIT_COMMAND] = "SET NAMES 'UTF8'"; $options[\PDO::MYSQL_ATTR_MULTI_STATEMENTS] = false; $options[\PDO::MYSQL_ATTR_FOUND_ROWS] = true; } $pdo = new \PDO($dsn, $user, (string)$pass, $options); $pdo->setAttribute(\PDO::ATTR_AUTOCOMMIT, true); $pdo->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_LOWER); $pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false); $pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC); $this->pdo = $pdo; } public function transaction(callable $fn){ $pdo = $this->pdo; if($pdo->inTransaction()){ throw new \Exception("Unable to start a new transaction"); } $transaction = new Transaction($this); if($fn($transaction) === false){ do{ $transaction->rollback(); }while($transaction->savepoint); $pdo->rollback(); }else{ $transaction->commit(); if($pdo->inTransaction()){ $pdo->commit(); } } unset($transaction); $pdo->setAttribute(\PDO::ATTR_AUTOCOMMIT, true); return $this; } public function table(string $name){ return new Table($this, $name); } public function prepare(string $statement, array $options = []){ $pdo = $this->pdo; if($pdo->getAttribute(\PDO::ATTR_DRIVER_NAME) == 'mysql'){ $options[\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY] = true; } $query = $pdo->prepare($statement); if($query === false){ throw $this->getError(); } return $query; } public function exec(string $statement){ $query = $this->prepare($statement); $count = 0; $query->execute(); while($query->fetch() !== false){ $count++; } if($count == 0){ $count = $query->rowCount(); } $query->closeCursor(); return $count; } public function query(string $statement, int $mode = null, ...$args){ $query = $this->prepare($statement); if(!is_null($mode)){ $query->setFetchMode($mode, ...$args); } $query->execute(); return $query; } public function quote(...$args){ return $this->pdo->quote(...$args); } public function beginTransaction(...$args){ return $this->pdo->beginTransaction(...$args); } public function setAttribute(...$args){ return $this->pdo->setAttribute(...$args); } public function getAttribute(...$args){ return $this->pdo->getAttribute(...$args); } public function lastInsertId(...$args){ return $this->pdo->lastInsertId(...$args); } public function getError(){ $error = $this->pdo->errorInfo(); return new \Exception($error[2], $error[1]); } } ?>