pdo.class.php 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. <?php
  2. namespace Juju;
  3. require_once('Data/securestring.class.php');
  4. require_once('PDO/transaction.class.php');
  5. require_once('PDO/table.class.php');
  6. use Juju\{Data\SecureString, PDO\Table, PDO\Transaction};
  7. class PDO {
  8. public static function from(string $dsnstring){
  9. $parts = explode(':', $dsnstring);
  10. $dsnstring = $parts[1];
  11. $dsn = explode(';', $dsnstring);
  12. $dsn = array_reduce($dsn, function($dsn, $item){
  13. $item = explode('=', $item);
  14. $dsn[$item[0]] = $item[1];
  15. return $dsn;
  16. });
  17. if(!isset($dsn['host'])){
  18. throw new \Exception("DSN '{$dsnstring}' missing host");
  19. }
  20. if(!isset($dsn['dbname'])){
  21. throw new \Exception("DSN '{$dsnstring}' missing dbname");
  22. }
  23. if(!isset($dsn['user'])){
  24. $user = $dsn['dbname'];
  25. }else{
  26. $user = $dsn['user'];
  27. }
  28. if(!isset($dsn['pass'])){
  29. $dsn['pass'] = $user;
  30. }
  31. $pass = SecureString::from($dsn['pass']);
  32. unset($dsn['pass'], $dsn['user']);
  33. $dsn = array_reduce(array_keys($dsn), function($a, $key) use($dsn){
  34. $a[] = "{$key}={$dsn[$key]}";
  35. return $a;
  36. });
  37. $dsnstring = $parts[0].':'.implode(';', $dsn);
  38. return new PDO($dsnstring, $user, $pass);
  39. }
  40. private $pdo;
  41. private function __construct(string $dsn, string $user, SecureString $pass){
  42. $mysql = explode(':', $dsn)[0] === 'mysql';
  43. $options = [];
  44. if($mysql){
  45. $options[\PDO::MYSQL_ATTR_INIT_COMMAND] = "SET NAMES 'UTF8'";
  46. $options[\PDO::MYSQL_ATTR_MULTI_STATEMENTS] = false;
  47. $options[\PDO::MYSQL_ATTR_FOUND_ROWS] = true;
  48. }
  49. $pdo = new \PDO($dsn, $user, (string)$pass, $options);
  50. $pdo->setAttribute(\PDO::ATTR_AUTOCOMMIT, true);
  51. $pdo->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_LOWER);
  52. $pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
  53. $pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
  54. $this->pdo = $pdo;
  55. }
  56. public function transaction(callable $fn){
  57. $pdo = $this->pdo;
  58. if($pdo->inTransaction()){
  59. throw new \Exception("Unable to start a new transaction");
  60. }
  61. $transaction = new Transaction($this);
  62. if($fn($transaction) === false){
  63. do{
  64. $transaction->rollback();
  65. }while($transaction->savepoint);
  66. $pdo->rollback();
  67. }else{
  68. $transaction->commit();
  69. if($pdo->inTransaction()){
  70. $pdo->commit();
  71. }
  72. }
  73. unset($transaction);
  74. $pdo->setAttribute(\PDO::ATTR_AUTOCOMMIT, true);
  75. return $this;
  76. }
  77. public function table(string $name){
  78. return new Table($this, $name);
  79. }
  80. public function prepare(string $statement, array $options = []){
  81. $pdo = $this->pdo;
  82. if($pdo->getAttribute(\PDO::ATTR_DRIVER_NAME) == 'mysql'){
  83. $options[\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY] = true;
  84. }
  85. $query = $pdo->prepare($statement);
  86. if($query === false){
  87. throw $this->getError();
  88. }
  89. return $query;
  90. }
  91. public function exec(string $statement){
  92. $query = $this->prepare($statement);
  93. $count = 0;
  94. $query->execute();
  95. while($query->fetch() !== false){
  96. $count++;
  97. }
  98. if($count == 0){
  99. $count = $query->rowCount();
  100. }
  101. $query->closeCursor();
  102. return $count;
  103. }
  104. public function query(string $statement, int $mode = null, ...$args){
  105. $query = $this->prepare($statement);
  106. if(!is_null($mode)){
  107. $query->setFetchMode($mode, ...$args);
  108. }
  109. $query->execute();
  110. return $query;
  111. }
  112. public function quote(...$args){
  113. return $this->pdo->quote(...$args);
  114. }
  115. public function beginTransaction(...$args){
  116. return $this->pdo->beginTransaction(...$args);
  117. }
  118. public function setAttribute(...$args){
  119. return $this->pdo->setAttribute(...$args);
  120. }
  121. public function getAttribute(...$args){
  122. return $this->pdo->getAttribute(...$args);
  123. }
  124. public function lastInsertId(...$args){
  125. return $this->pdo->lastInsertId(...$args);
  126. }
  127. public function getError(){
  128. $error = $this->pdo->errorInfo();
  129. return new \Exception($error[2], $error[1]);
  130. }
  131. public function stringFilter(array $filter = null){
  132. $where = '';
  133. if(!is_null($filter)){
  134. $where = 'where ';
  135. foreach($filter as $name => $value){
  136. $where .= "`{$name}` = {$this->quote($value)} and";
  137. }
  138. $where = rtrim($where, ' and');
  139. }
  140. return $where;
  141. }
  142. public function stringSet(array $data){
  143. $sets = '';
  144. foreach($data as $name => $value){
  145. $sets .= "`{$name}` = {$this->quote($value)},";
  146. }
  147. if(count($sets) > 0){
  148. $sets = 'set '.rtrim($sets, ',');
  149. }
  150. return $sets;
  151. }
  152. }
  153. ?>