db.class.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  1. <?php
  2. /**
  3. * [WeEngine System] Copyright (c) 2014 WE7.CC
  4. * WeEngine is NOT a free software, it under the license terms, visited http://www.we7.cc/ for more details.
  5. */
  6. defined('IN_IA') or exit('Access Denied');
  7. define('PDO_DEBUG', true);
  8. class DB {
  9. protected $pdo;
  10. protected $cfg;
  11. protected $tablepre;
  12. protected $result;
  13. protected $statement;
  14. protected $errors = array();
  15. protected $link = array();
  16. protected $name = '';
  17. public function getPDO() {
  18. return $this->pdo;
  19. }
  20. public function __construct($name = 'master') {
  21. global $_W;
  22. $this->cfg = $_W['config']['db'];
  23. $this->name = $name;
  24. unset($_W['config']['db']);
  25. $_W['config']['db'] = array(
  26. 'tablepre' => $this->cfg['master']['tablepre'] ?: $this->cfg['tablepre'],
  27. 'slave_status' => $this->cfg['slave_status']
  28. );
  29. $this->connect($name);
  30. }
  31. public function reConnect($errorInfo, $params) {
  32. if (in_array($errorInfo[1], array(1317, 2013))) {
  33. $this->pdo = null;
  34. $this->connect($this->name);
  35. $method = $params['method'];
  36. unset($params['method']);
  37. return call_user_func_array(array($this, $method), $params);
  38. }
  39. return false;
  40. }
  41. public function connect($name = 'master') {
  42. global $_W;
  43. if (is_array($name)) {
  44. $cfg = $name;
  45. } else {
  46. $cfg = $this->cfg[$name];
  47. }
  48. $this->tablepre = $cfg['tablepre'];
  49. if (empty($cfg)) {
  50. exit("The master database is not found, Please checking 'data/config.php'");
  51. }
  52. $dsn = "mysql:dbname={$cfg['database']};host={$cfg['host']};port={$cfg['port']};charset={$cfg['charset']}";
  53. $dbclass = '';
  54. $options = array();
  55. if (class_exists('PDO')) {
  56. if (extension_loaded('pdo_mysql') && in_array('mysql', PDO::getAvailableDrivers())) {
  57. $dbclass = 'PDO';
  58. $options = array(PDO::ATTR_PERSISTENT => $cfg['pconnect']);
  59. } else {
  60. if (!class_exists('_PDO')) {
  61. load()->library('pdo');
  62. }
  63. $dbclass = '_PDO';
  64. }
  65. } else {
  66. load()->library('pdo');
  67. $dbclass = 'PDO';
  68. }
  69. try {
  70. $pdo = new $dbclass($dsn, $cfg['username'], $cfg['password'], $options);
  71. } catch (\Exception $e) {
  72. return error(-1, '数据库连接失败,请联系管理员处理');
  73. }
  74. $this->pdo = $pdo;
  75. $sql = "SET NAMES '{$cfg['charset']}';";
  76. $this->pdo->exec($sql);
  77. $this->pdo->exec("SET sql_mode='';");
  78. if ('root' == $cfg['username'] && in_array($cfg['host'], array('localhost', '127.0.0.1'))) {
  79. $this->pdo->exec('SET GLOBAL max_allowed_packet = 2*1024*1024*10;');
  80. }
  81. if (is_string($name)) {
  82. $this->link[$name] = $this->pdo;
  83. }
  84. $this->logging($sql);
  85. }
  86. public function prepare($sql) {
  87. $sqlsafe = SqlPaser::checkquery($sql);
  88. if (is_error($sqlsafe)) {
  89. trigger_error($sqlsafe['message'], E_USER_ERROR);
  90. return false;
  91. }
  92. $statement = $this->pdo->prepare($sql);
  93. return $statement;
  94. }
  95. public function query($sql, $params = array()) {
  96. $sqlsafe = SqlPaser::checkquery($sql);
  97. if (is_error($sqlsafe)) {
  98. trigger_error($sqlsafe['message'], E_USER_ERROR);
  99. return false;
  100. }
  101. $starttime = intval(microtime(true));
  102. if (empty($params)) {
  103. $result = $this->pdo->exec($sql);
  104. $error_info = $this->pdo->errorInfo();
  105. $this->logging($sql, array(), $this->pdo->errorInfo());
  106. if (in_array($error_info[1], array(1317, 2013))) {
  107. $reConnect = $this->reConnect($error_info, array(
  108. 'method' => __METHOD__,
  109. 'sql' => $sql,
  110. 'params' => $params,
  111. ));
  112. return empty($reConnect) ? false : $reConnect;
  113. }
  114. return $result;
  115. }
  116. $statement = $this->prepare($sql);
  117. $result = $statement->execute($params);
  118. $this->logging($sql, $params, $statement->errorInfo());
  119. $endtime = intval(microtime(true));
  120. $this->performance($sql, $endtime - $starttime);
  121. $error_info = $statement->errorInfo();
  122. if (in_array($error_info[1], array(1317, 2013))) {
  123. $reConnect = $this->reConnect($error_info, array(
  124. 'method' => __METHOD__,
  125. 'sql' => $sql,
  126. 'params' => $params,
  127. ));
  128. return empty($reConnect) ? false : $reConnect;
  129. } else {
  130. return $statement->rowCount();
  131. }
  132. }
  133. public function fetchcolumn($sql, $params = array(), $column = 0) {
  134. $starttime = intval(microtime(true));
  135. $statement = $this->prepare($sql);
  136. $result = $statement->execute($params);
  137. $this->logging($sql, $params, $statement->errorInfo());
  138. $endtime = intval(microtime(true));
  139. $this->performance($sql, $endtime - $starttime);
  140. $error_info = $statement->errorInfo();
  141. if (in_array($error_info[1], array(1317, 2013))) {
  142. $reConnect = $this->reConnect($error_info, array(
  143. 'method' => __METHOD__,
  144. 'sql' => $sql,
  145. 'params' => $params,
  146. 'column' => $column,
  147. ));
  148. return empty($reConnect) ? false : $reConnect;
  149. } else {
  150. $data = $statement->fetchColumn($column);
  151. return $data;
  152. }
  153. }
  154. public function fetch($sql, $params = array()) {
  155. $starttime = intval(microtime(true));
  156. $statement = $this->prepare($sql);
  157. $result = $statement->execute($params);
  158. $this->logging($sql, $params, $statement->errorInfo());
  159. $endtime = intval(microtime(true));
  160. $this->performance($sql, intval($endtime - $starttime));
  161. $error_info = $statement->errorInfo();
  162. if (in_array($error_info[1], array(1317, 2013))) {
  163. $reConnect = $this->reConnect($error_info, array(
  164. 'method' => __METHOD__,
  165. 'sql' => $sql,
  166. 'params' => $params,
  167. ));
  168. return empty($reConnect) ? false : $reConnect;
  169. } else {
  170. $data = $statement->fetch(pdo::FETCH_ASSOC);
  171. return $data;
  172. }
  173. }
  174. public function fetchall($sql, $params = array(), $keyfield = '') {
  175. $starttime = intval(microtime(true));
  176. $statement = $this->prepare($sql);
  177. $result = $statement->execute($params);
  178. $this->logging($sql, $params, $statement->errorInfo());
  179. $endtime = intval(microtime(true));
  180. $this->performance($sql, $endtime - $starttime);
  181. $error_info = $statement->errorInfo();
  182. if (in_array($error_info[1], array(1317, 2013))) {
  183. $reConnect = $this->reConnect($error_info, array(
  184. 'method' => __METHOD__,
  185. 'sql' => $sql,
  186. 'params' => $params,
  187. 'keyfield' => $keyfield,
  188. ));
  189. return empty($reConnect) ? false : $reConnect;
  190. } else {
  191. if (empty($keyfield)) {
  192. $result = $statement->fetchAll(pdo::FETCH_ASSOC);
  193. } else {
  194. $temp = $statement->fetchAll(pdo::FETCH_ASSOC);
  195. $result = array();
  196. if (!empty($temp)) {
  197. foreach ($temp as $key => &$row) {
  198. if (isset($row[$keyfield])) {
  199. $result[$row[$keyfield]] = $row;
  200. } else {
  201. $result[] = $row;
  202. }
  203. }
  204. }
  205. }
  206. return $result;
  207. }
  208. }
  209. public function get($tablename, $params = array(), $fields = array(), $orderby = array()) {
  210. $select = SqlPaser::parseSelect($fields);
  211. $condition = SqlPaser::parseParameter($params, 'AND');
  212. $orderbysql = SqlPaser::parseOrderby($orderby);
  213. $sql = "{$select} FROM " . $this->tablename($tablename) . (!empty($condition['fields']) ? " WHERE {$condition['fields']}" : '') . " $orderbysql LIMIT 1";
  214. return $this->fetch($sql, $condition['params']);
  215. }
  216. public function getall($tablename, $params = array(), $fields = array(), $keyfield = '', $orderby = array(), $limit = array()) {
  217. $select = SqlPaser::parseSelect($fields);
  218. $condition = SqlPaser::parseParameter($params, 'AND');
  219. $limitsql = SqlPaser::parseLimit($limit);
  220. $orderbysql = SqlPaser::parseOrderby($orderby);
  221. $sql = "{$select} FROM " . $this->tablename($tablename) . (!empty($condition['fields']) ? " WHERE {$condition['fields']}" : '') . $orderbysql . $limitsql;
  222. return $this->fetchall($sql, $condition['params'], $keyfield);
  223. }
  224. public function getslice($tablename, $params = array(), $limit = array(), &$total = null, $fields = array(), $keyfield = '', $orderby = array()) {
  225. $select = SqlPaser::parseSelect($fields);
  226. $condition = SqlPaser::parseParameter($params, 'AND');
  227. $limitsql = SqlPaser::parseLimit($limit);
  228. if (!empty($orderby)) {
  229. if (is_array($orderby)) {
  230. $orderbysql = implode(',', $orderby);
  231. } else {
  232. $orderbysql = $orderby;
  233. }
  234. }
  235. $sql = "{$select} FROM " . $this->tablename($tablename) . (!empty($condition['fields']) ? " WHERE {$condition['fields']}" : '') . (!empty($orderbysql) ? " ORDER BY $orderbysql " : '') . $limitsql;
  236. $total = pdo_fetchcolumn('SELECT COUNT(*) FROM ' . tablename($tablename) . (!empty($condition['fields']) ? " WHERE {$condition['fields']}" : ''), $condition['params']);
  237. return $this->fetchall($sql, $condition['params'], $keyfield);
  238. }
  239. public function getcolumn($tablename, $params = array(), $field = '') {
  240. $result = $this->get($tablename, $params, $field);
  241. if (!empty($result)) {
  242. if (strexists($field, '(')) {
  243. return array_shift($result);
  244. } else {
  245. return $result[$field];
  246. }
  247. } else {
  248. return false;
  249. }
  250. }
  251. public function update($table, $data = array(), $params = array(), $glue = 'AND') {
  252. $fields = SqlPaser::parseParameter($data, ',');
  253. $condition = SqlPaser::parseParameter($params, $glue);
  254. $params = array_merge($fields['params'], $condition['params']);
  255. $sql = 'UPDATE ' . $this->tablename($table) . " SET {$fields['fields']}";
  256. $sql .= $condition['fields'] ? ' WHERE ' . $condition['fields'] : '';
  257. return $this->query($sql, $params);
  258. }
  259. public function insert($table, $data = array(), $replace = false) {
  260. $cmd = $replace ? 'REPLACE INTO' : 'INSERT INTO';
  261. $condition = SqlPaser::parseParameter($data, ',');
  262. return $this->query("$cmd " . $this->tablename($table) . " SET {$condition['fields']}", $condition['params']);
  263. }
  264. public function insertid() {
  265. return $this->pdo->lastInsertId();
  266. }
  267. public function delete($table, $params = array(), $glue = 'AND') {
  268. $condition = SqlPaser::parseParameter($params, $glue);
  269. $sql = 'DELETE FROM ' . $this->tablename($table);
  270. $sql .= $condition['fields'] ? ' WHERE ' . $condition['fields'] : '';
  271. return $this->query($sql, $condition['params']);
  272. }
  273. public function exists($tablename, $params = array()) {
  274. $row = $this->get($tablename, $params);
  275. if (empty($row) || !is_array($row) || 0 == count($row)) {
  276. return false;
  277. } else {
  278. return true;
  279. }
  280. }
  281. public function count($tablename, $params = array(), $cachetime = 30) {
  282. $total = pdo_getcolumn($tablename, $params, 'count(*)');
  283. return intval($total);
  284. }
  285. public function begin() {
  286. $this->pdo->beginTransaction();
  287. }
  288. public function commit() {
  289. $this->pdo->commit();
  290. }
  291. public function rollback() {
  292. $this->pdo->rollBack();
  293. }
  294. public function run($sql, $stuff = 'ims_') {
  295. if (!isset($sql) || empty($sql)) {
  296. return;
  297. }
  298. $sql = str_replace("\r", "\n", str_replace(' ' . $stuff, ' ' . $this->tablepre, $sql));
  299. $sql = str_replace("\r", "\n", str_replace(' `' . $stuff, ' `' . $this->tablepre, $sql));
  300. $ret = array();
  301. $num = 0;
  302. $sql = preg_replace("/\;[ \f\t\v]+/", ';', $sql);
  303. foreach (explode(";\n", trim($sql)) as $query) {
  304. $ret[$num] = '';
  305. $queries = explode("\n", trim($query));
  306. foreach ($queries as $query) {
  307. $ret[$num] .= (isset($query[0]) && '#' == $query[0]) || (isset($query[1]) && isset($query[1]) && $query[0] . $query[1] == '--') ? '' : $query;
  308. }
  309. ++$num;
  310. }
  311. unset($sql);
  312. foreach ($ret as $query) {
  313. $query = trim($query);
  314. if ($query) {
  315. $this->query($query, array());
  316. }
  317. }
  318. return true;
  319. }
  320. public function fieldexists($tablename, $fieldname) {
  321. if (!$this->tableexists($tablename)) {
  322. return false;
  323. }
  324. $fields = $this->fetchall("SHOW COLUMNS FROM " . $this->tablename($tablename));
  325. if (empty($fields)) {
  326. return false;
  327. }
  328. foreach ($fields as $field) {
  329. if ($fieldname === $field['Field']) {
  330. return true;
  331. }
  332. }
  333. return false;
  334. }
  335. public function fieldmatch($tablename, $fieldname, $datatype = '', $length = '') {
  336. $datatype = strtolower($datatype);
  337. $field_info = $this->fetch('DESCRIBE ' . $this->tablename($tablename) . " `{$fieldname}`", array());
  338. if (empty($field_info)) {
  339. return false;
  340. }
  341. if (!empty($datatype)) {
  342. $find = strexists($field_info['Type'], '(');
  343. if (empty($find)) {
  344. $length = '';
  345. }
  346. if (!empty($length)) {
  347. $datatype .= ("({$length})");
  348. }
  349. return 0 === strpos($field_info['Type'], $datatype) ? true : -1;
  350. }
  351. return true;
  352. }
  353. public function indexexists($tablename, $indexname) {
  354. if (!$this->tableexists($tablename)) {
  355. return false;
  356. }
  357. if (!empty($indexname)) {
  358. $indexs = $this->fetchall('SHOW INDEX FROM ' . $this->tablename($tablename), array(), '');
  359. if (!empty($indexs) && is_array($indexs)) {
  360. foreach ($indexs as $row) {
  361. if ($row['Key_name'] == $indexname) {
  362. return true;
  363. }
  364. }
  365. }
  366. }
  367. return false;
  368. }
  369. public function tablename($table) {
  370. return (0 === strpos($table, $this->tablepre) || 0 === strpos($table, 'ims_')) ? $table : "`{$this->tablepre}{$table}`";
  371. }
  372. public function debug($output = true, $append = array()) {
  373. if (!empty($append)) {
  374. $output = false;
  375. array_push($this->errors, $append);
  376. }
  377. if ($output) {
  378. print_r($this->errors);
  379. } else {
  380. if (!empty($append['error'][1])) {
  381. $traces = debug_backtrace();
  382. $ts = '';
  383. foreach ($traces as $trace) {
  384. $trace['file'] = str_replace('\\', '/', $trace['file']);
  385. $trace['file'] = str_replace(IA_ROOT, '', $trace['file']);
  386. $ts .= "file: {$trace['file']}; line: {$trace['line']}; <br />";
  387. }
  388. $params = var_export($append['params'], true);
  389. trigger_error("SQL: <br/>{$append['sql']}<hr/>Params: <br/>{$params}<hr/>SQL Error: <br/>{$append['error'][2]}<hr/>Traces: <br/>{$ts}", E_USER_WARNING);
  390. }
  391. }
  392. return $this->errors;
  393. }
  394. private function logging($sql, $params = array(), $message = '') {
  395. if (PDO_DEBUG) {
  396. $info = array();
  397. $info['sql'] = $sql;
  398. $info['params'] = $params;
  399. $info['error'] = empty($message) ? $this->pdo->errorInfo() : $message;
  400. $this->debug(false, $info);
  401. }
  402. return true;
  403. }
  404. public function tableexists($table) {
  405. if (!empty($table)) {
  406. $real_table = preg_match('/[a-zA-Z0-9_]{' . strlen($table) . '}/', $table);
  407. if (1 !== $real_table) {
  408. return false;
  409. }
  410. $tablename = (0 === strpos($table, $this->tablepre)) ? ($table) : ($this->tablepre . $table);
  411. $data = $this->fetch("SHOW TABLES LIKE '{$tablename}'", array());
  412. if (!empty($data)) {
  413. $data = array_values($data);
  414. if (in_array($tablename, $data)) {
  415. return true;
  416. } else {
  417. return false;
  418. }
  419. } else {
  420. return false;
  421. }
  422. } else {
  423. return false;
  424. }
  425. }
  426. private function performance($sql, $runtime = 0) {
  427. global $_W;
  428. if (0 == $runtime) {
  429. return false;
  430. }
  431. if (strexists($sql, 'core_performance')) {
  432. return false;
  433. }
  434. if (empty($_W['config']['setting']['maxtimesql'])) {
  435. $_W['config']['setting']['maxtimesql'] = 5;
  436. }
  437. if ($runtime > $_W['config']['setting']['maxtimesql'] && isset($_W['setting']['copyright']['log_status']) && $_W['setting']['copyright']['log_status'] == STATUS_ON) {
  438. $sqldata = array(
  439. 'type' => '2',
  440. 'runtime' => $runtime,
  441. 'runurl' => 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'],
  442. 'runsql' => $sql,
  443. 'createtime' => time(),
  444. );
  445. $this->insert('core_performance', $sqldata);
  446. }
  447. return true;
  448. }
  449. }
  450. class SqlPaser {
  451. private static $checkcmd = array('SELECT', 'UPDATE', 'INSERT', 'REPLAC', 'DELETE');
  452. private static $disable = array(
  453. 'function' => array('load_file', 'floor', 'hex', 'substring', 'if', 'ord', 'char', 'benchmark', 'reverse', 'strcmp', 'datadir', 'updatexml', 'extractvalue', 'name_const', 'multipoint', 'database', 'user'),
  454. 'action' => array('@', 'intooutfile', 'intodumpfile', 'unionselect', 'uniondistinct', 'information_schema', 'current_user', 'current_date'),
  455. 'note' => array('/*', '*/', '#', '--'),
  456. );
  457. public static function checkquery($sql) {
  458. $cmd = strtoupper(substr(trim($sql), 0, 6));
  459. if (in_array($cmd, self::$checkcmd)) {
  460. $mark = $clean = '';
  461. $sql = str_replace(array('\\\\', '\\\'', '\\"', '\'\''), '', $sql);
  462. if (false === strpos($sql, '/') && false === strpos($sql, '#') && false === strpos($sql, '-- ') && false === strpos($sql, '@') && false === strpos($sql, '`')) {
  463. $cleansql = preg_replace("/'(.+?)'/s", '', $sql);
  464. } else {
  465. $cleansql = self::stripSafeChar($sql);
  466. }
  467. $clean_function_sql = preg_replace("/\s+/", '', strtolower($cleansql));
  468. if (is_array(self::$disable['function'])) {
  469. foreach (self::$disable['function'] as $fun) {
  470. if (false !== strpos($clean_function_sql, $fun . '(')) {
  471. return error(1, 'SQL中包含禁用函数 - ' . $fun);
  472. }
  473. }
  474. }
  475. $cleansql = preg_replace("/[^a-z0-9_\-\(\)#\*\/\"]+/is", '', strtolower($cleansql));
  476. if (is_array(self::$disable['action'])) {
  477. foreach (self::$disable['action'] as $action) {
  478. if (false !== strpos($cleansql, $action)) {
  479. return error(2, 'SQL中包含禁用操作符 - ' . $action);
  480. }
  481. }
  482. }
  483. if (is_array(self::$disable['note'])) {
  484. foreach (self::$disable['note'] as $note) {
  485. if (false !== strpos($cleansql, $note)) {
  486. return error(3, 'SQL中包含注释信息');
  487. }
  488. }
  489. }
  490. } elseif ('/*' === substr($cmd, 0, 2)) {
  491. return error(3, 'SQL中包含注释信息');
  492. }
  493. }
  494. private static function stripSafeChar($sql) {
  495. $len = strlen($sql);
  496. $mark = $clean = '';
  497. for ($i = 0; $i < $len; ++$i) {
  498. $str = $sql[$i];
  499. switch ($str) {
  500. case '\'':
  501. if (!$mark) {
  502. $mark = '\'';
  503. $clean .= $str;
  504. } elseif ('\'' == $mark) {
  505. $mark = '';
  506. }
  507. break;
  508. case '/':
  509. if (empty($mark) && '*' == $sql[$i + 1]) {
  510. $mark = '/*';
  511. $clean .= $mark;
  512. ++$i;
  513. } elseif ('/*' == $mark && '*' == $sql[$i - 1]) {
  514. $mark = '';
  515. $clean .= '*';
  516. }
  517. break;
  518. case '#':
  519. if (empty($mark)) {
  520. $mark = $str;
  521. $clean .= $str;
  522. }
  523. break;
  524. case "\n":
  525. if ('#' == $mark || '--' == $mark) {
  526. $mark = '';
  527. }
  528. break;
  529. case '-':
  530. if (empty($mark) && '-- ' == substr($sql, $i, 3)) {
  531. $mark = '-- ';
  532. $clean .= $mark;
  533. }
  534. break;
  535. default:
  536. break;
  537. }
  538. $clean .= $mark ? '' : $str;
  539. }
  540. return $clean;
  541. }
  542. public static function parseParameter($params, $glue = ',', $alias = '') {
  543. $result = array('fields' => ' 1 ', 'params' => array());
  544. $split = '';
  545. $suffix = '';
  546. $allow_operator = array('>', '<', '<>', '!=', '>=', '<=', '+=', '-=', 'LIKE', 'like');
  547. if (in_array(strtolower($glue), array('and', 'or'))) {
  548. $suffix = '__';
  549. }
  550. if (!is_array($params)) {
  551. $result['fields'] = $params;
  552. return $result;
  553. }
  554. if (is_array($params)) {
  555. $result['fields'] = '';
  556. foreach ($params as $fields => $value) {
  557. if (',' == $glue) {
  558. $value = null === $value ? '' : $value;
  559. }
  560. $operator = '';
  561. if (false !== strpos($fields, ' ')) {
  562. list($fields, $operator) = explode(' ', $fields, 2);
  563. if (!in_array($operator, $allow_operator)) {
  564. $operator = '';
  565. }
  566. }
  567. if (empty($operator)) {
  568. $fields = trim($fields);
  569. if (is_array($value) && !empty($value)) {
  570. $operator = 'IN';
  571. } elseif ('NULL' === $value) {
  572. $operator = 'IS';
  573. } else {
  574. $operator = '=';
  575. }
  576. } elseif ('+=' == $operator) {
  577. $operator = " = `$fields` + ";
  578. } elseif ('-=' == $operator) {
  579. $operator = " = `$fields` - ";
  580. } elseif ('!=' == $operator || '<>' == $operator) {
  581. if (is_array($value) && !empty($value)) {
  582. $operator = 'NOT IN';
  583. } elseif ('NULL' === $value) {
  584. $operator = 'IS NOT';
  585. }
  586. }
  587. $select_fields = self::parseFieldAlias($fields, $alias);
  588. if (is_array($value) && !empty($value)) {
  589. $insql = array();
  590. $value = array_values($value);
  591. foreach ($value as $v) {
  592. $placeholder = self::parsePlaceholder($fields, $suffix);
  593. $insql[] = $placeholder;
  594. $result['params'][$placeholder] = is_null($v) ? '' : $v;
  595. }
  596. $result['fields'] .= $split . "$select_fields {$operator} (" . implode(',', $insql) . ')';
  597. $split = ' ' . $glue . ' ';
  598. } else {
  599. $placeholder = self::parsePlaceholder($fields, $suffix);
  600. $result['fields'] .= $split . "$select_fields {$operator} " . ('NULL' === $value ? 'NULL' : $placeholder);
  601. $split = ' ' . $glue . ' ';
  602. if ('NULL' !== $value) {
  603. $result['params'][$placeholder] = is_array($value) ? '' : $value;
  604. }
  605. }
  606. }
  607. }
  608. return $result;
  609. }
  610. private static function parsePlaceholder($field, $suffix = '') {
  611. static $params_index = 0;
  612. ++$params_index;
  613. $illegal_str = array('(', ')', ',', '.', '*');
  614. $placeholder = ":{$suffix}" . str_replace($illegal_str, '_', $field) . "_{$params_index}";
  615. return $placeholder;
  616. }
  617. private static function parseFieldAlias($field, $alias = '') {
  618. if (strexists($field, '.') || strexists($field, '*')) {
  619. return $field;
  620. }
  621. if (strexists($field, '(')) {
  622. $select_fields = str_replace(array('(', ')'), array('(' . (!empty($alias) ? "`{$alias}`." : '') . '`', '`)'), $field);
  623. } else {
  624. $select_fields = (!empty($alias) ? "`{$alias}`." : '') . "`$field`";
  625. }
  626. return $select_fields;
  627. }
  628. public static function parseSelect($field = array(), $alias = '') {
  629. if (empty($field) || '*' == $field) {
  630. return ' SELECT *';
  631. }
  632. if (!is_array($field)) {
  633. $field = array($field);
  634. }
  635. $select = array();
  636. $index = 0;
  637. foreach ($field as $field_row) {
  638. if (strexists($field_row, '*')) {
  639. if (!strexists(strtolower($field_row), 'as')) {
  640. }
  641. } elseif (strexists(strtolower($field_row), 'select')) {
  642. if ('(' != $field_row[0]) {
  643. $field_row = "($field_row) AS '{$index}'";
  644. }
  645. } elseif (strexists($field_row, '(')) {
  646. $field_row = str_replace(array('(', ')'), array('(' . (!empty($alias) ? "`{$alias}`." : '') . '`', '`)'), $field_row);
  647. if (!strexists(strtolower($field_row), 'as')) {
  648. $field_row .= " AS '{$index}'";
  649. }
  650. } else {
  651. $field_row = self::parseFieldAlias($field_row, $alias);
  652. }
  653. $select[] = $field_row;
  654. ++$index;
  655. }
  656. return ' SELECT ' . implode(',', $select);
  657. }
  658. public static function parseLimit($limit, $inpage = true) {
  659. $limitsql = '';
  660. if (empty($limit)) {
  661. return $limitsql;
  662. }
  663. if (is_array($limit)) {
  664. if (empty($limit[0]) && !empty($limit[1])) {
  665. $limitsql = ' LIMIT 0, ' . $limit[1];
  666. } else {
  667. $limit[0] = max(intval($limit[0]), 1);
  668. !empty($limit[1]) && $limit[1] = max(intval($limit[1]), 1);
  669. if (empty($limit[0]) && empty($limit[1])) {
  670. $limitsql = '';
  671. } elseif (!empty($limit[0]) && empty($limit[1])) {
  672. $limitsql = ' LIMIT ' . $limit[0];
  673. } else {
  674. $limitsql = ' LIMIT ' . ($inpage ? ($limit[0] - 1) * $limit[1] : $limit[0]) . ', ' . $limit[1];
  675. }
  676. }
  677. } else {
  678. $limit = trim($limit);
  679. if (preg_match('/^(?:limit)?[\s,0-9]+$/i', $limit)) {
  680. $limitsql = strexists(strtoupper($limit), 'LIMIT') ? " $limit " : " LIMIT $limit";
  681. }
  682. }
  683. return $limitsql;
  684. }
  685. public static function parseOrderby($orderby, $alias = '') {
  686. $orderbysql = '';
  687. if (empty($orderby)) {
  688. return $orderbysql;
  689. }
  690. if (!is_array($orderby)) {
  691. $orderby = explode(',', $orderby);
  692. }
  693. foreach ($orderby as $i => &$row) {
  694. if (strtoupper($row) == 'RAND()') {
  695. $row = strtoupper($row);
  696. } else {
  697. $row = strtolower($row);
  698. $orderbydata = explode(' ', $row);
  699. $field = empty($orderbydata[0]) ? '' : $orderbydata[0];
  700. $orderbyrule = empty($orderbydata[1]) ? '' : $orderbydata[1];
  701. if ('asc' != $orderbyrule && 'desc' != $orderbyrule) {
  702. unset($orderby[$i]);
  703. }
  704. $field = self::parseFieldAlias($field, $alias);
  705. $row = "{$field} {$orderbyrule}";
  706. }
  707. }
  708. $orderbysql = implode(',', $orderby);
  709. return !empty($orderbysql) ? " ORDER BY $orderbysql " : '';
  710. }
  711. public static function parseGroupby($statement, $alias = '') {
  712. if (empty($statement)) {
  713. return $statement;
  714. }
  715. if (!is_array($statement)) {
  716. $statement = explode(',', $statement);
  717. }
  718. foreach ($statement as $i => &$row) {
  719. $row = self::parseFieldAlias($row, $alias);
  720. if (strexists($row, ' ')) {
  721. unset($statement[$i]);
  722. }
  723. }
  724. $statementsql = implode(', ', $statement);
  725. return !empty($statementsql) ? " GROUP BY $statementsql " : '';
  726. }
  727. }