|
@@ -0,0 +1,127 @@
|
|
|
+<?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;
|
|
|
+ }
|
|
|
+ $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++;
|
|
|
+ }
|
|
|
+ $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 getError(){
|
|
|
+ $error = $this->pdo->errorInfo();
|
|
|
+ return new \Exception($error[2], $error[1]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+?>
|