Db.class.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  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' => '7ced1f1f69289c72',
  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 === ['*'] ? '*' : (
  93. count($columns) === 1 ? "`{$columns[0]}`" :
  94. implode('`,`', $columns)
  95. );
  96. $sql = "SELECT {$col} FROM `{$this->table}`";
  97. $this->buildWhere($sql);
  98. $this->buildOrder($sql);
  99. $this->buildLimit($sql);
  100. return $this->execute($sql, $this->params)->fetchAll();
  101. }
  102. /** 查单条 */
  103. public function first(array $columns = ['*'])
  104. {
  105. $this->limit(1);
  106. $list = $this->get($columns);
  107. return $list[0] ?? null;
  108. }
  109. /** 统计 */
  110. public function count(string $field = '*')
  111. {
  112. $sql = "SELECT COUNT({$field}) AS c FROM `{$this->table}`";
  113. $this->buildWhere($sql);
  114. $row = $this->execute($sql, $this->params)->fetch();
  115. return (int)($row['c'] ?? 0);
  116. }
  117. /** 插入 返回主键 */
  118. public function insert(array $data): string
  119. {
  120. $keys = array_keys($data);
  121. $vals = array_fill(0, count($keys), '?');
  122. $sql = "INSERT INTO `{$this->table}` (`" . implode('`,`', $keys) . "`) VALUES (" . implode(',', $vals) . ")";
  123. $this->execute($sql, array_values($data));
  124. return self::$pdo->lastInsertId();
  125. }
  126. /** 更新 */
  127. public function update(array $data): int
  128. {
  129. $set = [];
  130. foreach ($data as $k => $v) {
  131. $set[] = "`{$k}`=?";
  132. $this->params[] = $v;
  133. }
  134. $sql = "UPDATE `{$this->table}` SET " . implode(',', $set);
  135. $this->buildWhere($sql);
  136. return $this->execute($sql, $this->params)->rowCount();
  137. }
  138. /** 删除 */
  139. public function delete(): int
  140. {
  141. $sql = "DELETE FROM `{$this->table}`";
  142. $this->buildWhere($sql);
  143. return $this->execute($sql, $this->params)->rowCount();
  144. }
  145. /* ---------- 内部 ---------- */
  146. protected static function connect()
  147. {
  148. if (self::$pdo instanceof PDO) {
  149. return;
  150. }
  151. try {
  152. self::$pdo = new PDO(
  153. self::$cfg['dsn'],
  154. self::$cfg['user'],
  155. self::$cfg['pass'],
  156. self::$cfg['options']
  157. );
  158. } catch (PDOException $e) {
  159. die('数据库连接失败: ' . $e->getMessage());
  160. }
  161. }
  162. protected function execute(string $sql, array $params = [])
  163. {
  164. self::connect();
  165. $this->sql = $sql;
  166. $this->params = $params;
  167. if (self::$logEnable) {
  168. $this->log();
  169. }
  170. try {
  171. $stmt = self::$pdo->prepare($sql);
  172. $stmt->execute($params);
  173. $this->reset();
  174. return $stmt;
  175. } catch (PDOException $e) {
  176. $this->reset();
  177. throw new RuntimeException("SQL 错误: " . $e->getMessage() . "\nSQL: $sql\nPARAMS: " . json_encode($params));
  178. }
  179. }
  180. protected function buildWhere(string &$sql)
  181. {
  182. if (!$this->wheres) {
  183. return;
  184. }
  185. $str = [];
  186. foreach ($this->wheres as list($f, $o, $v)) {
  187. $str[] = "`{$f}` {$o} ?";
  188. $this->params[] = $v;
  189. }
  190. $sql .= ' WHERE ' . implode(' AND ', $str);
  191. }
  192. protected function buildOrder(string &$sql)
  193. {
  194. if ($this->orders) {
  195. $sql .= ' ORDER BY ' . implode(',', $this->orders);
  196. }
  197. }
  198. protected function buildLimit(string &$sql)
  199. {
  200. if ($this->limits) {
  201. $sql .= ' LIMIT ' . $this->limits;
  202. }
  203. }
  204. protected function reset()
  205. {
  206. $this->sql = '';
  207. $this->params = [];
  208. $this->wheres = [];
  209. $this->orders = [];
  210. $this->limits = '';
  211. }
  212. protected function log()
  213. {
  214. $txt = date('Y-m-d H:i:s') . ' | ' . $this->sql . ' | ' . json_encode($this->params) . PHP_EOL;
  215. error_log($txt, 3, self::$logFile);
  216. }
  217. }