Db.class.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  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. /** IN查询 */
  79. public function in(string $field, array $values): self
  80. {
  81. if (!empty($values)) {
  82. $this->wheres[] = [$field, 'IN', $values];
  83. }
  84. return $this;
  85. }
  86. /** NOT IN查询 */
  87. public function notIn(string $field, array $values): self
  88. {
  89. if (!empty($values)) {
  90. $this->wheres[] = [$field, 'NOT IN', $values];
  91. }
  92. return $this;
  93. }
  94. public function order(string $order): self
  95. {
  96. $this->orders[] = $order;
  97. return $this;
  98. }
  99. public function limit(int $offset, int $length = null): self
  100. {
  101. $this->limits = $length === null ? $offset : "$offset,$length";
  102. return $this;
  103. }
  104. /* ---------- 执行 ---------- */
  105. /** 查多条 */
  106. public function get(array $columns = ['*'])
  107. {
  108. $col = $columns === ['*'] ? '*' : (
  109. count($columns) === 1 ? "`{$columns[0]}`" :
  110. implode('`,`', $columns)
  111. );
  112. $sql = "SELECT {$col} FROM `{$this->table}`";
  113. $this->buildWhere($sql);
  114. $this->buildOrder($sql);
  115. $this->buildLimit($sql);
  116. return $this->execute($sql, $this->params)->fetchAll();
  117. }
  118. /** 查单条 */
  119. public function first(array $columns = ['*'])
  120. {
  121. $this->limit(1);
  122. $list = $this->get($columns);
  123. return $list[0] ?? null;
  124. }
  125. /** 统计 */
  126. public function count(string $field = '*')
  127. {
  128. $sql = "SELECT COUNT({$field}) AS c FROM `{$this->table}`";
  129. $this->buildWhere($sql);
  130. $row = $this->execute($sql, $this->params)->fetch();
  131. return (int)($row['c'] ?? 0);
  132. }
  133. /** 插入 返回主键 */
  134. public function insert(array $data): string
  135. {
  136. $keys = array_keys($data);
  137. $vals = array_fill(0, count($keys), '?');
  138. $sql = "INSERT INTO `{$this->table}` (`" . implode('`,`', $keys) . "`) VALUES (" . implode(',', $vals) . ")";
  139. $this->execute($sql, array_values($data));
  140. return self::$pdo->lastInsertId();
  141. }
  142. /** 更新 */
  143. public function update(array $data): int
  144. {
  145. $set = [];
  146. foreach ($data as $k => $v) {
  147. $set[] = "`{$k}`=?";
  148. $this->params[] = $v;
  149. }
  150. $sql = "UPDATE `{$this->table}` SET " . implode(',', $set);
  151. $this->buildWhere($sql);
  152. return $this->execute($sql, $this->params)->rowCount();
  153. }
  154. /** 删除 */
  155. public function delete(): int
  156. {
  157. $sql = "DELETE FROM `{$this->table}`";
  158. $this->buildWhere($sql);
  159. return $this->execute($sql, $this->params)->rowCount();
  160. }
  161. /* ---------- 内部 ---------- */
  162. protected static function connect()
  163. {
  164. if (self::$pdo instanceof PDO) {
  165. return;
  166. }
  167. try {
  168. self::$pdo = new PDO(
  169. self::$cfg['dsn'],
  170. self::$cfg['user'],
  171. self::$cfg['pass'],
  172. self::$cfg['options']
  173. );
  174. } catch (PDOException $e) {
  175. die('数据库连接失败: ' . $e->getMessage());
  176. }
  177. }
  178. protected function execute(string $sql, array $params = [])
  179. {
  180. self::connect();
  181. $this->sql = $sql;
  182. $this->params = $params;
  183. if (self::$logEnable) {
  184. $this->log();
  185. }
  186. try {
  187. $stmt = self::$pdo->prepare($sql);
  188. $stmt->execute($params);
  189. $this->reset();
  190. return $stmt;
  191. } catch (PDOException $e) {
  192. $this->reset();
  193. throw new RuntimeException("SQL 错误: " . $e->getMessage() . "\nSQL: $sql\nPARAMS: " . json_encode($params));
  194. }
  195. }
  196. protected function buildWhere(string &$sql)
  197. {
  198. if (!$this->wheres) {
  199. return;
  200. }
  201. $str = [];
  202. foreach ($this->wheres as list($f, $o, $v)) {
  203. if (in_array(strtoupper($o), ['IN', 'NOT IN']) && is_array($v)) {
  204. // 处理IN和NOT IN查询,需要特殊处理数组值
  205. $placeholders = [];
  206. foreach ($v as $value) {
  207. $placeholders[] = '?';
  208. $this->params[] = $value;
  209. }
  210. $str[] = "`{$f}` {$o} (" . implode(',', $placeholders) . ")";
  211. } else {
  212. // 普通查询条件
  213. $str[] = "`{$f}` {$o} ?";
  214. $this->params[] = $v;
  215. }
  216. }
  217. $sql .= ' WHERE ' . implode(' AND ', $str);
  218. }
  219. protected function buildOrder(string &$sql)
  220. {
  221. if ($this->orders) {
  222. $sql .= ' ORDER BY ' . implode(',', $this->orders);
  223. }
  224. }
  225. protected function buildLimit(string &$sql)
  226. {
  227. if ($this->limits) {
  228. $sql .= ' LIMIT ' . $this->limits;
  229. }
  230. }
  231. protected function reset()
  232. {
  233. $this->sql = '';
  234. $this->params = [];
  235. $this->wheres = [];
  236. $this->orders = [];
  237. $this->limits = '';
  238. }
  239. protected function log()
  240. {
  241. $txt = date('Y-m-d H:i:s') . ' | ' . $this->sql . ' | ' . json_encode($this->params) . PHP_EOL;
  242. error_log($txt, 3, self::$logFile);
  243. }
  244. }