1
0

pdo.class.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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 commit(...$args){
  119. return $this->pdo->commit(...$args);
  120. }
  121. public function rollback(...$args){
  122. return $this->pdo->rollback(...$args);
  123. }
  124. public function setAttribute(...$args){
  125. return $this->pdo->setAttribute(...$args);
  126. }
  127. public function getAttribute(...$args){
  128. return $this->pdo->getAttribute(...$args);
  129. }
  130. public function lastInsertId(...$args){
  131. return $this->pdo->lastInsertId(...$args);
  132. }
  133. public function getError(){
  134. $error = $this->pdo->errorInfo();
  135. return new \Exception($error[2], $error[1]);
  136. }
  137. public function stringFilter(array $filter = null){
  138. $where = '';
  139. if(!is_null($filter)){
  140. $where = 'where ';
  141. foreach($filter as $name => $value){
  142. $where .= "`{$name}` = {$this->quote($value)} and";
  143. }
  144. $where = rtrim($where, ' and');
  145. }
  146. return $where;
  147. }
  148. public function stringSet(array $data){
  149. $sets = '';
  150. foreach($data as $name => $value){
  151. $sets .= "`{$name}` = {$this->quote($value)},";
  152. }
  153. if(count($sets) > 0){
  154. $sets = 'set '.rtrim($sets, ',');
  155. }
  156. return $sets;
  157. }
  158. public function stringColumn(string $name, array $column){
  159. $default = '';
  160. if(!is_null($column['default'])){
  161. $default .= " DEFAULT {$this->quote($column['default'])}";
  162. }
  163. $null = $column['null'] ? ' NULL' : ' NOT NULL';
  164. $ai = $column['increment'] ? ' AUTO_INCREMENT' : '';
  165. return "{$name} {$column['type']}{$null}{$default}{$ai}";
  166. }
  167. public function stringIndex(string $name, array $idx){
  168. if($idx['unique']){
  169. return "constraint unique index {$name} (".implode(',', $idx['columns']).")";
  170. }else{
  171. return "index {$name} (".implode(',', $idx['columns']).")";
  172. }
  173. }
  174. public function stringForeignKey(string $name, array $foreignKey){
  175. $cols0 = '';
  176. $cols1 = '';
  177. foreach($foreignKey['columns'] as $row){
  178. $cols0 .= "{$row[0]},";
  179. $cols1 .= "{$row[1]},";
  180. }
  181. $cols0 = rtrim($cols0, ',');
  182. $cols1 = rtrim($cols1, ',');
  183. return "constraint `{$name}` foreign key ({$cols0}) references `{$foreignKey['references']}` ({$cols1})";
  184. }
  185. }
  186. ?>