Db.class.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <?php
  2. class Db
  3. {
  4. /** @var PDO */
  5. protected static $pdo;
  6. protected static $cfg = [
  7. 'dsn' => 'mysql:host=127.0.0.1;dbname=protection_center;charset=utf8mb4',
  8. 'user' => 'root',
  9. 'pass' => 'root',
  10. 'options' => [
  11. PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  12. PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
  13. PDO::ATTR_PERSISTENT => false,
  14. ],
  15. ];
  16. /* 当前 SQL 与参数 */
  17. protected $sql = '';
  18. protected $params = [];
  19. protected $table = '';
  20. protected $wheres = [];
  21. protected $orders = [];
  22. protected $limits = '';
  23. /* 日志开关 */
  24. public static $logEnable = false;
  25. public static $logFile = __DIR__ . '/sql.log';
  26. /* ---------- 公有入口 ---------- */
  27. /** 初始化(可外部覆盖配置) */
  28. public static function init(array $config = [])
  29. {
  30. self::$cfg = array_merge(self::$cfg, $config);
  31. }
  32. /** 快速实例化 */
  33. public static function table(string $table): self
  34. {
  35. return (new self)->setTable($table);
  36. }
  37. /** 原始 SQL 查询(带参数) */
  38. public static function query(string $sql, array $params = [])
  39. {
  40. return (new self)->execute($sql, $params);
  41. }
  42. /** 开启事务 */
  43. public static function begin(): bool
  44. {
  45. self::connect();
  46. return self::$pdo->beginTransaction();
  47. }
  48. /** 提交 */
  49. public static function commit(): bool
  50. {
  51. return self::$pdo->commit();
  52. }
  53. /** 回滚 */
  54. public static function roll(): bool
  55. {
  56. return self::$pdo->rollBack();
  57. }
  58. /* ---------- 链式构造 ---------- */
  59. public function setTable(string $table): self
  60. {
  61. $this->table = $table;
  62. $this->reset();
  63. return $this;
  64. }
  65. public function where($field, $op = null, $val = null): self
  66. {
  67. if (is_array($field)) { // 批量 where(['id'=>1,'status'=>2])
  68. foreach ($field as $k => $v) {
  69. $this->wheres[] = [$k, '=', $v];
  70. }
  71. } elseif ($val === null) { // where('id',1) 缺省 =
  72. $this->wheres[] = [$field, '=', $op];
  73. } else {
  74. $this->wheres[] = [$field, $op, $val];
  75. }
  76. return $this;
  77. }
  78. public function order(string $order): self
  79. {
  80. $this->orders[] = $order;
  81. return $this;
  82. }
  83. public function limit(int $offset, int $length = null): self
  84. {
  85. $this->limits = $length === null ? $offset : "$offset,$length";
  86. return $this;
  87. }
  88. /* ---------- 执行 ---------- */
  89. /** 查多条 */
  90. public function get(array $columns = ['*'])
  91. {
  92. $col = $columns === ['*'] ? '*' : implode('`,`', $columns);
  93. $sql = "SELECT `{$col}` FROM `{$this->table}`";
  94. $this->buildWhere($sql);
  95. $this->buildOrder($sql);
  96. $this->buildLimit($sql);
  97. return $this->execute($sql, $this->params)->fetchAll();
  98. }
  99. /** 查单条 */
  100. public function first(array $columns = ['*'])
  101. {
  102. $this->limit(1);
  103. $list = $this->get($columns);
  104. return $list[0] ?? null;
  105. }
  106. /** 统计 */
  107. public function count(string $field = '*')
  108. {
  109. $sql = "SELECT COUNT({$field}) AS c FROM `{$this->table}`";
  110. $this->buildWhere($sql);
  111. $row = $this->execute($sql, $this->params)->fetch();
  112. return (int)($row['c'] ?? 0);
  113. }
  114. /** 插入 返回主键 */
  115. public function insert(array $data): string
  116. {
  117. $keys = array_keys($data);
  118. $vals = array_fill(0, count($keys), '?');
  119. $sql = "INSERT INTO `{$this->table}` (`" . implode('`,`', $keys) . "`) VALUES (" . implode(',', $vals) . ")";
  120. $this->execute($sql, array_values($data));
  121. return self::$pdo->lastInsertId();
  122. }
  123. /** 更新 */
  124. public function update(array $data): int
  125. {
  126. $set = [];
  127. foreach ($data as $k => $v) {
  128. $set[] = "`{$k}`=?";
  129. $this->params[] = $v;
  130. }
  131. $sql = "UPDATE `{$this->table}` SET " . implode(',', $set);
  132. $this->buildWhere($sql);
  133. return $this->execute($sql, $this->params)->rowCount();
  134. }
  135. /** 删除 */
  136. public function delete(): int
  137. {
  138. $sql = "DELETE FROM `{$this->table}`";
  139. $this->buildWhere($sql);
  140. return $this->execute($sql, $this->params)->rowCount();
  141. }
  142. /* ---------- 内部 ---------- */
  143. protected static function connect()
  144. {
  145. if (self::$pdo instanceof PDO) {
  146. return;
  147. }
  148. try {
  149. self::$pdo = new PDO(
  150. self::$cfg['dsn'],
  151. self::$cfg['user'],
  152. self::$cfg['pass'],
  153. self::$cfg['options']
  154. );
  155. } catch (PDOException $e) {
  156. die('数据库连接失败: ' . $e->getMessage());
  157. }
  158. }
  159. protected function execute(string $sql, array $params = [])
  160. {
  161. self::connect();
  162. $this->sql = $sql;
  163. $this->params = $params;
  164. if (self::$logEnable) {
  165. $this->log();
  166. }
  167. try {
  168. $stmt = self::$pdo->prepare($sql);
  169. $stmt->execute($params);
  170. $this->reset();
  171. return $stmt;
  172. } catch (PDOException $e) {
  173. $this->reset();
  174. throw new RuntimeException("SQL 错误: " . $e->getMessage() . "\nSQL: $sql\nPARAMS: " . json_encode($params));
  175. }
  176. }
  177. protected function buildWhere(string &$sql)
  178. {
  179. if (!$this->wheres) {
  180. return;
  181. }
  182. $str = [];
  183. foreach ($this->wheres as list($f, $o, $v)) {
  184. $str[] = "`{$f}` {$o} ?";
  185. $this->params[] = $v;
  186. }
  187. $sql .= ' WHERE ' . implode(' AND ', $str);
  188. }
  189. protected function buildOrder(string &$sql)
  190. {
  191. if ($this->orders) {
  192. $sql .= ' ORDER BY ' . implode(',', $this->orders);
  193. }
  194. }
  195. protected function buildLimit(string &$sql)
  196. {
  197. if ($this->limits) {
  198. $sql .= ' LIMIT ' . $this->limits;
  199. }
  200. }
  201. protected function reset()
  202. {
  203. $this->sql = '';
  204. $this->params = [];
  205. $this->wheres = [];
  206. $this->orders = [];
  207. $this->limits = '';
  208. }
  209. protected function log()
  210. {
  211. $txt = date('Y-m-d H:i:s') . ' | ' . $this->sql . ' | ' . json_encode($this->params) . PHP_EOL;
  212. error_log($txt, 3, self::$logFile);
  213. }
  214. }