国内流行的内容管理系统(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.

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