Db.class.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  1. <?php
  2. // +--------------------------------------------------------------------------
  3. // | Senthot [ DEVELOPED BY ME ]
  4. // +--------------------------------------------------------------------------
  5. // | Copyright (c) 2005-2013 http://www.senthot.com All rights reserved.
  6. // | License ( http://www.apache.org/licenses/LICENSE-2.0 )
  7. // | Author: ms134n ( [email protected] )
  8. // +--------------------------------------------------------------------------
  9. /**
  10. * Senthot Database middle tier implementation class
  11. * @category Sen
  12. * @package Sen
  13. * @subpackage Core
  14. * @author ms134n <[email protected]>
  15. */
  16. class Db {
  17. // Database Type
  18. protected $dbType = null;
  19. // Whether to automatically release the results
  20. protected $autoFree = false;
  21. // Current operation belongs to the model name
  22. protected $model = '_sen_';
  23. // Whether to use persistent connections
  24. protected $pconnect = false;
  25. // Current SQL commands
  26. protected $queryStr = '';
  27. protected $modelSql = array();
  28. // Last inserted ID
  29. protected $lastInsID = null;
  30. // Or to affect the number of records returned
  31. protected $numRows = 0;
  32. // Returns the number of fields
  33. protected $numCols = 0;
  34. // Service Instructions
  35. protected $transTimes = 0;
  36. // Error Messages
  37. protected $error = '';
  38. // Database Connection ID Support multiple connections
  39. protected $linkID = array();
  40. // Current connection ID
  41. protected $_linkID = null;
  42. // Current query ID
  43. protected $queryID = null;
  44. // Is already connected to the database
  45. protected $connected = false;
  46. // Database connection configuration parameters
  47. protected $config = '';
  48. // Database Expressions
  49. protected $comparison = array('eq'=>'=','neq'=>'<>','gt'=>'>','egt'=>'>=','lt'=>'<','elt'=>'<=','notlike'=>'NOT LIKE','like'=>'LIKE','in'=>'IN','notin'=>'NOT IN');
  50. // Query Expressions
  51. protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%COMMENT%';
  52. /**
  53. * Get database class instance
  54. * @static
  55. * @access public
  56. * @return mixed Returns the database driver class
  57. */
  58. public static function getInstance() {
  59. $args = func_get_args();
  60. return get_instance_of(__CLASS__,'factory',$args);
  61. }
  62. /**
  63. * Loading the database Support configuration file or DSN
  64. * @access public
  65. * @param mixed $db_config Database configuration information
  66. * @return string
  67. */
  68. public function factory($db_config='') {
  69. // Read database configuration
  70. $db_config = $this->parseConfig($db_config);
  71. if(empty($db_config['dbms']))
  72. throw_exception(L('_NO_DB_CONFIG_'));
  73. // Database Type
  74. $this->dbType = ucwords(strtolower($db_config['dbms']));
  75. $class = 'Db'. $this->dbType;
  76. // Check the driver class
  77. if(class_exists($class)) {
  78. $db = new $class($db_config);
  79. // Get the current database type
  80. if( 'pdo' != strtolower($db_config['dbms']) )
  81. $db->dbType = strtoupper($this->dbType);
  82. else
  83. $db->dbType = $this->_getDsnType($db_config['dsn']);
  84. }else {
  85. // Class does not define
  86. throw_exception(L('_NO_DB_DRIVER_').': ' . $class);
  87. }
  88. return $db;
  89. }
  90. /**
  91. * According DSN for the database type Back to uppercase
  92. * @access protected
  93. * @param string $dsn dsn string
  94. * @return string
  95. */
  96. protected function _getDsnType($dsn) {
  97. $match = explode(':',$dsn);
  98. $dbType = strtoupper(trim($match[0]));
  99. return $dbType;
  100. }
  101. /**
  102. * Analysis of the database configuration information, Support and DSN array
  103. * @access private
  104. * @param mixed $db_config Database configuration information
  105. * @return string
  106. */
  107. private function parseConfig($db_config='') {
  108. if ( !empty($db_config) && is_string($db_config)) {
  109. // If the DSN string is parsed
  110. $db_config = $this->parseDSN($db_config);
  111. }elseif(is_array($db_config)) { // Array Configuration
  112. $db_config = array_change_key_case($db_config);
  113. $db_config = array(
  114. 'dbms' => $db_config['db_type'],
  115. 'username' => $db_config['db_user'],
  116. 'password' => $db_config['db_pwd'],
  117. 'hostname' => $db_config['db_host'],
  118. 'hostport' => $db_config['db_port'],
  119. 'database' => $db_config['db_name'],
  120. 'dsn' => $db_config['db_dsn'],
  121. 'params' => $db_config['db_params'],
  122. );
  123. }elseif(empty($db_config)) {
  124. // If the configuration is empty, reads the configuration file settings
  125. if( C('DB_DSN') && 'pdo' != strtolower(C('DB_TYPE')) ) { // If you set the DB_DSN Preferentially
  126. $db_config = $this->parseDSN(C('DB_DSN'));
  127. }else{
  128. $db_config = array (
  129. 'dbms' => C('DB_TYPE'),
  130. 'username' => C('DB_USER'),
  131. 'password' => C('DB_PWD'),
  132. 'hostname' => C('DB_HOST'),
  133. 'hostport' => C('DB_PORT'),
  134. 'database' => C('DB_NAME'),
  135. 'dsn' => C('DB_DSN'),
  136. 'params' => C('DB_PARAMS'),
  137. );
  138. }
  139. }
  140. return $db_config;
  141. }
  142. /**
  143. * Initialize the database connection
  144. * @access protected
  145. * @param boolean $master Master Server
  146. * @return void
  147. */
  148. protected function initConnect($master=true) {
  149. if(1 == C('DB_DEPLOY_TYPE'))
  150. // Using a distributed database
  151. $this->_linkID = $this->multiConnect($master);
  152. else
  153. // Default single database
  154. if ( !$this->connected ) $this->_linkID = $this->connect();
  155. }
  156. /**
  157. * Distributed server connection
  158. * @access protected
  159. * @param boolean $master Master Server
  160. * @return void
  161. */
  162. protected function multiConnect($master=false) {
  163. static $_config = array();
  164. if(empty($_config)) {
  165. // Distributed database configuration parsing cache
  166. foreach ($this->config as $key=>$val){
  167. $_config[$key] = explode(',',$val);
  168. }
  169. }
  170. // Whether separate database literacy
  171. if(C('DB_RW_SEPARATE')){
  172. // Master-slave using separate read and write
  173. if($master)
  174. // Primary server writes
  175. $r = floor(mt_rand(0,C('DB_MASTER_NUM')-1));
  176. else{
  177. if(is_numeric(C('DB_SLAVE_NO'))) {// Read the specified server
  178. $r = C('DB_SLAVE_NO');
  179. }else{
  180. // Read from the server connection
  181. $r = floor(mt_rand(C('DB_MASTER_NUM'),count($_config['hostname'])-1)); // Each randomly connected database
  182. }
  183. }
  184. }else{
  185. // The server does not distinguish between read and write operations
  186. $r = floor(mt_rand(0,count($_config['hostname'])-1)); // Each randomly connected database
  187. }
  188. $db_config = array(
  189. 'username' => isset($_config['username'][$r])?$_config['username'][$r]:$_config['username'][0],
  190. 'password' => isset($_config['password'][$r])?$_config['password'][$r]:$_config['password'][0],
  191. 'hostname' => isset($_config['hostname'][$r])?$_config['hostname'][$r]:$_config['hostname'][0],
  192. 'hostport' => isset($_config['hostport'][$r])?$_config['hostport'][$r]:$_config['hostport'][0],
  193. 'database' => isset($_config['database'][$r])?$_config['database'][$r]:$_config['database'][0],
  194. 'dsn' => isset($_config['dsn'][$r])?$_config['dsn'][$r]:$_config['dsn'][0],
  195. 'params' => isset($_config['params'][$r])?$_config['params'][$r]:$_config['params'][0],
  196. );
  197. return $this->connect($db_config,$r);
  198. }
  199. /**
  200. * DSN parsing
  201. * Format: mysql://username:passwd@localhost:3306/DbName
  202. * @static
  203. * @access public
  204. * @param string $dsnStr
  205. * @return array
  206. */
  207. public function parseDSN($dsnStr) {
  208. if( empty($dsnStr) ){return false;}
  209. $info = parse_url($dsnStr);
  210. if($info['scheme']){
  211. $dsn = array(
  212. 'dbms' => $info['scheme'],
  213. 'username' => isset($info['user']) ? $info['user'] : '',
  214. 'password' => isset($info['pass']) ? $info['pass'] : '',
  215. 'hostname' => isset($info['host']) ? $info['host'] : '',
  216. 'hostport' => isset($info['port']) ? $info['port'] : '',
  217. 'database' => isset($info['path']) ? substr($info['path'],1) : ''
  218. );
  219. }else {
  220. preg_match('/^(.*?)\:\/\/(.*?)\:(.*?)\@(.*?)\:([0-9]{1, 6})\/(.*?)$/',trim($dsnStr),$matches);
  221. $dsn = array (
  222. 'dbms' => $matches[1],
  223. 'username' => $matches[2],
  224. 'password' => $matches[3],
  225. 'hostname' => $matches[4],
  226. 'hostport' => $matches[5],
  227. 'database' => $matches[6]
  228. );
  229. }
  230. $dsn['dsn'] = ''; // Compatible with an array of configuration information
  231. return $dsn;
  232. }
  233. /**
  234. * Database Debugging Record the current SQL
  235. * @access protected
  236. */
  237. protected function debug() {
  238. $this->modelSql[$this->model] = $this->queryStr;
  239. $this->model = '_sen_';
  240. // End time recording operation
  241. if (C('DB_SQL_LOG')) {
  242. G('queryEndTime');
  243. trace($this->queryStr.' [ RunTime:'.G('queryStartTime','queryEndTime',6).'s ]','','SQL');
  244. }
  245. }
  246. /**
  247. * Set the lock mechanism
  248. * @access protected
  249. * @return string
  250. */
  251. protected function parseLock($lock=false) {
  252. if(!$lock) return '';
  253. if('ORACLE' == $this->dbType) {
  254. return ' FOR UPDATE NOWAIT ';
  255. }
  256. return ' FOR UPDATE ';
  257. }
  258. /**
  259. * set analysis
  260. * @access protected
  261. * @param array $data
  262. * @return string
  263. */
  264. protected function parseSet($data) {
  265. foreach ($data as $key=>$val){
  266. $value = $this->parseValue($val);
  267. if(is_scalar($value)) // Filtering non-scalar data
  268. $set[] = $this->parseKey($key).'='.$value;
  269. }
  270. return ' SET '.implode(',',$set);
  271. }
  272. /**
  273. * Analysis of field names
  274. * @access protected
  275. * @param string $key
  276. * @return string
  277. */
  278. protected function parseKey(&$key) {
  279. return $key;
  280. }
  281. /**
  282. * value analysis
  283. * @access protected
  284. * @param mixed $value
  285. * @return string
  286. */
  287. protected function parseValue($value) {
  288. if(is_string($value)) {
  289. $value = '\''.$this->escapeString($value).'\'';
  290. }elseif(isset($value[0]) && is_string($value[0]) && strtolower($value[0]) == 'exp'){
  291. $value = $this->escapeString($value[1]);
  292. }elseif(is_array($value)) {
  293. $value = array_map(array($this, 'parseValue'),$value);
  294. }elseif(is_bool($value)){
  295. $value = $value ? '1' : '0';
  296. }elseif(is_null($value)){
  297. $value = 'null';
  298. }
  299. return $value;
  300. }
  301. /**
  302. * field analysis
  303. * @access protected
  304. * @param mixed $fields
  305. * @return string
  306. */
  307. protected function parseField($fields) {
  308. if(is_string($fields) && strpos($fields,',')) {
  309. $fields = explode(',',$fields);
  310. }
  311. if(is_array($fields)) {
  312. // Perfect way to pass an array of field names Support
  313. // Support 'field1'=>'field2' Such a field alias definitions
  314. $array = array();
  315. foreach ($fields as $key=>$field){
  316. if(!is_numeric($key))
  317. $array[] = $this->parseKey($key).' AS '.$this->parseKey($field);
  318. else
  319. $array[] = $this->parseKey($field);
  320. }
  321. $fieldsStr = implode(',', $array);
  322. }elseif(is_string($fields) && !empty($fields)) {
  323. $fieldsStr = $this->parseKey($fields);
  324. }else{
  325. $fieldsStr = '*';
  326. }
  327. //TODO If all the fields in the query , and is a join of the way, then put the table to check plus individual names , so the field is covered
  328. return $fieldsStr;
  329. }
  330. /**
  331. * table analysis
  332. * @access protected
  333. * @param mixed $table
  334. * @return string
  335. */
  336. protected function parseTable($tables) {
  337. if(is_array($tables)) {// Support alias definitions
  338. $array = array();
  339. foreach ($tables as $table=>$alias){
  340. if(!is_numeric($table))
  341. $array[] = $this->parseKey($table).' '.$this->parseKey($alias);
  342. else
  343. $array[] = $this->parseKey($table);
  344. }
  345. $tables = $array;
  346. }elseif(is_string($tables)){
  347. $tables = explode(',',$tables);
  348. array_walk($tables, array(&$this, 'parseKey'));
  349. }
  350. return implode(',',$tables);
  351. }
  352. /**
  353. * where analysis
  354. * @access protected
  355. * @param mixed $where
  356. * @return string
  357. */
  358. protected function parseWhere($where) {
  359. $whereStr = '';
  360. if(is_string($where)) {
  361. // Conditions of use strings directly
  362. $whereStr = $where;
  363. }else{ // Using array expression
  364. $operate = isset($where['_logic'])?strtoupper($where['_logic']):'';
  365. if(in_array($operate,array('AND','OR','XOR'))){
  366. // Define logical rules For example, OR XOR AND NOT
  367. $operate = ' '.$operate.' ';
  368. unset($where['_logic']);
  369. }else{
  370. // The default for AND Computing
  371. $operate = ' AND ';
  372. }
  373. foreach ($where as $key=>$val){
  374. $whereStr .= '( ';
  375. if(0===strpos($key,'_')) {
  376. // Special conditions analytic expressions
  377. $whereStr .= $this->parseSenWhere($key,$val);
  378. }else{
  379. // Security filtering query fields
  380. if(!preg_match('/^[A-Z_\|\&\-.a-z0-9\(\)\,]+$/',trim($key))){
  381. throw_exception(L('_EXPRESS_ERROR_').':'.$key);
  382. }
  383. // Support multi-condition
  384. $multi = is_array($val) && isset($val['_multi']);
  385. $key = trim($key);
  386. if(strpos($key,'|')) { // Support name|title|nickname Define the query field
  387. $array = explode('|',$key);
  388. $str = array();
  389. foreach ($array as $m=>$k){
  390. $v = $multi?$val[$m]:$val;
  391. $str[] = '('.$this->parseWhereItem($this->parseKey($k),$v).')';
  392. }
  393. $whereStr .= implode(' OR ',$str);
  394. }elseif(strpos($key,'&')){
  395. $array = explode('&',$key);
  396. $str = array();
  397. foreach ($array as $m=>$k){
  398. $v = $multi?$val[$m]:$val;
  399. $str[] = '('.$this->parseWhereItem($this->parseKey($k),$v).')';
  400. }
  401. $whereStr .= implode(' AND ',$str);
  402. }else{
  403. $whereStr .= $this->parseWhereItem($this->parseKey($key),$val);
  404. }
  405. }
  406. $whereStr .= ' )'.$operate;
  407. }
  408. $whereStr = substr($whereStr,0,-strlen($operate));
  409. }
  410. return empty($whereStr)?'':' WHERE '.$whereStr;
  411. }
  412. // where sub- element analysis
  413. protected function parseWhereItem($key,$val) {
  414. $whereStr = '';
  415. if(is_array($val)) {
  416. if(is_string($val[0])) {
  417. if(preg_match('/^(EQ|NEQ|GT|EGT|LT|ELT)$/i',$val[0])) { // Comparison Operators
  418. $whereStr .= $key.' '.$this->comparison[strtolower($val[0])].' '.$this->parseValue($val[1]);
  419. }elseif(preg_match('/^(NOTLIKE|LIKE)$/i',$val[0])){// Fuzzy Lookup
  420. if(is_array($val[1])) {
  421. $likeLogic = isset($val[2])?strtoupper($val[2]):'OR';
  422. if(in_array($likeLogic,array('AND','OR','XOR'))){
  423. $likeStr = $this->comparison[strtolower($val[0])];
  424. $like = array();
  425. foreach ($val[1] as $item){
  426. $like[] = $key.' '.$likeStr.' '.$this->parseValue($item);
  427. }
  428. $whereStr .= '('.implode(' '.$likeLogic.' ',$like).')';
  429. }
  430. }else{
  431. $whereStr .= $key.' '.$this->comparison[strtolower($val[0])].' '.$this->parseValue($val[1]);
  432. }
  433. }elseif('exp'==strtolower($val[0])){ // Using Expressions
  434. $whereStr .= ' ('.$key.' '.$val[1].') ';
  435. }elseif(preg_match('/IN/i',$val[0])){ // IN Computing
  436. if(isset($val[2]) && 'exp'==$val[2]) {
  437. $whereStr .= $key.' '.strtoupper($val[0]).' '.$val[1];
  438. }else{
  439. if(is_string($val[1])) {
  440. $val[1] = explode(',',$val[1]);
  441. }
  442. $zone = implode(',',$this->parseValue($val[1]));
  443. $whereStr .= $key.' '.strtoupper($val[0]).' ('.$zone.')';
  444. }
  445. }elseif(preg_match('/BETWEEN/i',$val[0])){ // BETWEEN operator
  446. $data = is_string($val[1])? explode(',',$val[1]):$val[1];
  447. $whereStr .= ' ('.$key.' '.strtoupper($val[0]).' '.$this->parseValue($data[0]).' AND '.$this->parseValue($data[1]).' )';
  448. }else{
  449. throw_exception(L('_EXPRESS_ERROR_').':'.$val[0]);
  450. }
  451. }else {
  452. $count = count($val);
  453. $rule = isset($val[$count-1])?strtoupper($val[$count-1]):'';
  454. if(in_array($rule,array('AND','OR','XOR'))) {
  455. $count = $count -1;
  456. }else{
  457. $rule = 'AND';
  458. }
  459. for($i=0;$i<$count;$i++) {
  460. $data = is_array($val[$i])?$val[$i][1]:$val[$i];
  461. if('exp'==strtolower($val[$i][0])) {
  462. $whereStr .= '('.$key.' '.$data.') '.$rule.' ';
  463. }else{
  464. $op = is_array($val[$i])?$this->comparison[strtolower($val[$i][0])]:'=';
  465. $whereStr .= '('.$key.' '.$op.' '.$this->parseValue($data).') '.$rule.' ';
  466. }
  467. }
  468. $whereStr = substr($whereStr,0,-4);
  469. }
  470. }else {
  471. //The string type field using fuzzy matching
  472. if(C('DB_LIKE_FIELDS') && preg_match('/('.C('DB_LIKE_FIELDS').')/i',$key)) {
  473. $val = '%'.$val.'%';
  474. $whereStr .= $key.' LIKE '.$this->parseValue($val);
  475. }else {
  476. $whereStr .= $key.' = '.$this->parseValue($val);
  477. }
  478. }
  479. return $whereStr;
  480. }
  481. /**
  482. * Special Conditions
  483. * @access protected
  484. * @param string $key
  485. * @param mixed $val
  486. * @return string
  487. */
  488. protected function parseSenWhere($key,$val) {
  489. $whereStr = '';
  490. switch($key) {
  491. case '_string':
  492. // Query string pattern
  493. $whereStr = $val;
  494. break;
  495. case '_complex':
  496. // Composite query
  497. $whereStr = substr($this->parseWhere($val),6);
  498. break;
  499. case '_query':
  500. // Query string pattern
  501. parse_str($val,$where);
  502. if(isset($where['_logic'])) {
  503. $op = ' '.strtoupper($where['_logic']).' ';
  504. unset($where['_logic']);
  505. }else{
  506. $op = ' AND ';
  507. }
  508. $array = array();
  509. foreach ($where as $field=>$data)
  510. $array[] = $this->parseKey($field).' = '.$this->parseValue($data);
  511. $whereStr = implode($op,$array);
  512. break;
  513. }
  514. return $whereStr;
  515. }
  516. /**
  517. * limit analysis
  518. * @access protected
  519. * @param mixed $lmit
  520. * @return string
  521. */
  522. protected function parseLimit($limit) {
  523. return !empty($limit)? ' LIMIT '.$limit.' ':'';
  524. }
  525. /**
  526. * join analysis
  527. * @access protected
  528. * @param mixed $join
  529. * @return string
  530. */
  531. protected function parseJoin($join) {
  532. $joinStr = '';
  533. if(!empty($join)) {
  534. if(is_array($join)) {
  535. foreach ($join as $key=>$_join){
  536. if(false !== stripos($_join,'JOIN'))
  537. $joinStr .= ' '.$_join;
  538. else
  539. $joinStr .= ' LEFT JOIN ' .$_join;
  540. }
  541. }else{
  542. $joinStr .= ' LEFT JOIN ' .$join;
  543. }
  544. }
  545. //This string will __TABLE_NAME__ replace regular table name, and bring a prefix and suffix
  546. $joinStr = preg_replace("/__([A-Z_-]+)__/esU",C("DB_PREFIX").".strtolower('$1')",$joinStr);
  547. return $joinStr;
  548. }
  549. /**
  550. * order analysis
  551. * @access protected
  552. * @param mixed $order
  553. * @return string
  554. */
  555. protected function parseOrder($order) {
  556. if(is_array($order)) {
  557. $array = array();
  558. foreach ($order as $key=>$val){
  559. if(is_numeric($key)) {
  560. $array[] = $this->parseKey($val);
  561. }else{
  562. $array[] = $this->parseKey($key).' '.$val;
  563. }
  564. }
  565. $order = implode(',',$array);
  566. }
  567. return !empty($order)? ' ORDER BY '.$order:'';
  568. }
  569. /**
  570. * group analysis
  571. * @access protected
  572. * @param mixed $group
  573. * @return string
  574. */
  575. protected function parseGroup($group) {
  576. return !empty($group)? ' GROUP BY '.$group:'';
  577. }
  578. /**
  579. * having analyzed
  580. * @access protected
  581. * @param string $having
  582. * @return string
  583. */
  584. protected function parseHaving($having) {
  585. return !empty($having)? ' HAVING '.$having:'';
  586. }
  587. /**
  588. * comment analysis
  589. * @access protected
  590. * @param string $comment
  591. * @return string
  592. */
  593. protected function parseComment($comment) {
  594. return !empty($comment)? ' /* '.$comment.' */':'';
  595. }
  596. /**
  597. * distinct analysis
  598. * @access protected
  599. * @param mixed $distinct
  600. * @return string
  601. */
  602. protected function parseDistinct($distinct) {
  603. return !empty($distinct)? ' DISTINCT ' :'';
  604. }
  605. /**
  606. * union analysis
  607. * @access protected
  608. * @param mixed $union
  609. * @return string
  610. */
  611. protected function parseUnion($union) {
  612. if(empty($union)) return '';
  613. if(isset($union['_all'])) {
  614. $str = 'UNION ALL ';
  615. unset($union['_all']);
  616. }else{
  617. $str = 'UNION ';
  618. }
  619. foreach ($union as $u){
  620. $sql[] = $str.(is_array($u)?$this->buildSelectSql($u):$u);
  621. }
  622. return implode(' ',$sql);
  623. }
  624. /**
  625. * Insert records
  626. * @access public
  627. * @param mixed $data Data
  628. * @param array $options Parameter expression
  629. * @param boolean $replace Whether or replace
  630. * @return false | integer
  631. */
  632. public function insert($data,$options=array(),$replace=false) {
  633. $values = $fields = array();
  634. $this->model = $options['model'];
  635. foreach ($data as $key=>$val){
  636. $value = $this->parseValue($val);
  637. if(is_scalar($value)) { // Filtering non-scalar data
  638. $values[] = $value;
  639. $fields[] = $this->parseKey($key);
  640. }
  641. }
  642. $sql = ($replace?'REPLACE':'INSERT').' INTO '.$this->parseTable($options['table']).' ('.implode(',', $fields).') VALUES ('.implode(',', $values).')';
  643. $sql .= $this->parseLock(isset($options['lock'])?$options['lock']:false);
  644. $sql .= $this->parseComment(!empty($options['comment'])?$options['comment']:'');
  645. return $this->execute($sql);
  646. }
  647. /**
  648. * Inserted through the Select Records
  649. * @access public
  650. * @param string $fields To insert a data table field names
  651. * @param string $table To insert the data table name
  652. * @param array $option Query data parameters
  653. * @return false | integer
  654. */
  655. public function selectInsert($fields,$table,$options=array()) {
  656. $this->model = $options['model'];
  657. if(is_string($fields)) $fields = explode(',',$fields);
  658. array_walk($fields, array($this, 'parseKey'));
  659. $sql = 'INSERT INTO '.$this->parseTable($table).' ('.implode(',', $fields).') ';
  660. $sql .= $this->buildSelectSql($options);
  661. return $this->execute($sql);
  662. }
  663. /**
  664. * Update records
  665. * @access public
  666. * @param mixed $data Data
  667. * @param array $options Expression
  668. * @return false | integer
  669. */
  670. public function update($data,$options) {
  671. $this->model = $options['model'];
  672. $sql = 'UPDATE '
  673. .$this->parseTable($options['table'])
  674. .$this->parseSet($data)
  675. .$this->parseWhere(!empty($options['where'])?$options['where']:'')
  676. .$this->parseOrder(!empty($options['order'])?$options['order']:'')
  677. .$this->parseLimit(!empty($options['limit'])?$options['limit']:'')
  678. .$this->parseLock(isset($options['lock'])?$options['lock']:false)
  679. .$this->parseComment(!empty($options['comment'])?$options['comment']:'');
  680. return $this->execute($sql);
  681. }
  682. /**
  683. * Delete records
  684. * @access public
  685. * @param array $options Expression
  686. * @return false | integer
  687. */
  688. public function delete($options=array()) {
  689. $this->model = $options['model'];
  690. $sql = 'DELETE FROM '
  691. .$this->parseTable($options['table'])
  692. .$this->parseWhere(!empty($options['where'])?$options['where']:'')
  693. .$this->parseOrder(!empty($options['order'])?$options['order']:'')
  694. .$this->parseLimit(!empty($options['limit'])?$options['limit']:'')
  695. .$this->parseLock(isset($options['lock'])?$options['lock']:false)
  696. .$this->parseComment(!empty($options['comment'])?$options['comment']:'');
  697. return $this->execute($sql);
  698. }
  699. /**
  700. * Find Record
  701. * @access public
  702. * @param array $options Expression
  703. * @return mixed
  704. */
  705. public function select($options=array()) {
  706. $this->model = $options['model'];
  707. $sql = $this->buildSelectSql($options);
  708. $cache = isset($options['cache'])?$options['cache']:false;
  709. if($cache) { // Query cache detection
  710. $key = is_string($cache['key'])?$cache['key']:md5($sql);
  711. $value = S($key,'',$cache);
  712. if(false !== $value) {
  713. return $value;
  714. }
  715. }
  716. $result = $this->query($sql);
  717. if($cache && false !== $result ) { // Write the query cache
  718. S($key,$result,$cache);
  719. }
  720. return $result;
  721. }
  722. /**
  723. * Generate SQL queries
  724. * @access public
  725. * @param array $options Expression
  726. * @return string
  727. */
  728. public function buildSelectSql($options=array()) {
  729. if(isset($options['page'])) {
  730. // According Pages calculated limit
  731. if(strpos($options['page'],',')) {
  732. list($page,$listRows) = explode(',',$options['page']);
  733. }else{
  734. $page = $options['page'];
  735. }
  736. $page = $page?$page:1;
  737. $listRows= isset($listRows)?$listRows:(is_numeric($options['limit'])?$options['limit']:20);
  738. $offset = $listRows*((int)$page-1);
  739. $options['limit'] = $offset.','.$listRows;
  740. }
  741. if(C('DB_SQL_BUILD_CACHE')) { // SQL to create the cache
  742. $key = md5(serialize($options));
  743. $value = S($key);
  744. if(false !== $value) {
  745. return $value;
  746. }
  747. }
  748. $sql = $this->parseSql($this->selectSql,$options);
  749. $sql .= $this->parseLock(isset($options['lock'])?$options['lock']:false);
  750. if(isset($key)) { // Write SQL to create the cache
  751. S($key,$sql,array('expire'=>0,'length'=>C('DB_SQL_BUILD_LENGTH'),'queue'=>C('DB_SQL_BUILD_QUEUE')));
  752. }
  753. return $sql;
  754. }
  755. /**
  756. * Replace expressions in SQL statements
  757. * @access public
  758. * @param array $options Expression
  759. * @return string
  760. */
  761. public function parseSql($sql,$options=array()){
  762. $sql = str_replace(
  763. array('%TABLE%','%DISTINCT%','%FIELD%','%JOIN%','%WHERE%','%GROUP%','%HAVING%','%ORDER%','%LIMIT%','%UNION%','%COMMENT%'),
  764. array(
  765. $this->parseTable($options['table']),
  766. $this->parseDistinct(isset($options['distinct'])?$options['distinct']:false),
  767. $this->parseField(!empty($options['field'])?$options['field']:'*'),
  768. $this->parseJoin(!empty($options['join'])?$options['join']:''),
  769. $this->parseWhere(!empty($options['where'])?$options['where']:''),
  770. $this->parseGroup(!empty($options['group'])?$options['group']:''),
  771. $this->parseHaving(!empty($options['having'])?$options['having']:''),
  772. $this->parseOrder(!empty($options['order'])?$options['order']:''),
  773. $this->parseLimit(!empty($options['limit'])?$options['limit']:''),
  774. $this->parseUnion(!empty($options['union'])?$options['union']:''),
  775. $this->parseComment(!empty($options['comment'])?$options['comment']:'')
  776. ),$sql);
  777. return $sql;
  778. }
  779. /**
  780. * Get the last query sql statement
  781. * @param string $model Model Name
  782. * @access public
  783. * @return string
  784. */
  785. public function getLastSql($model='') {
  786. return $model?$this->modelSql[$model]:$this->queryStr;
  787. }
  788. /**
  789. * Get the last inserted ID
  790. * @access public
  791. * @return string
  792. */
  793. public function getLastInsID() {
  794. return $this->lastInsID;
  795. }
  796. /**
  797. * Get the most recent error message
  798. * @access public
  799. * @return string
  800. */
  801. public function getError() {
  802. return $this->error;
  803. }
  804. /**
  805. * SQL commands security filtering
  806. * @access public
  807. * @param string $str SQL string
  808. * @return string
  809. */
  810. public function escapeString($str) {
  811. return addslashes($str);
  812. }
  813. /**
  814. * Set the current operational model
  815. * @access public
  816. * @param string $model Model Name
  817. * @return void
  818. */
  819. public function setModel($model){
  820. $this->model = $model;
  821. }
  822. /**
  823. * Destructor
  824. * @access public
  825. */
  826. public function __destruct() {
  827. // Release inquiries
  828. if ($this->queryID){
  829. $this->free();
  830. }
  831. // Close the connection
  832. $this->close();
  833. }
  834. // Close the database Defined by the driver class
  835. public function close(){}
  836. }