Db.class.php 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923
  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 $exp = array('eq'=>'=','neq'=>'<>','gt'=>'>','egt'=>'>=','lt'=>'<','elt'=>'<=','notlike'=>'NOT LIKE','like'=>'LIKE','in'=>'IN','notin'=>'NOT IN','not in'=>'NOT IN','between'=>'BETWEEN','notbetween'=>'NOT BETWEEN','not between'=>'NOT BETWEEN');
  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).'='.$val[1];
  272. }elseif(is_scalar($val) || is_null(($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(is_numeric($key)){
  409. $key = '_complex';
  410. }
  411. if(0===strpos($key,'_')) {
  412. // 解析特殊条件表达式
  413. $whereStr .= $this->parseThinkWhere($key,$val);
  414. }else{
  415. // 查询字段的安全过滤
  416. if(!preg_match('/^[A-Z_\|\&\-.a-z0-9\(\)\,]+$/',trim($key))){
  417. throw_exception(L('_EXPRESS_ERROR_').':'.$key);
  418. }
  419. // 多条件支持
  420. $multi = is_array($val) && isset($val['_multi']);
  421. $key = trim($key);
  422. if(strpos($key,'|')) { // 支持 name|title|nickname 方式定义查询字段
  423. $array = explode('|',$key);
  424. $str = array();
  425. foreach ($array as $m=>$k){
  426. $v = $multi?$val[$m]:$val;
  427. $str[] = '('.$this->parseWhereItem($this->parseKey($k),$v).')';
  428. }
  429. $whereStr .= implode(' OR ',$str);
  430. }elseif(strpos($key,'&')){
  431. $array = explode('&',$key);
  432. $str = array();
  433. foreach ($array as $m=>$k){
  434. $v = $multi?$val[$m]:$val;
  435. $str[] = '('.$this->parseWhereItem($this->parseKey($k),$v).')';
  436. }
  437. $whereStr .= implode(' AND ',$str);
  438. }else{
  439. $whereStr .= $this->parseWhereItem($this->parseKey($key),$val);
  440. }
  441. }
  442. $whereStr .= ' )'.$operate;
  443. }
  444. $whereStr = substr($whereStr,0,-strlen($operate));
  445. }
  446. return empty($whereStr)?'':' WHERE '.$whereStr;
  447. }
  448. // where子单元分析
  449. protected function parseWhereItem($key,$val) {
  450. $whereStr = '';
  451. if(is_array($val)) {
  452. if(is_string($val[0])) {
  453. $exp = strtolower($val[0]);
  454. if(preg_match('/^(EQ|NEQ|GT|EGT|LT|ELT)$/i',$val[0])) { // 比较运算
  455. $whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($val[1]);
  456. }elseif(preg_match('/^(NOTLIKE|LIKE)$/i',$val[0])){// 模糊查找
  457. if(is_array($val[1])) {
  458. $likeLogic = isset($val[2])?strtoupper($val[2]):'OR';
  459. if(in_array($likeLogic,array('AND','OR','XOR'))){
  460. $like = array();
  461. foreach ($val[1] as $item){
  462. $like[] = $key.' '.$this->exp[$exp].' '.$this->parseValue($item);
  463. }
  464. $whereStr .= '('.implode(' '.$likeLogic.' ',$like).')';
  465. }
  466. }else{
  467. $whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($val[1]);
  468. }
  469. }elseif('exp'==$exp){ // 使用表达式
  470. $whereStr .= ' ('.$key.' '.$val[1].') ';
  471. }elseif(preg_match('/^(NOTIN|NOT IN|IN)$/i',$val[0])){ // IN 运算
  472. if(isset($val[2]) && 'exp'==$val[2]) {
  473. $whereStr .= $key.' '.$this->exp[$exp].' '.$val[1];
  474. }else{
  475. if(is_string($val[1])) {
  476. $val[1] = explode(',',$val[1]);
  477. }
  478. $zone = implode(',',$this->parseValue($val[1]));
  479. $whereStr .= $key.' '.$this->exp[$exp].' ('.$zone.')';
  480. }
  481. }elseif(preg_match('/^(NOTBETWEEN|NOT BETWEEN|BETWEEN)$/i',$val[0])){ // BETWEEN运算
  482. $data = is_string($val[1])? explode(',',$val[1]):$val[1];
  483. $whereStr .= ' ('.$key.' '.$this->exp[$exp].' '.$this->parseValue($data[0]).' AND '.$this->parseValue($data[1]).' )';
  484. }else{
  485. throw_exception(L('_EXPRESS_ERROR_').':'.$val[0]);
  486. }
  487. }else {
  488. $count = count($val);
  489. $rule = isset($val[$count-1])?strtoupper($val[$count-1]):'';
  490. if(in_array($rule,array('AND','OR','XOR'))) {
  491. $count = $count -1;
  492. }else{
  493. $rule = 'AND';
  494. }
  495. for($i=0;$i<$count;$i++) {
  496. $data = is_array($val[$i])?$val[$i][1]:$val[$i];
  497. if('exp'==strtolower($val[$i][0])) {
  498. $whereStr .= '('.$key.' '.$data.') '.$rule.' ';
  499. }else{
  500. $op = is_array($val[$i])?$this->exp[strtolower($val[$i][0])]:'=';
  501. $whereStr .= '('.$key.' '.$op.' '.$this->parseValue($data).') '.$rule.' ';
  502. }
  503. }
  504. $whereStr = substr($whereStr,0,-4);
  505. }
  506. }else {
  507. //对字符串类型字段采用模糊匹配
  508. if(C('DB_LIKE_FIELDS') && preg_match('/^('.C('DB_LIKE_FIELDS').')$/i',$key)) {
  509. $val = '%'.$val.'%';
  510. $whereStr .= $key.' LIKE '.$this->parseValue($val);
  511. }else {
  512. $whereStr .= $key.' = '.$this->parseValue($val);
  513. }
  514. }
  515. return $whereStr;
  516. }
  517. /**
  518. * 特殊条件分析
  519. * @access protected
  520. * @param string $key
  521. * @param mixed $val
  522. * @return string
  523. */
  524. protected function parseThinkWhere($key,$val) {
  525. $whereStr = '';
  526. switch($key) {
  527. case '_string':
  528. // 字符串模式查询条件
  529. $whereStr = $val;
  530. break;
  531. case '_complex':
  532. // 复合查询条件
  533. $whereStr = is_string($val)? $val : substr($this->parseWhere($val),6);
  534. break;
  535. case '_query':
  536. // 字符串模式查询条件
  537. parse_str($val,$where);
  538. if(isset($where['_logic'])) {
  539. $op = ' '.strtoupper($where['_logic']).' ';
  540. unset($where['_logic']);
  541. }else{
  542. $op = ' AND ';
  543. }
  544. $array = array();
  545. foreach ($where as $field=>$data)
  546. $array[] = $this->parseKey($field).' = '.$this->parseValue($data);
  547. $whereStr = implode($op,$array);
  548. break;
  549. }
  550. return $whereStr;
  551. }
  552. /**
  553. * limit分析
  554. * @access protected
  555. * @param mixed $lmit
  556. * @return string
  557. */
  558. protected function parseLimit($limit) {
  559. return !empty($limit)? ' LIMIT '.$limit.' ':'';
  560. }
  561. /**
  562. * join分析
  563. * @access protected
  564. * @param mixed $join
  565. * @return string
  566. */
  567. protected function parseJoin($join) {
  568. $joinStr = '';
  569. if(!empty($join)) {
  570. if(is_array($join)) {
  571. foreach ($join as $key=>$_join){
  572. if(false !== stripos($_join,'JOIN'))
  573. $joinStr .= ' '.$_join;
  574. else
  575. $joinStr .= ' LEFT JOIN ' .$_join;
  576. }
  577. }else{
  578. $joinStr .= ' LEFT JOIN ' .$join;
  579. }
  580. }
  581. //将__TABLE_NAME__这样的字符串替换成正规的表名,并且带上前缀和后缀
  582. $joinStr = preg_replace("/__([A-Z_-]+)__/esU",C("DB_PREFIX")."strtolower('$1')",$joinStr);
  583. return $joinStr;
  584. }
  585. /**
  586. * order分析
  587. * @access protected
  588. * @param mixed $order
  589. * @return string
  590. */
  591. protected function parseOrder($order) {
  592. if(is_array($order)) {
  593. $array = array();
  594. foreach ($order as $key=>$val){
  595. if(is_numeric($key)) {
  596. $array[] = $this->parseKey($val);
  597. }else{
  598. $array[] = $this->parseKey($key).' '.$val;
  599. }
  600. }
  601. $order = implode(',',$array);
  602. }
  603. return !empty($order)? ' ORDER BY '.$order:'';
  604. }
  605. /**
  606. * group分析
  607. * @access protected
  608. * @param mixed $group
  609. * @return string
  610. */
  611. protected function parseGroup($group) {
  612. return !empty($group)? ' GROUP BY '.$group:'';
  613. }
  614. /**
  615. * having分析
  616. * @access protected
  617. * @param string $having
  618. * @return string
  619. */
  620. protected function parseHaving($having) {
  621. return !empty($having)? ' HAVING '.$having:'';
  622. }
  623. /**
  624. * comment分析
  625. * @access protected
  626. * @param string $comment
  627. * @return string
  628. */
  629. protected function parseComment($comment) {
  630. return !empty($comment)? ' /* '.$comment.' */':'';
  631. }
  632. /**
  633. * distinct分析
  634. * @access protected
  635. * @param mixed $distinct
  636. * @return string
  637. */
  638. protected function parseDistinct($distinct) {
  639. return !empty($distinct)? ' DISTINCT ' :'';
  640. }
  641. /**
  642. * union分析
  643. * @access protected
  644. * @param mixed $union
  645. * @return string
  646. */
  647. protected function parseUnion($union) {
  648. if(empty($union)) return '';
  649. if(isset($union['_all'])) {
  650. $str = 'UNION ALL ';
  651. unset($union['_all']);
  652. }else{
  653. $str = 'UNION ';
  654. }
  655. foreach ($union as $u){
  656. $sql[] = $str.(is_array($u)?$this->buildSelectSql($u):$u);
  657. }
  658. return implode(' ',$sql);
  659. }
  660. /**
  661. * 插入记录
  662. * @access public
  663. * @param mixed $data 数据
  664. * @param array $options 参数表达式
  665. * @param boolean $replace 是否replace
  666. * @return false | integer
  667. */
  668. public function insert($data,$options=array(),$replace=false) {
  669. $values = $fields = array();
  670. $this->model = $options['model'];
  671. foreach ($data as $key=>$val){
  672. if(is_array($val) && 'exp' == $val[0]){
  673. $fields[] = $this->parseKey($key);
  674. $values[] = $val[1];
  675. }elseif(is_scalar($val) || is_null(($val))) { // 过滤非标量数据
  676. $fields[] = $this->parseKey($key);
  677. if(C('DB_BIND_PARAM') && 0 !== strpos($val,':')){
  678. $name = md5($key);
  679. $values[] = ':'.$name;
  680. $this->bindParam($name,$val);
  681. }else{
  682. $values[] = $this->parseValue($val);
  683. }
  684. }
  685. }
  686. $sql = ($replace?'REPLACE':'INSERT').' INTO '.$this->parseTable($options['table']).' ('.implode(',', $fields).') VALUES ('.implode(',', $values).')';
  687. $sql .= $this->parseLock(isset($options['lock'])?$options['lock']:false);
  688. $sql .= $this->parseComment(!empty($options['comment'])?$options['comment']:'');
  689. return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array()));
  690. }
  691. /**
  692. * 通过Select方式插入记录
  693. * @access public
  694. * @param string $fields 要插入的数据表字段名
  695. * @param string $table 要插入的数据表名
  696. * @param array $option 查询数据参数
  697. * @return false | integer
  698. */
  699. public function selectInsert($fields,$table,$options=array()) {
  700. $this->model = $options['model'];
  701. if(is_string($fields)) $fields = explode(',',$fields);
  702. array_walk($fields, array($this, 'parseKey'));
  703. $sql = 'INSERT INTO '.$this->parseTable($table).' ('.implode(',', $fields).') ';
  704. $sql .= $this->buildSelectSql($options);
  705. return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array()));
  706. }
  707. /**
  708. * 更新记录
  709. * @access public
  710. * @param mixed $data 数据
  711. * @param array $options 表达式
  712. * @return false | integer
  713. */
  714. public function update($data,$options) {
  715. $this->model = $options['model'];
  716. $sql = 'UPDATE '
  717. .$this->parseTable($options['table'])
  718. .$this->parseSet($data)
  719. .$this->parseWhere(!empty($options['where'])?$options['where']:'')
  720. .$this->parseOrder(!empty($options['order'])?$options['order']:'')
  721. .$this->parseLimit(!empty($options['limit'])?$options['limit']:'')
  722. .$this->parseLock(isset($options['lock'])?$options['lock']:false)
  723. .$this->parseComment(!empty($options['comment'])?$options['comment']:'');
  724. return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array()));
  725. }
  726. /**
  727. * 删除记录
  728. * @access public
  729. * @param array $options 表达式
  730. * @return false | integer
  731. */
  732. public function delete($options=array()) {
  733. $this->model = $options['model'];
  734. $sql = 'DELETE FROM '
  735. .$this->parseTable($options['table'])
  736. .$this->parseWhere(!empty($options['where'])?$options['where']:'')
  737. .$this->parseOrder(!empty($options['order'])?$options['order']:'')
  738. .$this->parseLimit(!empty($options['limit'])?$options['limit']:'')
  739. .$this->parseLock(isset($options['lock'])?$options['lock']:false)
  740. .$this->parseComment(!empty($options['comment'])?$options['comment']:'');
  741. return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array()));
  742. }
  743. /**
  744. * 查找记录
  745. * @access public
  746. * @param array $options 表达式
  747. * @return mixed
  748. */
  749. public function select($options=array()) {
  750. $this->model = $options['model'];
  751. $sql = $this->buildSelectSql($options);
  752. $cache = isset($options['cache'])?$options['cache']:false;
  753. if($cache) { // 查询缓存检测
  754. $key = is_string($cache['key'])?$cache['key']:md5($sql);
  755. $value = S($key,'',$cache);
  756. if(false !== $value) {
  757. return $value;
  758. }
  759. }
  760. $result = $this->query($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array()));
  761. if($cache && false !== $result ) { // 查询缓存写入
  762. S($key,$result,$cache);
  763. }
  764. return $result;
  765. }
  766. /**
  767. * 生成查询SQL
  768. * @access public
  769. * @param array $options 表达式
  770. * @return string
  771. */
  772. public function buildSelectSql($options=array()) {
  773. if(isset($options['page'])) {
  774. // 根据页数计算limit
  775. if(strpos($options['page'],',')) {
  776. list($page,$listRows) = explode(',',$options['page']);
  777. }else{
  778. $page = $options['page'];
  779. }
  780. $page = $page?$page:1;
  781. $listRows= isset($listRows)?$listRows:(is_numeric($options['limit'])?$options['limit']:20);
  782. $offset = $listRows*((int)$page-1);
  783. $options['limit'] = $offset.','.$listRows;
  784. }
  785. if(C('DB_SQL_BUILD_CACHE')) { // SQL创建缓存
  786. $key = md5(serialize($options));
  787. $value = S($key);
  788. if(false !== $value) {
  789. return $value;
  790. }
  791. }
  792. $sql = $this->parseSql($this->selectSql,$options);
  793. $sql .= $this->parseLock(isset($options['lock'])?$options['lock']:false);
  794. if(isset($key)) { // 写入SQL创建缓存
  795. S($key,$sql,array('expire'=>0,'length'=>C('DB_SQL_BUILD_LENGTH'),'queue'=>C('DB_SQL_BUILD_QUEUE')));
  796. }
  797. return $sql;
  798. }
  799. /**
  800. * 替换SQL语句中表达式
  801. * @access public
  802. * @param array $options 表达式
  803. * @return string
  804. */
  805. public function parseSql($sql,$options=array()){
  806. $sql = str_replace(
  807. array('%TABLE%','%DISTINCT%','%FIELD%','%JOIN%','%WHERE%','%GROUP%','%HAVING%','%ORDER%','%LIMIT%','%UNION%','%COMMENT%'),
  808. array(
  809. $this->parseTable($options['table']),
  810. $this->parseDistinct(isset($options['distinct'])?$options['distinct']:false),
  811. $this->parseField(!empty($options['field'])?$options['field']:'*'),
  812. $this->parseJoin(!empty($options['join'])?$options['join']:''),
  813. $this->parseWhere(!empty($options['where'])?$options['where']:''),
  814. $this->parseGroup(!empty($options['group'])?$options['group']:''),
  815. $this->parseHaving(!empty($options['having'])?$options['having']:''),
  816. $this->parseOrder(!empty($options['order'])?$options['order']:''),
  817. $this->parseLimit(!empty($options['limit'])?$options['limit']:''),
  818. $this->parseUnion(!empty($options['union'])?$options['union']:''),
  819. $this->parseComment(!empty($options['comment'])?$options['comment']:'')
  820. ),$sql);
  821. return $sql;
  822. }
  823. /**
  824. * 获取最近一次查询的sql语句
  825. * @param string $model 模型名
  826. * @access public
  827. * @return string
  828. */
  829. public function getLastSql($model='') {
  830. return $model?$this->modelSql[$model]:$this->queryStr;
  831. }
  832. /**
  833. * 获取最近插入的ID
  834. * @access public
  835. * @return string
  836. */
  837. public function getLastInsID() {
  838. return $this->lastInsID;
  839. }
  840. /**
  841. * 获取最近的错误信息
  842. * @access public
  843. * @return string
  844. */
  845. public function getError() {
  846. return $this->error;
  847. }
  848. /**
  849. * SQL指令安全过滤
  850. * @access public
  851. * @param string $str SQL字符串
  852. * @return string
  853. */
  854. public function escapeString($str) {
  855. return addslashes($str);
  856. }
  857. /**
  858. * 设置当前操作模型
  859. * @access public
  860. * @param string $model 模型名
  861. * @return void
  862. */
  863. public function setModel($model){
  864. $this->model = $model;
  865. }
  866. /**
  867. * 析构方法
  868. * @access public
  869. */
  870. public function __destruct() {
  871. // 释放查询
  872. if ($this->queryID){
  873. $this->free();
  874. }
  875. // 关闭连接
  876. $this->close();
  877. }
  878. // 关闭数据库 由驱动类定义
  879. public function close(){}
  880. }