mysqli.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. <?php
  2. /**
  3. * mysqli驱动
  4. *
  5. *
  6. * @package
  7. */
  8. defined('InShopNC') or exit('Access Invalid!');
  9. require_once(BASE_CORE_PATH . '/framework/function/statistic_tm.php');
  10. class Db
  11. {
  12. const ErrUnConnect = 2006;
  13. const ErrLock = 1205;
  14. private static $link = [];
  15. private static $ifTransacting = 0;
  16. private function __construct()
  17. {
  18. if (!extension_loaded('mysqli')) {
  19. throw_exception("Db Error: mysqli is not install");
  20. } else {
  21. self::init_link();
  22. }
  23. }
  24. private static function init_link()
  25. {
  26. if (!isset(self::$link['master']) || (is_object(self::$link['master']) == false)) {
  27. self::connect('master');
  28. }
  29. if (!isset(self::$link['slave']) || (is_object(self::$link['slave']) == false)) {
  30. self::connect('slave');
  31. }
  32. }
  33. private static function connect($host = 'slave')
  34. {
  35. self::closeLink($host);
  36. $conf = C('db.' . $host);
  37. self::$link[$host] = mysqli_init();
  38. self::$link[$host]->real_connect($conf['dbhost'], $conf['dbuser'], $conf['dbpwd'], $conf['dbname'], $conf['dbport'],NULL,MYSQLI_CLIENT_FOUND_ROWS);
  39. Log::record("{$host}:{$conf['dbhost']}",Log::DEBUG);
  40. if (mysqli_connect_errno()) {
  41. $err_no = mysqli_connect_errno();
  42. $error = mysqli_connect_error();
  43. throw_exception("Db Error: {$host} database connect failed errno={$err_no} err={$error}");
  44. }
  45. switch (strtoupper($conf['dbcharset'])) {
  46. case 'UTF-8':
  47. $query_string = "
  48. SET CHARACTER_SET_CLIENT = utf8mb4,
  49. CHARACTER_SET_CONNECTION = utf8mb4,
  50. CHARACTER_SET_DATABASE = utf8mb4,
  51. CHARACTER_SET_RESULTS = utf8mb4,
  52. CHARACTER_SET_SERVER = utf8mb4,
  53. COLLATION_CONNECTION = utf8mb4_unicode_ci,
  54. COLLATION_DATABASE = utf8mb4_unicode_ci,
  55. COLLATION_SERVER = utf8mb4_unicode_ci,
  56. sql_mode=''";
  57. break;
  58. case 'GBK':
  59. $query_string = "
  60. SET CHARACTER_SET_CLIENT = gbk,
  61. CHARACTER_SET_CONNECTION = gbk,
  62. CHARACTER_SET_DATABASE = gbk,
  63. CHARACTER_SET_RESULTS = gbk,
  64. CHARACTER_SET_SERVER = gbk,
  65. COLLATION_CONNECTION = gbk_chinese_ci,
  66. COLLATION_DATABASE = gbk_chinese_ci,
  67. COLLATION_SERVER = gbk_chinese_ci,
  68. sql_mode=''";
  69. break;
  70. default:
  71. $error = "Db Error: charset is Invalid";
  72. throw_exception($error);
  73. }
  74. //进行编码声明
  75. if (!self::$link[$host]->query($query_string)) {
  76. throw_exception("Db Error: " . mysqli_error(self::$link[$host]));
  77. }
  78. }
  79. private static function closeLink($host)
  80. {
  81. if (isset(self::$link[$host]) && is_object(self::$link[$host])) {
  82. self::$link[$host]->close();
  83. }
  84. self::$link[$host] = null;
  85. }
  86. public static function ping($host = 'master')
  87. {
  88. if (is_object(self::$link[$host])) {
  89. return self::$link[$host]->ping();
  90. }
  91. }
  92. /**
  93. * 执行查询
  94. *
  95. * @param string $sql
  96. * @return mixed
  97. */
  98. public static function query($sql, $host = 'master')
  99. {
  100. self::init_link();
  101. $count = 0;
  102. do
  103. {
  104. $staer = new statistic_tm();
  105. $query = self::$link[$host]->query($sql);
  106. $staer->end();
  107. if ($query === false)
  108. {
  109. $eno = mysqli_errno(self::$link[$host]);
  110. $emsg = mysqli_error(self::$link[$host]);
  111. $error = "DbError eno={$eno} msg={$emsg}";
  112. Log::record("{$error} sql={$sql}", Log::ERR);
  113. if ($eno == self::ErrUnConnect) {
  114. if ($count > 0) return false;
  115. self::connect($host);
  116. $count++;
  117. } elseif (C('debug')) {
  118. throw_exception($error . '<br/>' . $sql);
  119. return false;
  120. } elseif (self::ping($host) == false) {
  121. if ($count > 0) return false;
  122. self::connect($host);
  123. $count++;
  124. } else {
  125. return false;
  126. }
  127. }
  128. else {
  129. Log::record($sql . " [ RunTime:" . $staer->elapsed(6) . "s ]", Log::SQL);
  130. return $query;
  131. }
  132. } while (true);
  133. }
  134. /**
  135. * 取得数组
  136. *
  137. * @param string $sql
  138. * @return array
  139. */
  140. public static function getAll($sql, $host = 'slave')
  141. {
  142. $result = self::query($sql, $host);
  143. if ($result === false) return [];
  144. $array = [];
  145. while ($tmp = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
  146. $array[] = $tmp;
  147. }
  148. return !empty($array) ? $array : null;
  149. }
  150. /**
  151. * SELECT查询
  152. *
  153. * @param array $param 参数
  154. * @param object $obj_page 分类对象
  155. * @return array
  156. */
  157. public static function select($param, $obj_page = '', $host = 'slave')
  158. {
  159. static $_cache = [];
  160. if (empty($param)) throw_exception('Db Error: select param is empty!');
  161. if (empty($param['field'])) {
  162. $param['field'] = '*';
  163. }
  164. if (empty($param['count'])) {
  165. $param['count'] = 'count(*)';
  166. }
  167. if (isset($param['index'])) {
  168. $param['index'] = 'USE INDEX (' . $param['index'] . ')';
  169. }
  170. if (trim($param['where']) != '') {
  171. if (strtoupper(substr(trim($param['where']), 0, 5)) != 'WHERE') {
  172. if (strtoupper(substr(trim($param['where']), 0, 3)) == 'AND') {
  173. $param['where'] = substr(trim($param['where']), 3);
  174. }
  175. $param['where'] = 'WHERE ' . $param['where'];
  176. }
  177. } else {
  178. $param['where'] = '';
  179. }
  180. $param['where_group'] = '';
  181. if (!empty($param['group'])) {
  182. $param['where_group'] .= ' group by ' . $param['group'];
  183. }
  184. $param['where_order'] = '';
  185. if (!empty($param['order'])) {
  186. $param['where_order'] .= ' order by ' . $param['order'];
  187. }
  188. //判断是否是联表
  189. $tmp_table = explode(',', $param['table']);
  190. if (!empty($tmp_table) && count($tmp_table) > 1) {
  191. //判断join表数量和join条件是否一致
  192. if ((count($tmp_table) - 1) != count($param['join_on'])) {
  193. throw_exception('Db Error: join number is wrong!');
  194. }
  195. //trim 掉空白字符
  196. foreach ($tmp_table as $key => $val) {
  197. $tmp_table[$key] = trim($val);
  198. }
  199. //拼join on 语句
  200. $tmp_sql = '';
  201. for ($i = 1; $i < count($tmp_table); $i++) {
  202. $tmp_sql .= $param['join_type'] . ' `' . DBPRE . $tmp_table[$i] . '` as `' . $tmp_table[$i] . '` ON ' . $param['join_on'][$i - 1] . ' ';
  203. }
  204. $sql = 'SELECT ' . $param['field'] . ' FROM `' . DBPRE . $tmp_table[0] . '` as `' . $tmp_table[0] . '` ' . $tmp_sql . ' ' . $param['where'] . $param['where_group'] . $param['where_order'];
  205. //如果有分页,那么计算信息总数
  206. $count_sql = 'SELECT ' . $param['count'] . ' as count FROM `' . DBPRE . $tmp_table[0] . '` as `' . $tmp_table[0] . '` ' . $tmp_sql . ' ' . $param['where'] . $param['where_group'];
  207. } else {
  208. $sql = 'SELECT ' . $param['field'] . ' FROM `' . DBPRE . $param['table'] . '` as `' . $param['table'] . '` ' . $param['index'] . ' ' . $param['where'] . $param['where_group'] . $param['where_order'];
  209. $count_sql = 'SELECT ' . $param['count'] . ' as count FROM `' . DBPRE . $param['table'] . '` as `' . $param['table'] . '` ' . $param['index'] . ' ' . $param['where'];
  210. }
  211. //limit ,如果有分页对象的话,那么优先分页对象
  212. if ($obj_page instanceof Page) {
  213. $count_query = self::query($count_sql, $host);
  214. $count_fetch = mysqli_fetch_array($count_query, MYSQLI_ASSOC);
  215. $obj_page->setTotalNum($count_fetch['count']);
  216. $param['limit'] = $obj_page->getLimitStart() . "," . $obj_page->getEachNum();
  217. }
  218. if ($param['limit'] != '') {
  219. $sql .= ' limit ' . $param['limit'];
  220. }
  221. if ($param['cache'] !== false) {
  222. $key = is_string($param['cache_key']) ? $param['cache_key'] : md5($sql);
  223. if (isset($_cache[$key])) return $_cache[$key];
  224. }
  225. $result = self::query($sql, $host);
  226. if ($result === false) {
  227. $result = [];
  228. }
  229. $array = [];
  230. while ($tmp = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
  231. $array[] = $tmp;
  232. }
  233. if ($param['cache'] !== false && !isset($_cache[$key])) {
  234. $_cache[$key] = $array;
  235. }
  236. return $array;
  237. }
  238. /**
  239. * 插入操作
  240. *
  241. * @param string $table_name 表名
  242. * @param array $insert_array 待插入数据
  243. * @return mixed
  244. */
  245. public static function insert($table_name, $insert_array = [], $host = 'master')
  246. {
  247. if (!is_array($insert_array)) return false;
  248. $fields = [];
  249. $value = [];
  250. foreach ($insert_array as $key => $val) {
  251. $fields[] = self::parseKey($key);
  252. $value[] = self::parseValue($val);
  253. }
  254. $sql = 'INSERT INTO `' . DBPRE . $table_name . '` (' . implode(',', $fields) . ') VALUES(' . implode(',', $value) . ')';
  255. //当数据库没有自增ID的情况下,返回是否成功
  256. $result = self::query($sql, $host);
  257. $insert_id = self::getLastId($host);
  258. return $insert_id ? $insert_id : $result;
  259. }
  260. /**
  261. * 批量插入
  262. *
  263. * @param string $table_name 表名
  264. * @param array $insert_array 待插入数据
  265. * @return mixed
  266. */
  267. public static function insertAll($table_name, $insert_array = [], $host = 'master')
  268. {
  269. if (!is_array($insert_array[0])) return false;
  270. $fields = array_keys($insert_array[0]);
  271. array_walk($fields, [__CLASS__, 'parseKey']);
  272. $values = [];
  273. foreach ($insert_array as $data) {
  274. $value = array();
  275. foreach ($data as $key => $val) {
  276. $val = self::parseValue($val);
  277. if (is_scalar($val)) {
  278. $value[] = $val;
  279. }
  280. }
  281. $values[] = '(' . implode(',', $value) . ')';
  282. }
  283. $sql = 'INSERT INTO `' . DBPRE . $table_name . '` (' . implode(',', $fields) . ') VALUES ' . implode(',', $values);
  284. $result = self::query($sql, $host);
  285. $insert_id = self::getLastId($host);
  286. return $insert_id ? $insert_id : $result;
  287. }
  288. /**
  289. * 更新操作
  290. *
  291. * @param string $table_name 表名
  292. * @param array $update_array 待更新数据
  293. * @param string $where 执行条件
  294. * @return bool
  295. */
  296. public static function update($table_name, $update_array = [], $where = '', $host = 'master')
  297. {
  298. if (!is_array($update_array)) return false;
  299. $string_value = '';
  300. foreach ($update_array as $k => $v) {
  301. if (is_array($v)) {
  302. switch ($v['sign']) {
  303. case 'increase':
  304. $string_value .= " $k = $k + " . $v['value'] . ",";
  305. break;
  306. case 'decrease':
  307. $string_value .= " $k = $k - " . $v['value'] . ",";
  308. break;
  309. case 'calc':
  310. $string_value .= " $k = " . $v['value'] . ",";
  311. break;
  312. default:
  313. $string_value .= " $k = " . self::parseValue($v['value']) . ",";
  314. }
  315. } else {
  316. $string_value .= " $k = " . self::parseValue($v) . ",";
  317. }
  318. }
  319. $string_value = trim(trim($string_value), ',');
  320. if (trim($where) != '') {
  321. if (strtoupper(substr(trim($where), 0, 5)) != 'WHERE') {
  322. if (strtoupper(substr(trim($where), 0, 3)) == 'AND') {
  323. $where = substr(trim($where), 3);
  324. }
  325. $where = ' WHERE ' . $where;
  326. }
  327. }
  328. $sql = 'UPDATE `' . DBPRE . $table_name . '` AS `' . $table_name . '` SET ' . $string_value . ' ' . $where;
  329. $result = self::query($sql, $host);
  330. return $result;
  331. }
  332. /**
  333. * 删除操作
  334. *
  335. * @param string $table_name 表名
  336. * @param string $where 执行条件
  337. * @return bool
  338. */
  339. public static function delete($table_name, $where = '', $host = 'master')
  340. {
  341. if (trim($where) != '') {
  342. if (strtoupper(substr(trim($where), 0, 5)) != 'WHERE') {
  343. if (strtoupper(substr(trim($where), 0, 3)) == 'AND') {
  344. $where = substr(trim($where), 3);
  345. }
  346. $where = ' WHERE ' . $where;
  347. }
  348. $sql = 'DELETE FROM `' . DBPRE . $table_name . '` ' . $where;
  349. return self::query($sql, $host);
  350. } else {
  351. throw_exception('Db Error: the condition of delete is empty!');
  352. }
  353. }
  354. /**
  355. * 取得上一步插入产生的ID
  356. *
  357. * @return int
  358. */
  359. public static function getLastId($host = 'master')
  360. {
  361. $id = mysqli_insert_id(self::$link[$host]);
  362. if (!$id) {
  363. $result = self::query('SELECT last_insert_id() as id', $host);
  364. if ($result === false) return false;
  365. $id = mysqli_fetch_array($result, MYSQLI_ASSOC);
  366. $id = $id['id'];
  367. }
  368. return $id;
  369. }
  370. /**
  371. * 取得一行信息
  372. *
  373. * @param array $param
  374. * @param string $fields
  375. * @return array
  376. */
  377. public static function getRow($param, $fields = '*', $host = 'slave')
  378. {
  379. $table = $param['table'];
  380. $wfield = $param['field'];
  381. $value = $param['value'];
  382. if (is_array($wfield)) {
  383. $where = [];
  384. foreach ($wfield as $k => $v) {
  385. $where[] = $v . "='" . $value[$k] . "'";
  386. }
  387. $where = implode(' and ', $where);
  388. } else {
  389. $where = $wfield . "='" . $value . "'";
  390. }
  391. $sql = "SELECT " . $fields . " FROM `" . DBPRE . $table . "` WHERE " . $where;
  392. $result = self::query($sql, $host);
  393. if ($result === false) return [];
  394. return mysqli_fetch_array($result, MYSQLI_ASSOC);
  395. }
  396. /**
  397. * 执行REPLACE操作
  398. *
  399. * @param string $table_name 表名
  400. * @param array $replace_array 待更新的数据
  401. * @return bool
  402. */
  403. public static function replace($table_name, $replace_array = [], $host = 'master')
  404. {
  405. if (!empty($replace_array)) {
  406. $string_field = '';
  407. $string_value = '';
  408. foreach ($replace_array as $k => $v) {
  409. $string_field .= " $k ,";
  410. $string_value .= " '" . $v . "',";
  411. }
  412. $sql = 'REPLACE INTO `' . DBPRE . $table_name . '` (' . trim($string_field, ', ') . ') VALUES(' . trim($string_value, ', ') . ')';
  413. return self::query($sql, $host);
  414. } else {
  415. return false;
  416. }
  417. }
  418. /**
  419. * 返回单表查询记录数量
  420. *
  421. * @param string $table 表名
  422. * @param $condition mixed 查询条件,可以为空,也可以为数组或字符串
  423. * @return int
  424. */
  425. public static function getCount($table, $condition = null, $host = 'slave')
  426. {
  427. if (!empty($condition) && is_array($condition)) {
  428. $where = '';
  429. foreach ($condition as $key => $val) {
  430. self::parseKey($key);
  431. $val = self::parseValue($val);
  432. $where .= ' AND ' . $key . '=' . $val;
  433. }
  434. $where = ' WHERE ' . substr($where, 4);
  435. } elseif (is_string($condition)) {
  436. if (strtoupper(substr(trim($condition), 0, 3)) == 'AND') {
  437. $where = ' WHERE ' . substr(trim($condition), 4);
  438. } else {
  439. $where = ' WHERE ' . $condition;
  440. }
  441. }
  442. $sql = 'SELECT COUNT(*) as `count` FROM `' . DBPRE . $table . '` as `' . $table . '` ' . (isset($where) ? $where : '');
  443. $result = self::query($sql, $host);
  444. if ($result === false) return 0;
  445. $result = mysqli_fetch_array($result, MYSQLI_ASSOC);
  446. return $result['count'];
  447. }
  448. /**
  449. * 执行SQL语句
  450. *
  451. * @param string $sql 待执行的SQL
  452. * @return
  453. */
  454. public static function execute($sql, $host = 'master')
  455. {
  456. $result = self::query($sql, $host);
  457. return $result;
  458. }
  459. /**
  460. * 列出所有表
  461. *
  462. * @return array
  463. */
  464. public static function showTables($host = 'slave')
  465. {
  466. $sql = 'SHOW TABLES';
  467. $result = self::query($sql, $host);
  468. if ($result === false) return [];
  469. $array = [];
  470. while ($tmp = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
  471. $array[] = $tmp;
  472. }
  473. return $array;
  474. }
  475. /**
  476. * 显示建表语句
  477. *
  478. * @param string $table
  479. * @return string
  480. */
  481. public static function showCreateTable($table, $host = 'slave')
  482. {
  483. $sql = 'SHOW CREATE TABLE `' . DBPRE . $table . '`';
  484. $result = self::query($sql, $host);
  485. if ($result === false) return '';
  486. $result = mysqli_fetch_array($result, MYSQLI_ASSOC);
  487. return $result['Create Table'];
  488. }
  489. /**
  490. * 显示表结构信息
  491. *
  492. * @param string $table
  493. * @return array
  494. */
  495. public static function showColumns($table, $host = 'slave')
  496. {
  497. $sql = 'SHOW COLUMNS FROM `' . DBPRE . $table . '`';
  498. $result = self::query($sql, $host);
  499. if ($result === false) return [];
  500. $array = [];
  501. while ($tmp = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
  502. $array[$tmp['Field']] = array(
  503. 'name' => $tmp['Field'],
  504. 'type' => $tmp['Type'],
  505. 'null' => $tmp['Null'],
  506. 'default' => $tmp['Default'],
  507. 'primary' => (strtolower($tmp['Key']) == 'pri'),
  508. 'autoinc' => (strtolower($tmp['Extra']) == 'auto_increment'),
  509. );
  510. }
  511. return $array;
  512. }
  513. /**
  514. * 取得服务器信息
  515. *
  516. * @return string
  517. */
  518. public static function getServerInfo($host = 'slave')
  519. {
  520. self::init_link();
  521. $result = mysqli_get_server_info(self::$link[$host]);
  522. return $result;
  523. }
  524. /**
  525. * 格式化字段
  526. *
  527. * @param string $key 字段名
  528. * @return string
  529. */
  530. public static function parseKey(&$key)
  531. {
  532. $key = trim($key);
  533. if (!preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {
  534. $key = '`' . $key . '`';
  535. }
  536. return $key;
  537. }
  538. /**
  539. * 格式化值
  540. *
  541. * @param mixed $value
  542. * @return mixed
  543. */
  544. public static function parseValue($value)
  545. {
  546. $value = addslashes(stripslashes($value));//重新加斜线,防止从数据库直接读取出错
  547. return "'" . $value . "'";
  548. }
  549. public static function beginTransaction($host = 'master')
  550. {
  551. self::init_link();
  552. if (self::$ifTransacting === 0) {
  553. self::$link[$host]->autocommit(false);
  554. Log::record("transaction begin",Log::DEBUG);
  555. }
  556. self::$ifTransacting += 1;
  557. }
  558. public static function commit($host = 'master')
  559. {
  560. self::$ifTransacting -= 1;
  561. if (self::$ifTransacting === 0)
  562. {
  563. $result = self::$link[$host]->commit();
  564. self::$link[$host]->autocommit(true);
  565. Log::record("transaction commit",Log::DEBUG);
  566. if (!$result) {
  567. $err = mysqli_error(self::$link[$host]);
  568. self::connect($host);
  569. throw_exception("Db Error: {$err}");
  570. }
  571. }
  572. }
  573. public static function rollback($host = 'master')
  574. {
  575. self::$ifTransacting -= 1;
  576. if (self::$ifTransacting === 0)
  577. {
  578. $result = self::$link[$host]->rollback();
  579. $fsuccess = self::$link[$host]->autocommit(true);
  580. Log::record("transaction rollback",Log::DEBUG);
  581. if (!$result || !$fsuccess) {
  582. $err = mysqli_error(self::$link[$host]);
  583. self::connect($host);
  584. throw_exception("Db Error: {$err}");
  585. }
  586. }
  587. }
  588. public static function affected_rows($host = 'master')
  589. {
  590. return self::$link[$host]->affected_rows;
  591. }
  592. }