国内流行的内容管理系统(CMS)多端全媒体解决方案 https://www.dedebiz.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

531 lines
18KB

  1. <?php
  2. if (!defined('DEDEINC')) exit ('dedebiz');
  3. /**
  4. * 系统底层数据库核心,调用这个类前,请先设定这些外部变量
  5. *
  6. * $GLOBALS['cfg_dbhost'];
  7. * $GLOBALS['cfg_dbuser'];
  8. * $GLOBALS['cfg_dbpwd'];
  9. * $GLOBALS['cfg_dbname'];
  10. * $GLOBALS['cfg_dbprefix'];
  11. *
  12. * @version $id:dedesqli.class.php 15:00 2011-1-21 tianya $
  13. * @package DedeBIZ.Libraries
  14. * @copyright Copyright (c) 2022 DedeBIZ.COM
  15. * @license https://www.dedebiz.com/license
  16. * @link https://www.dedebiz.com
  17. */
  18. @set_time_limit(0);
  19. //在工程所有文件中均不需要单独初始化这个类,可直接用$dsql或$db进行操作,为了防止错误,操作完后不必关闭数据库
  20. if (!function_exists("mysqli_init")) {
  21. ShowMsg("尚未发现开启mysqli模块,请在php.ini中启用`extension=mysqli`","javasctipt:;",-1) ;
  22. exit;
  23. }
  24. $dsql = $dsqli = $db = new DedeSqli(FALSE);
  25. /**
  26. * Dede MySQLi数据库类
  27. *
  28. * @package DedeSqli
  29. * @subpackage DedeBIZ.Libraries
  30. * @link https://www.dedebiz.com
  31. */
  32. class DedeSqli
  33. {
  34. var $linkID;
  35. var $dbHost;
  36. var $dbUser;
  37. var $dbPwd;
  38. var $dbName;
  39. var $dbPrefix;
  40. var $result;
  41. var $queryString;
  42. var $parameters;
  43. var $isClose;
  44. var $safeCheck;
  45. var $showError = true;
  46. var $recordLog = false; //记录日志到data/mysqli_record_log.inc便于进行调试
  47. var $isInit = false;
  48. var $pconnect = false;
  49. //用外部定义的变量初始类,并连接数据库
  50. function __construct($pconnect = FALSE, $nconnect = FALSE)
  51. {
  52. $this->isClose = FALSE;
  53. $this->safeCheck = TRUE;
  54. $this->pconnect = $pconnect;
  55. $this->dbHost = $GLOBALS['cfg_dbhost'];
  56. $this->dbUser = $GLOBALS['cfg_dbuser'];
  57. $this->dbPwd = $GLOBALS['cfg_dbpwd'];
  58. $this->dbName = $GLOBALS['cfg_dbname'];
  59. $this->dbPrefix = $GLOBALS['cfg_dbprefix'];
  60. if ($nconnect) {
  61. $this->Init($pconnect);
  62. }
  63. }
  64. function DedeSql($pconnect = FALSE, $nconnect = TRUE)
  65. {
  66. $this->__construct($pconnect, $nconnect);
  67. }
  68. function Init($pconnect = FALSE)
  69. {
  70. $this->linkID = 0;
  71. //$this->queryString = '';
  72. //$this->parameters = Array();
  73. $this->result["me"] = 0;
  74. $this->Open($pconnect);
  75. }
  76. //用指定参数初始数据库信息
  77. function SetSource($host, $username, $pwd, $dbname, $dbprefix = "dede_")
  78. {
  79. $this->dbHost = $host;
  80. $this->dbUser = $username;
  81. $this->dbPwd = $pwd;
  82. $this->dbName = $dbname;
  83. $this->dbPrefix = $dbprefix;
  84. $this->result["me"] = 0;
  85. }
  86. function SelectDB($dbname)
  87. {
  88. mysqli_select_db($this->linkID, $dbname);
  89. }
  90. //设置SQL里的参数
  91. function SetParameter($key, $value)
  92. {
  93. $this->parameters[$key] = $value;
  94. }
  95. //连接数据库
  96. function Open($pconnect = FALSE)
  97. {
  98. global $dsqli;
  99. //连接数据库
  100. if ($dsqli && !$dsqli->isClose && $dsqli->isInit) {
  101. $this->linkID = $dsqli->linkID;
  102. } else {
  103. $i = 0;
  104. @list($dbhost, $dbport) = explode(':', $this->dbHost);
  105. !$dbport && $dbport = 3306;
  106. $this->linkID = mysqli_init();
  107. try {
  108. mysqli_real_connect($this->linkID, $dbhost, $this->dbUser, $this->dbPwd, false, $dbport);
  109. mysqli_errno($this->linkID) != 0 && $this->DisplayError('链接('.$this->pconnect.')到MySQL发生错误');
  110. } catch (Exception $e) {
  111. $this->DisplayError("连接数据库失败,数据库密码不对或数据库服务器出错");
  112. exit;
  113. }
  114. //复制一个对象副本
  115. CopySQLiPoint($this);
  116. }
  117. //处理错误,成功连接则选择数据库
  118. if (!$this->linkID) {
  119. $this->DisplayError("连接数据库失败,数据库密码不对或数据库服务器出错");
  120. exit();
  121. }
  122. $this->isInit = TRUE;
  123. $serverinfo = mysqli_get_server_info($this->linkID);
  124. if (version_compare($serverinfo, '4.1', ">=") && $GLOBALS['cfg_db_language']) {
  125. mysqli_query($this->linkID, "SET character_set_connection=".$GLOBALS['cfg_db_language'].",character_set_results=".$GLOBALS['cfg_db_language'].",character_set_client=binary");
  126. }
  127. if (version_compare($serverinfo, '5.0', ">")) {
  128. mysqli_query($this->linkID, "SET sql_mode=''");
  129. }
  130. if ($this->dbName && !@mysqli_select_db($this->linkID, $this->dbName)) {
  131. $this->DisplayError('连接数据库失败,数据库服务器奔溃');
  132. }
  133. return TRUE;
  134. }
  135. //为了防止采集等需要较长运行时间的程序超时,在运行这类程序时设置系统等待和交互时间
  136. function SetLongLink()
  137. {
  138. if ($this->linkID) {
  139. @mysqli_query($this->linkID, "SET interactive_timeout=3600, wait_timeout=3600 ;");
  140. }
  141. }
  142. //获得错误描述
  143. function GetError()
  144. {
  145. $str = mysqli_error($this->linkID);
  146. return $str;
  147. }
  148. //关闭数据库
  149. //mysql能自动管理非持久连接的连接池
  150. //实际上关闭并无意义并且容易出错,所以取消这函数
  151. function Close($isok = FALSE)
  152. {
  153. $this->FreeResultAll();
  154. if ($isok) {
  155. @mysqli_close($this->linkID);
  156. $this->isClose = TRUE;
  157. $GLOBALS['dsql'] = NULL;
  158. }
  159. }
  160. //定期清理死连接
  161. function ClearErrLink()
  162. {
  163. }
  164. //关闭指定的数据库连接
  165. function CloseLink($dblink)
  166. {
  167. @mysqli_close($dblink);
  168. }
  169. function Esc($_str)
  170. {
  171. global $dsqli;
  172. if (!$dsqli->isInit) {
  173. $this->Init($this->pconnect);
  174. }
  175. if (version_compare(phpversion(), '4.3.0', '>=')) {
  176. return @mysqli_real_escape_string($this->linkID, $_str);
  177. } else {
  178. return @mysqli_escape_string($this->linkID, $_str);
  179. }
  180. }
  181. //执行一个不返回结果的SQL语句,如update,delete,insert等
  182. function ExecuteNoneQuery($sql = '')
  183. {
  184. global $dsqli;
  185. if (!$dsqli->isInit) {
  186. $this->Init($this->pconnect);
  187. }
  188. if ($dsqli->isClose) {
  189. $this->Open(FALSE);
  190. $dsqli->isClose = FALSE;
  191. }
  192. if (!empty($sql)) {
  193. $this->SetQuery($sql);
  194. } else {
  195. return FALSE;
  196. }
  197. if (is_array($this->parameters)) {
  198. foreach ($this->parameters as $key => $value) {
  199. $this->queryString = str_replace("@".$key, "'$value'", $this->queryString);
  200. }
  201. }
  202. //SQL语句安全检查
  203. if ($this->safeCheck) CheckSql($this->queryString, 'update');
  204. $t1 = ExecTime();
  205. $rs = mysqli_query($this->linkID, $this->queryString);
  206. //查询性能测试
  207. if ($this->recordLog) {
  208. $queryTime = ExecTime() - $t1;
  209. $this->RecordLog($queryTime);
  210. }
  211. if (DEBUG_LEVEL === TRUE) {
  212. $queryTime = ExecTime() - $t1;
  213. if (PHP_SAPI === 'cli') {
  214. echo "执行SQL:".$this->queryString.",执行时间:{$queryTime}\r\n";
  215. } else {
  216. echo DedeAlert("执行SQL:".$this->queryString.",执行时间:{$queryTime}", ALERT_SUCCESS);
  217. }
  218. }
  219. return $rs;
  220. }
  221. //执行一个返回影响记录条数的SQL语句,如update,delete,insert等
  222. function ExecuteNoneQuery2($sql = '')
  223. {
  224. global $dsqli;
  225. if (!$dsqli->isInit) {
  226. $this->Init($this->pconnect);
  227. }
  228. if ($dsqli->isClose) {
  229. $this->Open(FALSE);
  230. $dsqli->isClose = FALSE;
  231. }
  232. if (!empty($sql)) {
  233. $this->SetQuery($sql);
  234. }
  235. if (is_array($this->parameters)) {
  236. foreach ($this->parameters as $key => $value) {
  237. $this->queryString = str_replace("@".$key, "'$value'", $this->queryString);
  238. }
  239. }
  240. $t1 = ExecTime();
  241. mysqli_query($this->linkID, $this->queryString);
  242. //查询性能测试
  243. if ($this->recordLog) {
  244. $queryTime = ExecTime() - $t1;
  245. $this->RecordLog($queryTime);
  246. //echo $this->queryString."--{$queryTime}<hr/>\r\n";
  247. }
  248. if (DEBUG_LEVEL === TRUE) {
  249. $queryTime = ExecTime() - $t1;
  250. if (PHP_SAPI === 'cli') {
  251. echo "执行SQL:".$this->queryString.",执行时间:{$queryTime}\r\n";
  252. } else {
  253. echo DedeAlert("执行SQL:".$this->queryString.",执行时间:{$queryTime}", ALERT_SUCCESS);
  254. }
  255. }
  256. return mysqli_affected_rows($this->linkID);
  257. }
  258. function ExecNoneQuery($sql = '')
  259. {
  260. return $this->ExecuteNoneQuery($sql);
  261. }
  262. function GetFetchRow($id = 'me')
  263. {
  264. return @mysqli_fetch_row($this->result[$id]);
  265. }
  266. function GetAffectedRows()
  267. {
  268. return mysqli_affected_rows($this->linkID);
  269. }
  270. //执行一个带返回结果的SQL语句,如SELECT,SHOW等
  271. function Execute($id = "me", $sql = '')
  272. {
  273. global $dsqli;
  274. if (!$dsqli->isInit) {
  275. $this->Init($this->pconnect);
  276. }
  277. if ($dsqli->isClose) {
  278. $this->Open(FALSE);
  279. $dsqli->isClose = FALSE;
  280. }
  281. if (!empty($sql)) {
  282. $this->SetQuery($sql);
  283. }
  284. //SQL语句安全检查
  285. if ($this->safeCheck) {
  286. CheckSql($this->queryString);
  287. }
  288. $t1 = ExecTime();
  289. //var_dump($this->queryString);
  290. $this->result[$id] = mysqli_query($this->linkID, $this->queryString);
  291. //var_dump(mysql_error());
  292. //查询性能测试
  293. if ($this->recordLog) {
  294. $queryTime = ExecTime() - $t1;
  295. $this->RecordLog($queryTime);
  296. //echo $this->queryString."--{$queryTime}<hr/>\r\n";
  297. }
  298. if (DEBUG_LEVEL === TRUE) {
  299. $queryTime = ExecTime() - $t1;
  300. if (PHP_SAPI === 'cli') {
  301. echo "执行SQL:".$this->queryString.",执行时间:{$queryTime}\r\n";
  302. } else {
  303. echo DedeAlert("执行SQL:".$this->queryString.",执行时间:{$queryTime}", ALERT_SUCCESS);
  304. }
  305. }
  306. if ($this->result[$id] === FALSE) {
  307. $this->DisplayError(mysqli_error($this->linkID)." <br>Error sql:<span class='text-primary'>".$this->queryString."</span>");
  308. }
  309. }
  310. function Query($id = "me", $sql = '')
  311. {
  312. $this->Execute($id, $sql);
  313. }
  314. //执行一个SQL语句,返回前一条记录或仅返回一条记录
  315. function GetOne($sql = '', $acctype = MYSQLI_ASSOC)
  316. {
  317. global $dsqli;
  318. //$t1 = ExecTime();
  319. if (!$dsqli->isInit) {
  320. $this->Init($this->pconnect);
  321. }
  322. //echo ExecTime() - $t1;
  323. if ($dsqli->isClose) {
  324. $this->Open(FALSE);
  325. $dsqli->isClose = FALSE;
  326. }
  327. if (!empty($sql)) {
  328. if (!preg_match("/LIMIT/i", $sql)) $this->SetQuery(preg_replace("/[,;]$/i", '', trim($sql))." LIMIT 0,1;");
  329. else $this->SetQuery($sql);
  330. }
  331. $this->Execute("one");
  332. $arr = $this->GetArray("one", $acctype);
  333. if (!is_array($arr)) {
  334. return '';
  335. } else {
  336. @mysqli_free_result($this->result["one"]);
  337. return ($arr);
  338. }
  339. }
  340. //执行一个不与任何表名有关的SQL语句,Create等
  341. function ExecuteSafeQuery($sql, $id = "me")
  342. {
  343. global $dsqli;
  344. if (!$dsqli->isInit) {
  345. $this->Init($this->pconnect);
  346. }
  347. if ($dsqli->isClose) {
  348. $this->Open(FALSE);
  349. $dsqli->isClose = FALSE;
  350. }
  351. $this->result[$id] = @mysqli_query($sql, $this->linkID);
  352. }
  353. //返回当前的一条记录并把游标移向下一记录
  354. //MYSQLI_ASSOC、MYSQLI_NUM、MYSQLI_BOTH
  355. function GetArray($id = "me", $acctype = MYSQLI_ASSOC)
  356. {
  357. //var_dump($this->result);
  358. if ($this->result[$id] === 0) {
  359. return FALSE;
  360. } else {
  361. return @mysqli_fetch_array($this->result[$id], $acctype);
  362. }
  363. }
  364. function GetObject($id = "me")
  365. {
  366. if ($this->result[$id] === 0) {
  367. return FALSE;
  368. } else {
  369. return mysqli_fetch_object($this->result[$id]);
  370. }
  371. }
  372. //检测是否存在某数据表
  373. function IsTable($tbname)
  374. {
  375. global $dsqli;
  376. $prefix = "#@__";
  377. $tbname = str_replace($prefix, $GLOBALS['cfg_dbprefix'], $tbname);
  378. if (!preg_match('/^[\p{L}_][\p{L}\p{N}@$#\-_]*$/u', $tbname)) {
  379. return FALSE;
  380. }
  381. if (!$dsqli->isInit) {
  382. $this->Init($this->pconnect);
  383. }
  384. if (mysqli_num_rows(@mysqli_query($this->linkID, "SHOW TABLES LIKE '".$tbname."'"))) {
  385. return TRUE;
  386. }
  387. return FALSE;
  388. }
  389. //获得MySql的版本号
  390. function GetVersion($isformat = TRUE)
  391. {
  392. global $dsqli;
  393. if (!$dsqli->isInit) {
  394. $this->Init($this->pconnect);
  395. }
  396. if ($dsqli->isClose) {
  397. $this->Open(FALSE);
  398. $dsqli->isClose = FALSE;
  399. }
  400. $rs = mysqli_query($this->linkID, "SELECT VERSION();");
  401. $row = mysqli_fetch_array($rs);
  402. $mysql_version = $row[0];
  403. mysqli_free_result($rs);
  404. if ($isformat) {
  405. $mysql_versions = explode(".", trim($mysql_version));
  406. $mysql_version = number_format($mysql_versions[0].".".$mysql_versions[1], 2);
  407. }
  408. return $mysql_version;
  409. }
  410. //获取特定表的信息
  411. function GetTableFields($tbname, $id = "me")
  412. {
  413. global $dsqli;
  414. if (!$dsqli->isInit) {
  415. $this->Init($this->pconnect);
  416. }
  417. $prefix = "#@__";
  418. $tbname = str_replace($prefix, $GLOBALS['cfg_dbprefix'], $tbname);
  419. $query = "SELECT * FROM {$tbname} LIMIT 0,1";
  420. $this->result[$id] = mysqli_query($this->linkID, $query);
  421. }
  422. //获取字段详细信息
  423. function GetFieldObject($id = "me")
  424. {
  425. return mysqli_fetch_field($this->result[$id]);
  426. }
  427. //获得查询的总记录数
  428. function GetTotalRow($id = "me")
  429. {
  430. if ($this->result[$id] === 0) {
  431. return -1;
  432. } else {
  433. return @mysqli_num_rows($this->result[$id]);
  434. }
  435. }
  436. //获取上一步INSERT操作产生的id
  437. function GetLastID()
  438. {
  439. //如果 AUTO_INCREMENT 的列的类型是 BIGINT,则 mysqli_insert_id() 返回的值不正确
  440. //可以在 SQL 查询中用 MySQL 内部的 SQL 函数 LAST_INSERT_ID() 来替代
  441. //$rs = mysqli_query($this->linkID, "Select LAST_INSERT_ID() as lid");
  442. //$row = mysqli_fetch_array($rs);
  443. //return $row["lid"];
  444. return mysqli_insert_id($this->linkID);
  445. }
  446. //释放记录集占用的资源
  447. function FreeResult($id = "me")
  448. {
  449. @mysqli_free_result($this->result[$id]);
  450. }
  451. function FreeResultAll()
  452. {
  453. if (!is_array($this->result)) {
  454. return '';
  455. }
  456. foreach ($this->result as $vv) {
  457. if ($vv) {
  458. @mysqli_free_result($vv);
  459. }
  460. }
  461. }
  462. //设置SQL语句,会自动把SQL语句里的#@__替换为$this->dbPrefix(在配置文件中为$cfg_dbprefix)
  463. function SetQuery($sql)
  464. {
  465. $prefix = "#@__";
  466. $sql = str_replace($prefix, $GLOBALS['cfg_dbprefix'], $sql);
  467. $this->queryString = $sql;
  468. }
  469. function SetSql($sql)
  470. {
  471. $this->SetQuery($sql);
  472. }
  473. function RecordLog($runtime = 0)
  474. {
  475. global $cfg_cookie_encode;
  476. $enkey = substr(md5(substr($cfg_cookie_encode.'dedebiz', 0, 5)), 0, 10);
  477. $RecordLogFile = DEDEDATA.'/mysqli_record_log_'.$enkey.'.inc';
  478. $url = $this->GetCurUrl();
  479. $savemsg = <<<EOT
  480. ------------------------------------------
  481. SQL:{$this->queryString}
  482. Page:$url
  483. Runtime:$runtime
  484. EOT;
  485. $fp = @fopen($RecordLogFile, 'a');
  486. @fwrite($fp, $savemsg);
  487. @fclose($fp);
  488. }
  489. //显示数据链接错误信息
  490. function DisplayError($msg)
  491. {
  492. global $cfg_cookie_encode;
  493. $enkey = substr(md5(substr($cfg_cookie_encode.'dedebiz', 0, 5)), 0, 10);
  494. $errorTrackFile = DEDEDATA.'/mysqli_error_trace_'.$enkey.'.inc';
  495. if ($this->showError) {
  496. $msg = str_replace(array("\r","\n"),"",addslashes($msg));
  497. ShowMsg("{$msg}", "javascript:;", -1);
  498. exit;
  499. }
  500. $savemsg = 'Page: '.$this->GetCurUrl()."\r\nError: ".$msg."\r\nTime".date('Y-m-d H:i:s');
  501. //保存MySql错误日志
  502. $fp = @fopen($errorTrackFile, 'a');
  503. @fwrite($fp, '<'.'?php exit();'."\r\n/*\r\n{$savemsg}\r\n*/\r\n?".">\r\n");
  504. @fclose($fp);
  505. }
  506. //获得当前的脚本网址
  507. function GetCurUrl()
  508. {
  509. if (!empty($_SERVER["REQUEST_URI"])) {
  510. $scriptName = $_SERVER["REQUEST_URI"];
  511. $nowurl = $scriptName;
  512. } else {
  513. $scriptName = $_SERVER["PHP_SELF"];
  514. if (empty($_SERVER["QUERY_STRING"])) {
  515. $nowurl = $scriptName;
  516. } else {
  517. $nowurl = $scriptName."?".$_SERVER["QUERY_STRING"];
  518. }
  519. }
  520. return $nowurl;
  521. }
  522. }
  523. //复制一个对象副本
  524. function CopySQLiPoint(&$ndsql)
  525. {
  526. $GLOBALS['dsqli'] = $ndsql;
  527. }
  528. ?>