PageRenderTime 51ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/ThinkPHP/Lib/Core/Db.class.php

https://github.com/huangshan/yuqing
PHP | 919 lines | 572 code | 42 blank | 305 comment | 81 complexity | 0cb2ced5c5a8b0cdebfb831a04f5fe2f MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: liu21st <liu21st@gmail.com>
  10. // +----------------------------------------------------------------------
  11. /**
  12. * ThinkPHP 数据库中间层实现类
  13. * @category Think
  14. * @package Think
  15. * @subpackage Core
  16. * @author liu21st <liu21st@gmail.com>
  17. */
  18. class Db {
  19. // 数据库类型
  20. protected $dbType = null;
  21. // 是否自动释放查询结果
  22. protected $autoFree = false;
  23. // 当前操作所属的模型名
  24. protected $model = '_think_';
  25. // 是否使用永久连接
  26. protected $pconnect = false;
  27. // 当前SQL指令
  28. protected $queryStr = '';
  29. protected $modelSql = array();
  30. // 最后插入ID
  31. protected $lastInsID = null;
  32. // 返回或者影响记录数
  33. protected $numRows = 0;
  34. // 返回字段数
  35. protected $numCols = 0;
  36. // 事务指令数
  37. protected $transTimes = 0;
  38. // 错误信息
  39. protected $error = '';
  40. // 数据库连接ID 支持多个连接
  41. protected $linkID = array();
  42. // 当前连接ID
  43. protected $_linkID = null;
  44. // 当前查询ID
  45. protected $queryID = null;
  46. // 是否已经连接数据库
  47. protected $connected = false;
  48. // 数据库连接参数配置
  49. protected $config = '';
  50. // 数据库表达式
  51. protected $comparison = array('eq'=>'=','neq'=>'<>','gt'=>'>','egt'=>'>=','lt'=>'<','elt'=>'<=','notlike'=>'NOT LIKE','like'=>'LIKE','in'=>'IN','notin'=>'NOT IN');
  52. // 查询表达式
  53. protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%COMMENT%';
  54. // 参数绑定
  55. protected $bind = array();
  56. /**
  57. * 取得数据库类实例
  58. * @static
  59. * @access public
  60. * @return mixed 返回数据库驱动类
  61. */
  62. public static function getInstance() {
  63. $args = func_get_args();
  64. return get_instance_of(__CLASS__,'factory',$args);
  65. }
  66. /**
  67. * 加载数据库 支持配置文件或者 DSN
  68. * @access public
  69. * @param mixed $db_config 数据库配置信息
  70. * @return string
  71. */
  72. public function factory($db_config='') {
  73. // 读取数据库配置
  74. $db_config = $this->parseConfig($db_config);
  75. if(empty($db_config['dbms']))
  76. throw_exception(L('_NO_DB_CONFIG_'));
  77. // 数据库类型
  78. $this->dbType = ucwords(strtolower($db_config['dbms']));
  79. $class = 'Db'. $this->dbType;
  80. // 检查驱动类
  81. if(class_exists($class)) {
  82. $db = new $class($db_config);
  83. // 获取当前的数据库类型
  84. if( 'pdo' != strtolower($db_config['dbms']) )
  85. $db->dbType = strtoupper($this->dbType);
  86. else
  87. $db->dbType = $this->_getDsnType($db_config['dsn']);
  88. }else {
  89. // 类没有定义
  90. throw_exception(L('_NO_DB_DRIVER_').': ' . $class);
  91. }
  92. return $db;
  93. }
  94. /**
  95. * 根据DSN获取数据库类型 返回大写
  96. * @access protected
  97. * @param string $dsn dsn字符串
  98. * @return string
  99. */
  100. protected function _getDsnType($dsn) {
  101. $match = explode(':',$dsn);
  102. $dbType = strtoupper(trim($match[0]));
  103. return $dbType;
  104. }
  105. /**
  106. * 分析数据库配置信息,支持数组和DSN
  107. * @access private
  108. * @param mixed $db_config 数据库配置信息
  109. * @return string
  110. */
  111. private function parseConfig($db_config='') {
  112. if ( !empty($db_config) && is_string($db_config)) {
  113. // 如果DSN字符串则进行解析
  114. $db_config = $this->parseDSN($db_config);
  115. }elseif(is_array($db_config)) { // 数组配置
  116. $db_config = array_change_key_case($db_config);
  117. $db_config = array(
  118. 'dbms' => $db_config['db_type'],
  119. 'username' => $db_config['db_user'],
  120. 'password' => $db_config['db_pwd'],
  121. 'hostname' => $db_config['db_host'],
  122. 'hostport' => $db_config['db_port'],
  123. 'database' => $db_config['db_name'],
  124. 'dsn' => $db_config['db_dsn'],
  125. 'params' => $db_config['db_params'],
  126. );
  127. }elseif(empty($db_config)) {
  128. // 如果配置为空,读取配置文件设置
  129. if( C('DB_DSN') && 'pdo' != strtolower(C('DB_TYPE')) ) { // 如果设置了DB_DSN 则优先
  130. $db_config = $this->parseDSN(C('DB_DSN'));
  131. }else{
  132. $db_config = array (
  133. 'dbms' => C('DB_TYPE'),
  134. 'username' => C('DB_USER'),
  135. 'password' => C('DB_PWD'),
  136. 'hostname' => C('DB_HOST'),
  137. 'hostport' => C('DB_PORT'),
  138. 'database' => C('DB_NAME'),
  139. 'dsn' => C('DB_DSN'),
  140. 'params' => C('DB_PARAMS'),
  141. );
  142. }
  143. }
  144. return $db_config;
  145. }
  146. /**
  147. * 初始化数据库连接
  148. * @access protected
  149. * @param boolean $master 主服务器
  150. * @return void
  151. */
  152. protected function initConnect($master=true) {
  153. if(1 == C('DB_DEPLOY_TYPE'))
  154. // 采用分布式数据库
  155. $this->_linkID = $this->multiConnect($master);
  156. else
  157. // 默认单数据库
  158. if ( !$this->connected ) $this->_linkID = $this->connect();
  159. }
  160. /**
  161. * 连接分布式服务器
  162. * @access protected
  163. * @param boolean $master 主服务器
  164. * @return void
  165. */
  166. protected function multiConnect($master=false) {
  167. static $_config = array();
  168. if(empty($_config)) {
  169. // 缓存分布式数据库配置解析
  170. foreach ($this->config as $key=>$val){
  171. $_config[$key] = explode(',',$val);
  172. }
  173. }
  174. // 数据库读写是否分离
  175. if(C('DB_RW_SEPARATE')){
  176. // 主从式采用读写分离
  177. if($master)
  178. // 主服务器写入
  179. $r = floor(mt_rand(0,C('DB_MASTER_NUM')-1));
  180. else{
  181. if(is_numeric(C('DB_SLAVE_NO'))) {// 指定服务器读
  182. $r = C('DB_SLAVE_NO');
  183. }else{
  184. // 读操作连接从服务器
  185. $r = floor(mt_rand(C('DB_MASTER_NUM'),count($_config['hostname'])-1)); // 每次随机连接的数据库
  186. }
  187. }
  188. }else{
  189. // 读写操作不区分服务器
  190. $r = floor(mt_rand(0,count($_config['hostname'])-1)); // 每次随机连接的数据库
  191. }
  192. $db_config = array(
  193. 'username' => isset($_config['username'][$r])?$_config['username'][$r]:$_config['username'][0],
  194. 'password' => isset($_config['password'][$r])?$_config['password'][$r]:$_config['password'][0],
  195. 'hostname' => isset($_config['hostname'][$r])?$_config['hostname'][$r]:$_config['hostname'][0],
  196. 'hostport' => isset($_config['hostport'][$r])?$_config['hostport'][$r]:$_config['hostport'][0],
  197. 'database' => isset($_config['database'][$r])?$_config['database'][$r]:$_config['database'][0],
  198. 'dsn' => isset($_config['dsn'][$r])?$_config['dsn'][$r]:$_config['dsn'][0],
  199. 'params' => isset($_config['params'][$r])?$_config['params'][$r]:$_config['params'][0],
  200. );
  201. return $this->connect($db_config,$r);
  202. }
  203. /**
  204. * DSN解析
  205. * 格式: mysql://username:passwd@localhost:3306/DbName
  206. * @static
  207. * @access public
  208. * @param string $dsnStr
  209. * @return array
  210. */
  211. public function parseDSN($dsnStr) {
  212. if( empty($dsnStr) ){return false;}
  213. $info = parse_url($dsnStr);
  214. if($info['scheme']){
  215. $dsn = array(
  216. 'dbms' => $info['scheme'],
  217. 'username' => isset($info['user']) ? $info['user'] : '',
  218. 'password' => isset($info['pass']) ? $info['pass'] : '',
  219. 'hostname' => isset($info['host']) ? $info['host'] : '',
  220. 'hostport' => isset($info['port']) ? $info['port'] : '',
  221. 'database' => isset($info['path']) ? substr($info['path'],1) : ''
  222. );
  223. }else {
  224. preg_match('/^(.*?)\:\/\/(.*?)\:(.*?)\@(.*?)\:([0-9]{1, 6})\/(.*?)$/',trim($dsnStr),$matches);
  225. $dsn = array (
  226. 'dbms' => $matches[1],
  227. 'username' => $matches[2],
  228. 'password' => $matches[3],
  229. 'hostname' => $matches[4],
  230. 'hostport' => $matches[5],
  231. 'database' => $matches[6]
  232. );
  233. }
  234. $dsn['dsn'] = ''; // 兼容配置信息数组
  235. return $dsn;
  236. }
  237. /**
  238. * 数据库调试 记录当前SQL
  239. * @access protected
  240. */
  241. protected function debug() {
  242. $this->modelSql[$this->model] = $this->queryStr;
  243. $this->model = '_think_';
  244. // 记录操作结束时间
  245. if (C('DB_SQL_LOG')) {
  246. G('queryEndTime');
  247. trace($this->queryStr.' [ RunTime:'.G('queryStartTime','queryEndTime',6).'s ]','','SQL');
  248. }
  249. }
  250. /**
  251. * 设置锁机制
  252. * @access protected
  253. * @return string
  254. */
  255. protected function parseLock($lock=false) {
  256. if(!$lock) return '';
  257. if('ORACLE' == $this->dbType) {
  258. return ' FOR UPDATE NOWAIT ';
  259. }
  260. return ' FOR UPDATE ';
  261. }
  262. /**
  263. * set分析
  264. * @access protected
  265. * @param array $data
  266. * @return string
  267. */
  268. protected function parseSet($data) {
  269. foreach ($data as $key=>$val){
  270. if(is_array($val) && 'exp' == $val[0]){
  271. $set[] = $this->parseKey($key).'='.$this->escapeString($val[1]);
  272. }elseif(is_scalar($val)) { // 过滤非标量数据
  273. if(C('DB_BIND_PARAM') && 0 !== strpos($val,':')){
  274. $name = md5($key);
  275. $set[] = $this->parseKey($key).'=:'.$name;
  276. $this->bindParam($name,$val);
  277. }else{
  278. $set[] = $this->parseKey($key).'='.$this->parseValue($val);
  279. }
  280. }
  281. }
  282. return ' SET '.implode(',',$set);
  283. }
  284. /**
  285. * 参数绑定
  286. * @access protected
  287. * @param string $name 绑定参数名
  288. * @param mixed $value 绑定值
  289. * @return void
  290. */
  291. protected function bindParam($name,$value){
  292. $this->bind[':'.$name] = $value;
  293. }
  294. /**
  295. * 参数绑定分析
  296. * @access protected
  297. * @param array $bind
  298. * @return array
  299. */
  300. protected function parseBind($bind){
  301. $bind = array_merge($this->bind,$bind);
  302. $this->bind = array();
  303. return $bind;
  304. }
  305. /**
  306. * 字段名分析
  307. * @access protected
  308. * @param string $key
  309. * @return string
  310. */
  311. protected function parseKey(&$key) {
  312. return $key;
  313. }
  314. /**
  315. * value分析
  316. * @access protected
  317. * @param mixed $value
  318. * @return string
  319. */
  320. protected function parseValue($value) {
  321. if(is_string($value)) {
  322. $value = '\''.$this->escapeString($value).'\'';
  323. }elseif(isset($value[0]) && is_string($value[0]) && strtolower($value[0]) == 'exp'){
  324. $value = $this->escapeString($value[1]);
  325. }elseif(is_array($value)) {
  326. $value = array_map(array($this, 'parseValue'),$value);
  327. }elseif(is_bool($value)){
  328. $value = $value ? '1' : '0';
  329. }elseif(is_null($value)){
  330. $value = 'null';
  331. }
  332. return $value;
  333. }
  334. /**
  335. * field分析
  336. * @access protected
  337. * @param mixed $fields
  338. * @return string
  339. */
  340. protected function parseField($fields) {
  341. if(is_string($fields) && strpos($fields,',')) {
  342. $fields = explode(',',$fields);
  343. }
  344. if(is_array($fields)) {
  345. // 完善数组方式传字段名的支持
  346. // 支持 'field1'=>'field2' 这样的字段别名定义
  347. $array = array();
  348. foreach ($fields as $key=>$field){
  349. if(!is_numeric($key))
  350. $array[] = $this->parseKey($key).' AS '.$this->parseKey($field);
  351. else
  352. $array[] = $this->parseKey($field);
  353. }
  354. $fieldsStr = implode(',', $array);
  355. }elseif(is_string($fields) && !empty($fields)) {
  356. $fieldsStr = $this->parseKey($fields);
  357. }else{
  358. $fieldsStr = '*';
  359. }
  360. //TODO 如果是查询全部字段,并且是join的方式,那么就把要查的表加个别名,以免字段被覆盖
  361. return $fieldsStr;
  362. }
  363. /**
  364. * table分析
  365. * @access protected
  366. * @param mixed $table
  367. * @return string
  368. */
  369. protected function parseTable($tables) {
  370. if(is_array($tables)) {// 支持别名定义
  371. $array = array();
  372. foreach ($tables as $table=>$alias){
  373. if(!is_numeric($table))
  374. $array[] = $this->parseKey($table).' '.$this->parseKey($alias);
  375. else
  376. $array[] = $this->parseKey($table);
  377. }
  378. $tables = $array;
  379. }elseif(is_string($tables)){
  380. $tables = explode(',',$tables);
  381. array_walk($tables, array(&$this, 'parseKey'));
  382. }
  383. return implode(',',$tables);
  384. }
  385. /**
  386. * where分析
  387. * @access protected
  388. * @param mixed $where
  389. * @return string
  390. */
  391. protected function parseWhere($where) {
  392. $whereStr = '';
  393. if(is_string($where)) {
  394. // 直接使用字符串条件
  395. $whereStr = $where;
  396. }else{ // 使用数组表达式
  397. $operate = isset($where['_logic'])?strtoupper($where['_logic']):'';
  398. if(in_array($operate,array('AND','OR','XOR'))){
  399. // 定义逻辑运算规则 例如 OR XOR AND NOT
  400. $operate = ' '.$operate.' ';
  401. unset($where['_logic']);
  402. }else{
  403. // 默认进行 AND 运算
  404. $operate = ' AND ';
  405. }
  406. foreach ($where as $key=>$val){
  407. $whereStr .= '( ';
  408. if(0===strpos($key,'_')) {
  409. // 解析特殊条件表达式
  410. $whereStr .= $this->parseThinkWhere($key,$val);
  411. }else{
  412. // 查询字段的安全过滤
  413. if(!preg_match('/^[A-Z_\|\&\-.a-z0-9\(\)\,]+$/',trim($key))){
  414. throw_exception(L('_EXPRESS_ERROR_').':'.$key);
  415. }
  416. // 多条件支持
  417. $multi = is_array($val) && isset($val['_multi']);
  418. $key = trim($key);
  419. if(strpos($key,'|')) { // 支持 name|title|nickname 方式定义查询字段
  420. $array = explode('|',$key);
  421. $str = array();
  422. foreach ($array as $m=>$k){
  423. $v = $multi?$val[$m]:$val;
  424. $str[] = '('.$this->parseWhereItem($this->parseKey($k),$v).')';
  425. }
  426. $whereStr .= implode(' OR ',$str);
  427. }elseif(strpos($key,'&')){
  428. $array = explode('&',$key);
  429. $str = array();
  430. foreach ($array as $m=>$k){
  431. $v = $multi?$val[$m]:$val;
  432. $str[] = '('.$this->parseWhereItem($this->parseKey($k),$v).')';
  433. }
  434. $whereStr .= implode(' AND ',$str);
  435. }else{
  436. $whereStr .= $this->parseWhereItem($this->parseKey($key),$val);
  437. }
  438. }
  439. $whereStr .= ' )'.$operate;
  440. }
  441. $whereStr = substr($whereStr,0,-strlen($operate));
  442. }
  443. return empty($whereStr)?'':' WHERE '.$whereStr;
  444. }
  445. // where子单元分析
  446. protected function parseWhereItem($key,$val) {
  447. $whereStr = '';
  448. if(is_array($val)) {
  449. if(is_string($val[0])) {
  450. if(preg_match('/^(EQ|NEQ|GT|EGT|LT|ELT)$/i',$val[0])) { // 比较运算
  451. $whereStr .= $key.' '.$this->comparison[strtolower($val[0])].' '.$this->parseValue($val[1]);
  452. }elseif(preg_match('/^(NOTLIKE|LIKE)$/i',$val[0])){// 模糊查找
  453. if(is_array($val[1])) {
  454. $likeLogic = isset($val[2])?strtoupper($val[2]):'OR';
  455. if(in_array($likeLogic,array('AND','OR','XOR'))){
  456. $likeStr = $this->comparison[strtolower($val[0])];
  457. $like = array();
  458. foreach ($val[1] as $item){
  459. $like[] = $key.' '.$likeStr.' '.$this->parseValue($item);
  460. }
  461. $whereStr .= '('.implode(' '.$likeLogic.' ',$like).')';
  462. }
  463. }else{
  464. $whereStr .= $key.' '.$this->comparison[strtolower($val[0])].' '.$this->parseValue($val[1]);
  465. }
  466. }elseif('exp'==strtolower($val[0])){ // 使用表达式
  467. $whereStr .= ' ('.$key.' '.$val[1].') ';
  468. }elseif(preg_match('/IN/i',$val[0])){ // IN 运算
  469. if(isset($val[2]) && 'exp'==$val[2]) {
  470. $whereStr .= $key.' '.strtoupper($val[0]).' '.$val[1];
  471. }else{
  472. if(is_string($val[1])) {
  473. $val[1] = explode(',',$val[1]);
  474. }
  475. $zone = implode(',',$this->parseValue($val[1]));
  476. $whereStr .= $key.' '.strtoupper($val[0]).' ('.$zone.')';
  477. }
  478. }elseif(preg_match('/BETWEEN/i',$val[0])){ // BETWEEN运算
  479. $data = is_string($val[1])? explode(',',$val[1]):$val[1];
  480. $whereStr .= ' ('.$key.' '.strtoupper($val[0]).' '.$this->parseValue($data[0]).' AND '.$this->parseValue($data[1]).' )';
  481. }else{
  482. throw_exception(L('_EXPRESS_ERROR_').':'.$val[0]);
  483. }
  484. }else {
  485. $count = count($val);
  486. $rule = isset($val[$count-1])?strtoupper($val[$count-1]):'';
  487. if(in_array($rule,array('AND','OR','XOR'))) {
  488. $count = $count -1;
  489. }else{
  490. $rule = 'AND';
  491. }
  492. for($i=0;$i<$count;$i++) {
  493. $data = is_array($val[$i])?$val[$i][1]:$val[$i];
  494. if('exp'==strtolower($val[$i][0])) {
  495. $whereStr .= '('.$key.' '.$data.') '.$rule.' ';
  496. }else{
  497. $op = is_array($val[$i])?$this->comparison[strtolower($val[$i][0])]:'=';
  498. $whereStr .= '('.$key.' '.$op.' '.$this->parseValue($data).') '.$rule.' ';
  499. }
  500. }
  501. $whereStr = substr($whereStr,0,-4);
  502. }
  503. }else {
  504. //对字符串类型字段采用模糊匹配
  505. if(C('DB_LIKE_FIELDS') && preg_match('/('.C('DB_LIKE_FIELDS').')/i',$key)) {
  506. $val = '%'.$val.'%';
  507. $whereStr .= $key.' LIKE '.$this->parseValue($val);
  508. }else {
  509. $whereStr .= $key.' = '.$this->parseValue($val);
  510. }
  511. }
  512. return $whereStr;
  513. }
  514. /**
  515. * 特殊条件分析
  516. * @access protected
  517. * @param string $key
  518. * @param mixed $val
  519. * @return string
  520. */
  521. protected function parseThinkWhere($key,$val) {
  522. $whereStr = '';
  523. switch($key) {
  524. case '_string':
  525. // 字符串模式查询条件
  526. $whereStr = $val;
  527. break;
  528. case '_complex':
  529. // 复合查询条件
  530. $whereStr = substr($this->parseWhere($val),6);
  531. break;
  532. case '_query':
  533. // 字符串模式查询条件
  534. parse_str($val,$where);
  535. if(isset($where['_logic'])) {
  536. $op = ' '.strtoupper($where['_logic']).' ';
  537. unset($where['_logic']);
  538. }else{
  539. $op = ' AND ';
  540. }
  541. $array = array();
  542. foreach ($where as $field=>$data)
  543. $array[] = $this->parseKey($field).' = '.$this->parseValue($data);
  544. $whereStr = implode($op,$array);
  545. break;
  546. }
  547. return $whereStr;
  548. }
  549. /**
  550. * limit分析
  551. * @access protected
  552. * @param mixed $lmit
  553. * @return string
  554. */
  555. protected function parseLimit($limit) {
  556. return !empty($limit)? ' LIMIT '.$limit.' ':'';
  557. }
  558. /**
  559. * join分析
  560. * @access protected
  561. * @param mixed $join
  562. * @return string
  563. */
  564. protected function parseJoin($join) {
  565. $joinStr = '';
  566. if(!empty($join)) {
  567. if(is_array($join)) {
  568. foreach ($join as $key=>$_join){
  569. if(false !== stripos($_join,'JOIN'))
  570. $joinStr .= ' '.$_join;
  571. else
  572. $joinStr .= ' LEFT JOIN ' .$_join;
  573. }
  574. }else{
  575. $joinStr .= ' LEFT JOIN ' .$join;
  576. }
  577. }
  578. //将__TABLE_NAME__这样的字符串替换成正规的表名,并且带上前缀和后缀
  579. $joinStr = preg_replace("/__([A-Z_-]+)__/esU",C("DB_PREFIX").".strtolower('$1')",$joinStr);
  580. return $joinStr;
  581. }
  582. /**
  583. * order分析
  584. * @access protected
  585. * @param mixed $order
  586. * @return string
  587. */
  588. protected function parseOrder($order) {
  589. if(is_array($order)) {
  590. $array = array();
  591. foreach ($order as $key=>$val){
  592. if(is_numeric($key)) {
  593. $array[] = $this->parseKey($val);
  594. }else{
  595. $array[] = $this->parseKey($key).' '.$val;
  596. }
  597. }
  598. $order = implode(',',$array);
  599. }
  600. return !empty($order)? ' ORDER BY '.$order:'';
  601. }
  602. /**
  603. * group分析
  604. * @access protected
  605. * @param mixed $group
  606. * @return string
  607. */
  608. protected function parseGroup($group) {
  609. return !empty($group)? ' GROUP BY '.$group:'';
  610. }
  611. /**
  612. * having分析
  613. * @access protected
  614. * @param string $having
  615. * @return string
  616. */
  617. protected function parseHaving($having) {
  618. return !empty($having)? ' HAVING '.$having:'';
  619. }
  620. /**
  621. * comment分析
  622. * @access protected
  623. * @param string $comment
  624. * @return string
  625. */
  626. protected function parseComment($comment) {
  627. return !empty($comment)? ' /* '.$comment.' */':'';
  628. }
  629. /**
  630. * distinct分析
  631. * @access protected
  632. * @param mixed $distinct
  633. * @return string
  634. */
  635. protected function parseDistinct($distinct) {
  636. return !empty($distinct)? ' DISTINCT ' :'';
  637. }
  638. /**
  639. * union分析
  640. * @access protected
  641. * @param mixed $union
  642. * @return string
  643. */
  644. protected function parseUnion($union) {
  645. if(empty($union)) return '';
  646. if(isset($union['_all'])) {
  647. $str = 'UNION ALL ';
  648. unset($union['_all']);
  649. }else{
  650. $str = 'UNION ';
  651. }
  652. foreach ($union as $u){
  653. $sql[] = $str.(is_array($u)?$this->buildSelectSql($u):$u);
  654. }
  655. return implode(' ',$sql);
  656. }
  657. /**
  658. * 插入记录
  659. * @access public
  660. * @param mixed $data 数据
  661. * @param array $options 参数表达式
  662. * @param boolean $replace 是否replace
  663. * @return false | integer
  664. */
  665. public function insert($data,$options=array(),$replace=false) {
  666. $values = $fields = array();
  667. $this->model = $options['model'];
  668. foreach ($data as $key=>$val){
  669. if(is_array($val) && 'exp' == $val[0]){
  670. $fields[] = $this->parseKey($key);
  671. $values[] = $this->escapeString($val[1]);
  672. }elseif(is_scalar($val)) { // 过滤非标量数据
  673. $fields[] = $this->parseKey($key);
  674. if(C('DB_BIND_PARAM') && 0 !== strpos($val,':')){
  675. $name = md5($key);
  676. $values[] = ':'.$name;
  677. $this->bindParam($name,$val);
  678. }else{
  679. $values[] = $this->parseValue($val);
  680. }
  681. }
  682. }
  683. $sql = ($replace?'REPLACE':'INSERT').' INTO '.$this->parseTable($options['table']).' ('.implode(',', $fields).') VALUES ('.implode(',', $values).')';
  684. $sql .= $this->parseLock(isset($options['lock'])?$options['lock']:false);
  685. $sql .= $this->parseComment(!empty($options['comment'])?$options['comment']:'');
  686. return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array()));
  687. }
  688. /**
  689. * 通过Select方式插入记录
  690. * @access public
  691. * @param string $fields 要插入的数据表字段名
  692. * @param string $table 要插入的数据表名
  693. * @param array $option 查询数据参数
  694. * @return false | integer
  695. */
  696. public function selectInsert($fields,$table,$options=array()) {
  697. $this->model = $options['model'];
  698. if(is_string($fields)) $fields = explode(',',$fields);
  699. array_walk($fields, array($this, 'parseKey'));
  700. $sql = 'INSERT INTO '.$this->parseTable($table).' ('.implode(',', $fields).') ';
  701. $sql .= $this->buildSelectSql($options);
  702. return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array()));
  703. }
  704. /**
  705. * 更新记录
  706. * @access public
  707. * @param mixed $data 数据
  708. * @param array $options 表达式
  709. * @return false | integer
  710. */
  711. public function update($data,$options) {
  712. $this->model = $options['model'];
  713. $sql = 'UPDATE '
  714. .$this->parseTable($options['table'])
  715. .$this->parseSet($data)
  716. .$this->parseWhere(!empty($options['where'])?$options['where']:'')
  717. .$this->parseOrder(!empty($options['order'])?$options['order']:'')
  718. .$this->parseLimit(!empty($options['limit'])?$options['limit']:'')
  719. .$this->parseLock(isset($options['lock'])?$options['lock']:false)
  720. .$this->parseComment(!empty($options['comment'])?$options['comment']:'');
  721. return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array()));
  722. }
  723. /**
  724. * 删除记录
  725. * @access public
  726. * @param array $options 表达式
  727. * @return false | integer
  728. */
  729. public function delete($options=array()) {
  730. $this->model = $options['model'];
  731. $sql = 'DELETE FROM '
  732. .$this->parseTable($options['table'])
  733. .$this->parseWhere(!empty($options['where'])?$options['where']:'')
  734. .$this->parseOrder(!empty($options['order'])?$options['order']:'')
  735. .$this->parseLimit(!empty($options['limit'])?$options['limit']:'')
  736. .$this->parseLock(isset($options['lock'])?$options['lock']:false)
  737. .$this->parseComment(!empty($options['comment'])?$options['comment']:'');
  738. return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array()));
  739. }
  740. /**
  741. * 查找记录
  742. * @access public
  743. * @param array $options 表达式
  744. * @return mixed
  745. */
  746. public function select($options=array()) {
  747. $this->model = $options['model'];
  748. $sql = $this->buildSelectSql($options);
  749. $cache = isset($options['cache'])?$options['cache']:false;
  750. if($cache) { // 查询缓存检测
  751. $key = is_string($cache['key'])?$cache['key']:md5($sql);
  752. $value = S($key,'',$cache);
  753. if(false !== $value) {
  754. return $value;
  755. }
  756. }
  757. $result = $this->query($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array()));
  758. if($cache && false !== $result ) { // 查询缓存写入
  759. S($key,$result,$cache);
  760. }
  761. return $result;
  762. }
  763. /**
  764. * 生成查询SQL
  765. * @access public
  766. * @param array $options 表达式
  767. * @return string
  768. */
  769. public function buildSelectSql($options=array()) {
  770. if(isset($options['page'])) {
  771. // 根据页数计算limit
  772. if(strpos($options['page'],',')) {
  773. list($page,$listRows) = explode(',',$options['page']);
  774. }else{
  775. $page = $options['page'];
  776. }
  777. $page = $page?$page:1;
  778. $listRows= isset($listRows)?$listRows:(is_numeric($options['limit'])?$options['limit']:20);
  779. $offset = $listRows*((int)$page-1);
  780. $options['limit'] = $offset.','.$listRows;
  781. }
  782. if(C('DB_SQL_BUILD_CACHE')) { // SQL创建缓存
  783. $key = md5(serialize($options));
  784. $value = S($key);
  785. if(false !== $value) {
  786. return $value;
  787. }
  788. }
  789. $sql = $this->parseSql($this->selectSql,$options);
  790. $sql .= $this->parseLock(isset($options['lock'])?$options['lock']:false);
  791. if(isset($key)) { // 写入SQL创建缓存
  792. S($key,$sql,array('expire'=>0,'length'=>C('DB_SQL_BUILD_LENGTH'),'queue'=>C('DB_SQL_BUILD_QUEUE')));
  793. }
  794. return $sql;
  795. }
  796. /**
  797. * 替换SQL语句中表达式
  798. * @access public
  799. * @param array $options 表达式
  800. * @return string
  801. */
  802. public function parseSql($sql,$options=array()){
  803. $sql = str_replace(
  804. array('%TABLE%','%DISTINCT%','%FIELD%','%JOIN%','%WHERE%','%GROUP%','%HAVING%','%ORDER%','%LIMIT%','%UNION%','%COMMENT%'),
  805. array(
  806. $this->parseTable($options['table']),
  807. $this->parseDistinct(isset($options['distinct'])?$options['distinct']:false),
  808. $this->parseField(!empty($options['field'])?$options['field']:'*'),
  809. $this->parseJoin(!empty($options['join'])?$options['join']:''),
  810. $this->parseWhere(!empty($options['where'])?$options['where']:''),
  811. $this->parseGroup(!empty($options['group'])?$options['group']:''),
  812. $this->parseHaving(!empty($options['having'])?$options['having']:''),
  813. $this->parseOrder(!empty($options['order'])?$options['order']:''),
  814. $this->parseLimit(!empty($options['limit'])?$options['limit']:''),
  815. $this->parseUnion(!empty($options['union'])?$options['union']:''),
  816. $this->parseComment(!empty($options['comment'])?$options['comment']:'')
  817. ),$sql);
  818. return $sql;
  819. }
  820. /**
  821. * 获取最近一次查询的sql语句
  822. * @param string $model 模型名
  823. * @access public
  824. * @return string
  825. */
  826. public function getLastSql($model='') {
  827. return $model?$this->modelSql[$model]:$this->queryStr;
  828. }
  829. /**
  830. * 获取最近插入的ID
  831. * @access public
  832. * @return string
  833. */
  834. public function getLastInsID() {
  835. return $this->lastInsID;
  836. }
  837. /**
  838. * 获取最近的错误信息
  839. * @access public
  840. * @return string
  841. */
  842. public function getError() {
  843. return $this->error;
  844. }
  845. /**
  846. * SQL指令安全过滤
  847. * @access public
  848. * @param string $str SQL字符串
  849. * @return string
  850. */
  851. public function escapeString($str) {
  852. return addslashes($str);
  853. }
  854. /**
  855. * 设置当前操作模型
  856. * @access public
  857. * @param string $model 模型名
  858. * @return void
  859. */
  860. public function setModel($model){
  861. $this->model = $model;
  862. }
  863. /**
  864. * 析构方法
  865. * @access public
  866. */
  867. public function __destruct() {
  868. // 释放查询
  869. if ($this->queryID){
  870. $this->free();
  871. }
  872. // 关闭连接
  873. $this->close();
  874. }
  875. // 关闭数据库 由驱动类定义
  876. public function close(){}
  877. }