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

559 lines
20KB

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