今天来点基础的设计模式:

如何利用单例模式实现一个数据库中间层

  1. class Db{
  2.   static private $_instance;  //当前数据库连接实例
  3.   static public function getInstance($config){
  4.     if(!isset(self::$_instance)){
  5.       //new 创建实例
  6.       $class = "Think\\DB\\Driver\\".$config['type'];
  7.       self::$_instance = new $class($config);
  8.     }
  9.     return self::$_instance;
  10.   }
  11. }
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006-2014 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. namespace Think\Db;
  13. use Think\Config;
  14. use Think\Debug;
  15. use Think\Log;
  16. use PDO;
  17.  
  18. abstract class Driver {
  19. // PDO操作实例
  20. protected $PDOStatement = null;
  21. // 当前操作所属的模型名
  22. protected $model = '_think_';
  23. // 当前SQL指令
  24. protected $queryStr = '';
  25. protected $modelSql = array();
  26. // 最后插入ID
  27. protected $lastInsID = null;
  28. // 返回或者影响记录数
  29. protected $numRows = 0;
  30. // 事务指令数
  31. protected $transTimes = 0;
  32. // 错误信息
  33. protected $error = '';
  34. // 数据库连接ID 支持多个连接
  35. protected $linkID = array();
  36. // 当前连接ID
  37. protected $_linkID = null;
  38. // 数据库连接参数配置
  39. protected $config = array(
  40. 'type' => '', // 数据库类型
  41. 'hostname' => '127.0.0.1', // 服务器地址
  42. 'database' => '', // 数据库名
  43. 'username' => '', // 用户名
  44. 'password' => '', // 密码
  45. 'hostport' => '', // 端口
  46. 'dsn' => '', //
  47. 'params' => array(), // 数据库连接参数
  48. 'charset' => 'utf8', // 数据库编码默认采用utf8
  49. 'prefix' => '', // 数据库表前缀
  50. 'debug' => false, // 数据库调试模式
  51. 'deploy' => 0, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
  52. 'rw_separate' => false, // 数据库读写是否分离 主从式有效
  53. 'master_num' => 1, // 读写分离后 主服务器数量
  54. 'slave_no' => '', // 指定从服务器序号
  55. 'db_like_fields' => '',
  56. );
  57. // 数据库表达式
  58. 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','not between'=>'NOT BETWEEN','notbetween'=>'NOT BETWEEN');
  59. // 查询表达式
  60. protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%LOCK%%COMMENT%';
  61. // 查询次数
  62. protected $queryTimes = 0;
  63. // 执行次数
  64. protected $executeTimes = 0;
  65. // PDO连接参数
  66. protected $options = array(
  67. PDO::ATTR_CASE => PDO::CASE_LOWER,
  68. PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  69. PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
  70. PDO::ATTR_STRINGIFY_FETCHES => false,
  71. );
  72. protected $bind = array(); // 参数绑定
  73.  
  74. /**
  75. * 架构函数 读取数据库配置信息
  76. * @access public
  77. * @param array $config 数据库配置数组
  78. */
  79. public function __construct($config=''){
  80. if(!empty($config)) {
  81. $this->config = array_merge($this->config,$config);
  82. if(is_array($this->config['params'])){
  83. $this->options = $this->config['params'] + $this->options;
  84. }
  85. }
  86. }
  87.  
  88. /**
  89. * 连接数据库方法
  90. * @access public
  91. */
  92. public function connect($config='',$linkNum=0,$autoConnection=false) {
  93. if ( !isset($this->linkID[$linkNum]) ) {
  94. if(empty($config)) $config = $this->config;
  95. try{
  96. if(empty($config['dsn'])) {
  97. $config['dsn'] = $this->parseDsn($config);
  98. }
  99. if(version_compare(PHP_VERSION,'5.3.6','<=')){
  100. // 禁用模拟预处理语句
  101. $this->options[PDO::ATTR_EMULATE_PREPARES] = false;
  102. }
  103. $this->linkID[$linkNum] = new PDO( $config['dsn'], $config['username'], $config['password'],$this->options);
  104. }catch (\PDOException $e) {
  105. if($autoConnection){
  106. trace($e->getMessage(),'','ERR');
  107. return $this->connect($autoConnection,$linkNum);
  108. }elseif($config['debug']){
  109. E($e->getMessage());
  110. }
  111. }
  112. }
  113. return $this->linkID[$linkNum];
  114. }
  115.  
  116. /**
  117. * 解析pdo连接的dsn信息
  118. * @access public
  119. * @param array $config 连接信息
  120. * @return string
  121. */
  122. protected function parseDsn($config){}
  123.  
  124. /**
  125. * 释放查询结果
  126. * @access public
  127. */
  128. public function free() {
  129. $this->PDOStatement = null;
  130. }
  131.  
  132. /**
  133. * 执行查询 返回数据集
  134. * @access public
  135. * @param string $str sql指令
  136. * @param boolean $fetchSql 不执行只是获取SQL
  137. * @return mixed
  138. */
  139. public function query($str,$fetchSql=false) {
  140. $this->initConnect(false);
  141. if ( !$this->_linkID ) return false;
  142. $this->queryStr = $str;
  143. if(!empty($this->bind)){
  144. $that = $this;
  145. $this->queryStr = strtr($this->queryStr,array_map(function($val) use($that){ return '\''.$that->escapeString($val).'\''; },$this->bind));
  146. }
  147. if($fetchSql){
  148. return $this->queryStr;
  149. }
  150. //释放前次的查询结果
  151. if ( !empty($this->PDOStatement) ) $this->free();
  152. $this->queryTimes++;
  153. N('db_query',1); // 兼容代码
  154. // 调试开始
  155. $this->debug(true);
  156. $this->PDOStatement = $this->_linkID->prepare($str);
  157. if(false === $this->PDOStatement){
  158. $this->error();
  159. return false;
  160. }
  161. foreach ($this->bind as $key => $val) {
  162. if(is_array($val)){
  163. $this->PDOStatement->bindValue($key, $val[0], $val[1]);
  164. }else{
  165. $this->PDOStatement->bindValue($key, $val);
  166. }
  167. }
  168. $this->bind = array();
  169. try{
  170. $result = $this->PDOStatement->execute();
  171. // 调试结束
  172. $this->debug(false);
  173. if ( false === $result ) {
  174. $this->error();
  175. return false;
  176. } else {
  177. return $this->getResult();
  178. }
  179. }catch (\PDOException $e) {
  180. $this->error();
  181. return false;
  182. }
  183. }
  184.  
  185. /**
  186. * 执行语句
  187. * @access public
  188. * @param string $str sql指令
  189. * @param boolean $fetchSql 不执行只是获取SQL
  190. * @return mixed
  191. */
  192. public function execute($str,$fetchSql=false) {
  193. $this->initConnect(true);
  194. if ( !$this->_linkID ) return false;
  195. $this->queryStr = $str;
  196. if(!empty($this->bind)){
  197. $that = $this;
  198. $this->queryStr = strtr($this->queryStr,array_map(function($val) use($that){ return '\''.$that->escapeString($val).'\''; },$this->bind));
  199. }
  200. if($fetchSql){
  201. return $this->queryStr;
  202. }
  203. //释放前次的查询结果
  204. if ( !empty($this->PDOStatement) ) $this->free();
  205. $this->executeTimes++;
  206. N('db_write',1); // 兼容代码
  207. // 记录开始执行时间
  208. $this->debug(true);
  209. $this->PDOStatement = $this->_linkID->prepare($str);
  210. if(false === $this->PDOStatement) {
  211. $this->error();
  212. return false;
  213. }
  214. foreach ($this->bind as $key => $val) {
  215. if(is_array($val)){
  216. $this->PDOStatement->bindValue($key, $val[0], $val[1]);
  217. }else{
  218. $this->PDOStatement->bindValue($key, $val);
  219. }
  220. }
  221. $this->bind = array();
  222. try{
  223. $result = $this->PDOStatement->execute();
  224. // 调试结束
  225. $this->debug(false);
  226. if ( false === $result) {
  227. $this->error();
  228. return false;
  229. } else {
  230. $this->numRows = $this->PDOStatement->rowCount();
  231. if(preg_match("/^\s*(INSERT\s+INTO|REPLACE\s+INTO)\s+/i", $str)) {
  232. $this->lastInsID = $this->_linkID->lastInsertId();
  233. }
  234. return $this->numRows;
  235. }
  236. }catch (\PDOException $e) {
  237. $this->error();
  238. return false;
  239. }
  240. }
  241.  
  242. /**
  243. * 启动事务
  244. * @access public
  245. * @return void
  246. */
  247. public function startTrans() {
  248. $this->initConnect(true);
  249. if ( !$this->_linkID ) return false;
  250. //数据rollback 支持
  251. if ($this->transTimes == 0) {
  252. $this->_linkID->beginTransaction();
  253. }
  254. $this->transTimes++;
  255. return ;
  256. }
  257.  
  258. /**
  259. * 用于非自动提交状态下面的查询提交
  260. * @access public
  261. * @return boolean
  262. */
  263. public function commit() {
  264. if ($this->transTimes > 0) {
  265. $result = $this->_linkID->commit();
  266. $this->transTimes = 0;
  267. if(!$result){
  268. $this->error();
  269. return false;
  270. }
  271. }
  272. return true;
  273. }
  274.  
  275. /**
  276. * 事务回滚
  277. * @access public
  278. * @return boolean
  279. */
  280. public function rollback() {
  281. if ($this->transTimes > 0) {
  282. $result = $this->_linkID->rollback();
  283. $this->transTimes = 0;
  284. if(!$result){
  285. $this->error();
  286. return false;
  287. }
  288. }
  289. return true;
  290. }
  291.  
  292. /**
  293. * 获得所有的查询数据
  294. * @access private
  295. * @return array
  296. */
  297. private function getResult() {
  298. //返回数据集
  299. $result = $this->PDOStatement->fetchAll(PDO::FETCH_ASSOC);
  300. $this->numRows = count( $result );
  301. return $result;
  302. }
  303.  
  304. /**
  305. * 获得查询次数
  306. * @access public
  307. * @param boolean $execute 是否包含所有查询
  308. * @return integer
  309. */
  310. public function getQueryTimes($execute=false){
  311. return $execute?$this->queryTimes+$this->executeTimes:$this->queryTimes;
  312. }
  313.  
  314. /**
  315. * 获得执行次数
  316. * @access public
  317. * @return integer
  318. */
  319. public function getExecuteTimes(){
  320. return $this->executeTimes;
  321. }
  322.  
  323. /**
  324. * 关闭数据库
  325. * @access public
  326. */
  327. public function close() {
  328. $this->_linkID = null;
  329. }
  330.  
  331. /**
  332. * 数据库错误信息
  333. * 并显示当前的SQL语句
  334. * @access public
  335. * @return string
  336. */
  337. public function error() {
  338. if($this->PDOStatement) {
  339. $error = $this->PDOStatement->errorInfo();
  340. $this->error = $error[1].':'.$error[2];
  341. }else{
  342. $this->error = '';
  343. }
  344. if('' != $this->queryStr){
  345. $this->error .= "\n [ SQL语句 ] : ".$this->queryStr;
  346. }
  347. // 记录错误日志
  348. trace($this->error,'','ERR');
  349. if($this->config['debug']) {// 开启数据库调试模式
  350. E($this->error);
  351. }else{
  352. return $this->error;
  353. }
  354. }
  355.  
  356. /**
  357. * 设置锁机制
  358. * @access protected
  359. * @return string
  360. */
  361. protected function parseLock($lock=false) {
  362. return $lock? ' FOR UPDATE ' : '';
  363. }
  364.  
  365. /**
  366. * set分析
  367. * @access protected
  368. * @param array $data
  369. * @return string
  370. */
  371. protected function parseSet($data) {
  372. foreach ($data as $key=>$val){
  373. if(is_array($val) && 'exp' == $val[0]){
  374. $set[] = $this->parseKey($key).'='.$val[1];
  375. }elseif(is_null($val)){
  376. $set[] = $this->parseKey($key).'=NULL';
  377. }elseif(is_scalar($val)) {// 过滤非标量数据
  378. if(0===strpos($val,':') && in_array($val,array_keys($this->bind)) ){
  379. $set[] = $this->parseKey($key).'='.$this->escapeString($val);
  380. }else{
  381. $name = count($this->bind);
  382. $set[] = $this->parseKey($key).'=:'.$name;
  383. $this->bindParam($name,$val);
  384. }
  385. }
  386. }
  387. return ' SET '.implode(',',$set);
  388. }
  389.  
  390. /**
  391. * 参数绑定
  392. * @access protected
  393. * @param string $name 绑定参数名
  394. * @param mixed $value 绑定值
  395. * @return void
  396. */
  397. protected function bindParam($name,$value){
  398. $this->bind[':'.$name] = $value;
  399. }
  400.  
  401. /**
  402. * 字段名分析
  403. * @access protected
  404. * @param string $key
  405. * @return string
  406. */
  407. protected function parseKey(&$key) {
  408. return $key;
  409. }
  410.  
  411. /**
  412. * value分析
  413. * @access protected
  414. * @param mixed $value
  415. * @return string
  416. */
  417. protected function parseValue($value) {
  418. if(is_string($value)) {
  419. $value = strpos($value,':') === 0 && in_array($value,array_keys($this->bind))? $this->escapeString($value) : '\''.$this->escapeString($value).'\'';
  420. }elseif(isset($value[0]) && is_string($value[0]) && strtolower($value[0]) == 'exp'){
  421. $value = $this->escapeString($value[1]);
  422. }elseif(is_array($value)) {
  423. $value = array_map(array($this, 'parseValue'),$value);
  424. }elseif(is_bool($value)){
  425. $value = $value ? '1' : '0';
  426. }elseif(is_null($value)){
  427. $value = 'null';
  428. }
  429. return $value;
  430. }
  431.  
  432. /**
  433. * field分析
  434. * @access protected
  435. * @param mixed $fields
  436. * @return string
  437. */
  438. protected function parseField($fields) {
  439. if(is_string($fields) && '' !== $fields) {
  440. $fields = explode(',',$fields);
  441. }
  442. if(is_array($fields)) {
  443. // 完善数组方式传字段名的支持
  444. // 支持 'field1'=>'field2' 这样的字段别名定义
  445. $array = array();
  446. foreach ($fields as $key=>$field){
  447. if(!is_numeric($key))
  448. $array[] = $this->parseKey($key).' AS '.$this->parseKey($field);
  449. else
  450. $array[] = $this->parseKey($field);
  451. }
  452. $fieldsStr = implode(',', $array);
  453. }else{
  454. $fieldsStr = '*';
  455. }
  456. //TODO 如果是查询全部字段,并且是join的方式,那么就把要查的表加个别名,以免字段被覆盖
  457. return $fieldsStr;
  458. }
  459.  
  460. /**
  461. * table分析
  462. * @access protected
  463. * @param mixed $table
  464. * @return string
  465. */
  466. protected function parseTable($tables) {
  467. if(is_array($tables)) {// 支持别名定义
  468. $array = array();
  469. foreach ($tables as $table=>$alias){
  470. if(!is_numeric($table))
  471. $array[] = $this->parseKey($table).' '.$this->parseKey($alias);
  472. else
  473. $array[] = $this->parseKey($alias);
  474. }
  475. $tables = $array;
  476. }elseif(is_string($tables)){
  477. $tables = explode(',',$tables);
  478. array_walk($tables, array(&$this, 'parseKey'));
  479. }
  480. return implode(',',$tables);
  481. }
  482.  
  483. /**
  484. * where分析
  485. * @access protected
  486. * @param mixed $where
  487. * @return string
  488. */
  489. protected function parseWhere($where) {
  490. $whereStr = '';
  491. if(is_string($where)) {
  492. // 直接使用字符串条件
  493. $whereStr = $where;
  494. }else{ // 使用数组表达式
  495. $operate = isset($where['_logic'])?strtoupper($where['_logic']):'';
  496. if(in_array($operate,array('AND','OR','XOR'))){
  497. // 定义逻辑运算规则 例如 OR XOR AND NOT
  498. $operate = ' '.$operate.' ';
  499. unset($where['_logic']);
  500. }else{
  501. // 默认进行 AND 运算
  502. $operate = ' AND ';
  503. }
  504. foreach ($where as $key=>$val){
  505. if(is_numeric($key)){
  506. $key = '_complex';
  507. }
  508. if(0===strpos($key,'_')) {
  509. // 解析特殊条件表达式
  510. $whereStr .= $this->parseThinkWhere($key,$val);
  511. }else{
  512. // 查询字段的安全过滤
  513. // if(!preg_match('/^[A-Z_\|\&\-.a-z0-9\(\)\,]+$/',trim($key))){
  514. // E(L('_EXPRESS_ERROR_').':'.$key);
  515. // }
  516. // 多条件支持
  517. $multi = is_array($val) && isset($val['_multi']);
  518. $key = trim($key);
  519. if(strpos($key,'|')) { // 支持 name|title|nickname 方式定义查询字段
  520. $array = explode('|',$key);
  521. $str = array();
  522. foreach ($array as $m=>$k){
  523. $v = $multi?$val[$m]:$val;
  524. $str[] = $this->parseWhereItem($this->parseKey($k),$v);
  525. }
  526. $whereStr .= '( '.implode(' OR ',$str).' )';
  527. }elseif(strpos($key,'&')){
  528. $array = explode('&',$key);
  529. $str = array();
  530. foreach ($array as $m=>$k){
  531. $v = $multi?$val[$m]:$val;
  532. $str[] = '('.$this->parseWhereItem($this->parseKey($k),$v).')';
  533. }
  534. $whereStr .= '( '.implode(' AND ',$str).' )';
  535. }else{
  536. $whereStr .= $this->parseWhereItem($this->parseKey($key),$val);
  537. }
  538. }
  539. $whereStr .= $operate;
  540. }
  541. $whereStr = substr($whereStr,0,-strlen($operate));
  542. }
  543. return empty($whereStr)?'':' WHERE '.$whereStr;
  544. }
  545.  
  546. // where子单元分析
  547. protected function parseWhereItem($key,$val) {
  548. $whereStr = '';
  549. if(is_array($val)) {
  550. if(is_string($val[0])) {
  551. $exp = strtolower($val[0]);
  552. if(preg_match('/^(eq|neq|gt|egt|lt|elt)$/',$exp)) { // 比较运算
  553. $whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($val[1]);
  554. }elseif(preg_match('/^(notlike|like)$/',$exp)){// 模糊查找
  555. if(is_array($val[1])) {
  556. $likeLogic = isset($val[2])?strtoupper($val[2]):'OR';
  557. if(in_array($likeLogic,array('AND','OR','XOR'))){
  558. $like = array();
  559. foreach ($val[1] as $item){
  560. $like[] = $key.' '.$this->exp[$exp].' '.$this->parseValue($item);
  561. }
  562. $whereStr .= '('.implode(' '.$likeLogic.' ',$like).')';
  563. }
  564. }else{
  565. $whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($val[1]);
  566. }
  567. }elseif('bind' == $exp ){ // 使用表达式
  568. $whereStr .= $key.' = :'.$val[1];
  569. }elseif('exp' == $exp ){ // 使用表达式
  570. $whereStr .= $key.' '.$val[1];
  571. }elseif(preg_match('/^(notin|not in|in)$/',$exp)){ // IN 运算
  572. if(isset($val[2]) && 'exp'==$val[2]) {
  573. $whereStr .= $key.' '.$this->exp[$exp].' '.$val[1];
  574. }else{
  575. if(is_string($val[1])) {
  576. $val[1] = explode(',',$val[1]);
  577. }
  578. $zone = implode(',',$this->parseValue($val[1]));
  579. $whereStr .= $key.' '.$this->exp[$exp].' ('.$zone.')';
  580. }
  581. }elseif(preg_match('/^(notbetween|not between|between)$/',$exp)){ // BETWEEN运算
  582. $data = is_string($val[1])? explode(',',$val[1]):$val[1];
  583. $whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($data[0]).' AND '.$this->parseValue($data[1]);
  584. }else{
  585. E(L('_EXPRESS_ERROR_').':'.$val[0]);
  586. }
  587. }else {
  588. $count = count($val);
  589. $rule = isset($val[$count-1]) ? (is_array($val[$count-1]) ? strtoupper($val[$count-1][0]) : strtoupper($val[$count-1]) ) : '' ;
  590. if(in_array($rule,array('AND','OR','XOR'))) {
  591. $count = $count -1;
  592. }else{
  593. $rule = 'AND';
  594. }
  595. for($i=0;$i<$count;$i++) {
  596. $data = is_array($val[$i])?$val[$i][1]:$val[$i];
  597. if('exp'==strtolower($val[$i][0])) {
  598. $whereStr .= $key.' '.$data.' '.$rule.' ';
  599. }else{
  600. $whereStr .= $this->parseWhereItem($key,$val[$i]).' '.$rule.' ';
  601. }
  602. }
  603. $whereStr = '( '.substr($whereStr,0,-4).' )';
  604. }
  605. }else {
  606. //对字符串类型字段采用模糊匹配
  607. $likeFields = $this->config['db_like_fields'];
  608. if($likeFields && preg_match('/^('.$likeFields.')$/i',$key)) {
  609. $whereStr .= $key.' LIKE '.$this->parseValue('%'.$val.'%');
  610. }else {
  611. $whereStr .= $key.' = '.$this->parseValue($val);
  612. }
  613. }
  614. return $whereStr;
  615. }
  616.  
  617. /**
  618. * 特殊条件分析
  619. * @access protected
  620. * @param string $key
  621. * @param mixed $val
  622. * @return string
  623. */
  624. protected function parseThinkWhere($key,$val) {
  625. $whereStr = '';
  626. switch($key) {
  627. case '_string':
  628. // 字符串模式查询条件
  629. $whereStr = $val;
  630. break;
  631. case '_complex':
  632. // 复合查询条件
  633. $whereStr = substr($this->parseWhere($val),6);
  634. break;
  635. case '_query':
  636. // 字符串模式查询条件
  637. parse_str($val,$where);
  638. if(isset($where['_logic'])) {
  639. $op = ' '.strtoupper($where['_logic']).' ';
  640. unset($where['_logic']);
  641. }else{
  642. $op = ' AND ';
  643. }
  644. $array = array();
  645. foreach ($where as $field=>$data)
  646. $array[] = $this->parseKey($field).' = '.$this->parseValue($data);
  647. $whereStr = implode($op,$array);
  648. break;
  649. }
  650. return '( '.$whereStr.' )';
  651. }
  652.  
  653. /**
  654. * limit分析
  655. * @access protected
  656. * @param mixed $lmit
  657. * @return string
  658. */
  659. protected function parseLimit($limit) {
  660. return !empty($limit)? ' LIMIT '.$limit.' ':'';
  661. }
  662.  
  663. /**
  664. * join分析
  665. * @access protected
  666. * @param mixed $join
  667. * @return string
  668. */
  669. protected function parseJoin($join) {
  670. $joinStr = '';
  671. if(!empty($join)) {
  672. $joinStr = ' '.implode(' ',$join).' ';
  673. }
  674. return $joinStr;
  675. }
  676.  
  677. /**
  678. * order分析
  679. * @access protected
  680. * @param mixed $order
  681. * @return string
  682. */
  683. protected function parseOrder($order) {
  684. if(is_array($order)) {
  685. $array = array();
  686. foreach ($order as $key=>$val){
  687. if(is_numeric($key)) {
  688. $array[] = $this->parseKey($val);
  689. }else{
  690. $array[] = $this->parseKey($key).' '.$val;
  691. }
  692. }
  693. $order = implode(',',$array);
  694. }
  695. return !empty($order)? ' ORDER BY '.$order:'';
  696. }
  697.  
  698. /**
  699. * group分析
  700. * @access protected
  701. * @param mixed $group
  702. * @return string
  703. */
  704. protected function parseGroup($group) {
  705. return !empty($group)? ' GROUP BY '.$group:'';
  706. }
  707.  
  708. /**
  709. * having分析
  710. * @access protected
  711. * @param string $having
  712. * @return string
  713. */
  714. protected function parseHaving($having) {
  715. return !empty($having)? ' HAVING '.$having:'';
  716. }
  717.  
  718. /**
  719. * comment分析
  720. * @access protected
  721. * @param string $comment
  722. * @return string
  723. */
  724. protected function parseComment($comment) {
  725. return !empty($comment)? ' /* '.$comment.' */':'';
  726. }
  727.  
  728. /**
  729. * distinct分析
  730. * @access protected
  731. * @param mixed $distinct
  732. * @return string
  733. */
  734. protected function parseDistinct($distinct) {
  735. return !empty($distinct)? ' DISTINCT ' :'';
  736. }
  737.  
  738. /**
  739. * union分析
  740. * @access protected
  741. * @param mixed $union
  742. * @return string
  743. */
  744. protected function parseUnion($union) {
  745. if(empty($union)) return '';
  746. if(isset($union['_all'])) {
  747. $str = 'UNION ALL ';
  748. unset($union['_all']);
  749. }else{
  750. $str = 'UNION ';
  751. }
  752. foreach ($union as $u){
  753. $sql[] = $str.(is_array($u)?$this->buildSelectSql($u):$u);
  754. }
  755. return implode(' ',$sql);
  756. }
  757.  
  758. /**
  759. * 参数绑定分析
  760. * @access protected
  761. * @param array $bind
  762. * @return array
  763. */
  764. protected function parseBind($bind){
  765. $this->bind = array_merge($this->bind,$bind);
  766. }
  767.  
  768. /**
  769. * index分析,可在操作链中指定需要强制使用的索引
  770. * @access protected
  771. * @param mixed $index
  772. * @return string
  773. */
  774. protected function parseForce($index) {
  775. if(empty($index)) return '';
  776. if(is_array($index)) $index = join(",", $index);
  777. return sprintf(" FORCE INDEX ( %s ) ", $index);
  778. }
  779.  
  780. /**
  781. * ON DUPLICATE KEY UPDATE 分析
  782. * @access protected
  783. * @param mixed $duplicate
  784. * @return string
  785. */
  786. protected function parseDuplicate($duplicate){
  787. return '';
  788. }
  789.  
  790. /**
  791. * 插入记录
  792. * @access public
  793. * @param mixed $data 数据
  794. * @param array $options 参数表达式
  795. * @param boolean $replace 是否replace
  796. * @return false | integer
  797. */
  798. public function insert($data,$options=array(),$replace=false) {
  799. $values = $fields = array();
  800. $this->model = $options['model'];
  801. $this->parseBind(!empty($options['bind'])?$options['bind']:array());
  802. foreach ($data as $key=>$val){
  803. if(is_array($val) && 'exp' == $val[0]){
  804. $fields[] = $this->parseKey($key);
  805. $values[] = $val[1];
  806. }elseif(is_null($val)){
  807. $fields[] = $this->parseKey($key);
  808. $values[] = 'NULL';
  809. }elseif(is_scalar($val)) { // 过滤非标量数据
  810. $fields[] = $this->parseKey($key);
  811. if(0===strpos($val,':') && in_array($val,array_keys($this->bind))){
  812. $values[] = $this->parseValue($val);
  813. }else{
  814. $name = count($this->bind);
  815. $values[] = ':'.$name;
  816. $this->bindParam($name,$val);
  817. }
  818. }
  819. }
  820. // 兼容数字传入方式
  821. $replace= (is_numeric($replace) && $replace>0)?true:$replace;
  822. $sql = (true===$replace?'REPLACE':'INSERT').' INTO '.$this->parseTable($options['table']).' ('.implode(',', $fields).') VALUES ('.implode(',', $values).')'.$this->parseDuplicate($replace);
  823. $sql .= $this->parseComment(!empty($options['comment'])?$options['comment']:'');
  824. return $this->execute($sql,!empty($options['fetch_sql']) ? true : false);
  825. }
  826.  
  827. /**
  828. * 批量插入记录
  829. * @access public
  830. * @param mixed $dataSet 数据集
  831. * @param array $options 参数表达式
  832. * @param boolean $replace 是否replace
  833. * @return false | integer
  834. */
  835. public function insertAll($dataSet,$options=array(),$replace=false) {
  836. $values = array();
  837. $this->model = $options['model'];
  838. if(!is_array($dataSet[0])) return false;
  839. $this->parseBind(!empty($options['bind'])?$options['bind']:array());
  840. $fields = array_map(array($this,'parseKey'),array_keys($dataSet[0]));
  841. foreach ($dataSet as $data){
  842. $value = array();
  843. foreach ($data as $key=>$val){
  844. if(is_array($val) && 'exp' == $val[0]){
  845. $value[] = $val[1];
  846. }elseif(is_null($val)){
  847. $value[] = 'NULL';
  848. }elseif(is_scalar($val)){
  849. if(0===strpos($val,':') && in_array($val,array_keys($this->bind))){
  850. $value[] = $this->parseValue($val);
  851. }else{
  852. $name = count($this->bind);
  853. $value[] = ':'.$name;
  854. $this->bindParam($name,$val);
  855. }
  856. }
  857. }
  858. $values[] = 'SELECT '.implode(',', $value);
  859. }
  860. $sql = 'INSERT INTO '.$this->parseTable($options['table']).' ('.implode(',', $fields).') '.implode(' UNION ALL ',$values);
  861. $sql .= $this->parseComment(!empty($options['comment'])?$options['comment']:'');
  862. return $this->execute($sql,!empty($options['fetch_sql']) ? true : false);
  863. }
  864.  
  865. /**
  866. * 通过Select方式插入记录
  867. * @access public
  868. * @param string $fields 要插入的数据表字段名
  869. * @param string $table 要插入的数据表名
  870. * @param array $option 查询数据参数
  871. * @return false | integer
  872. */
  873. public function selectInsert($fields,$table,$options=array()) {
  874. $this->model = $options['model'];
  875. $this->parseBind(!empty($options['bind'])?$options['bind']:array());
  876. if(is_string($fields)) $fields = explode(',',$fields);
  877. array_walk($fields, array($this, 'parseKey'));
  878. $sql = 'INSERT INTO '.$this->parseTable($table).' ('.implode(',', $fields).') ';
  879. $sql .= $this->buildSelectSql($options);
  880. return $this->execute($sql,!empty($options['fetch_sql']) ? true : false);
  881. }
  882.  
  883. /**
  884. * 更新记录
  885. * @access public
  886. * @param mixed $data 数据
  887. * @param array $options 表达式
  888. * @return false | integer
  889. */
  890. public function update($data,$options) {
  891. $this->model = $options['model'];
  892. $this->parseBind(!empty($options['bind'])?$options['bind']:array());
  893. $table = $this->parseTable($options['table']);
  894. $sql = 'UPDATE ' . $table . $this->parseSet($data);
  895. if(strpos($table,',')){// 多表更新支持JOIN操作
  896. $sql .= $this->parseJoin(!empty($options['join'])?$options['join']:'');
  897. }
  898. $sql .= $this->parseWhere(!empty($options['where'])?$options['where']:'');
  899. if(!strpos($table,',')){
  900. // 单表更新支持order和lmit
  901. $sql .= $this->parseOrder(!empty($options['order'])?$options['order']:'')
  902. .$this->parseLimit(!empty($options['limit'])?$options['limit']:'');
  903. }
  904. $sql .= $this->parseComment(!empty($options['comment'])?$options['comment']:'');
  905. return $this->execute($sql,!empty($options['fetch_sql']) ? true : false);
  906. }
  907.  
  908. /**
  909. * 删除记录
  910. * @access public
  911. * @param array $options 表达式
  912. * @return false | integer
  913. */
  914. public function delete($options=array()) {
  915. $this->model = $options['model'];
  916. $this->parseBind(!empty($options['bind'])?$options['bind']:array());
  917. $table = $this->parseTable($options['table']);
  918. $sql = 'DELETE FROM '.$table;
  919. if(strpos($table,',')){// 多表删除支持USING和JOIN操作
  920. if(!empty($options['using'])){
  921. $sql .= ' USING '.$this->parseTable($options['using']).' ';
  922. }
  923. $sql .= $this->parseJoin(!empty($options['join'])?$options['join']:'');
  924. }
  925. $sql .= $this->parseWhere(!empty($options['where'])?$options['where']:'');
  926. if(!strpos($table,',')){
  927. // 单表删除支持order和limit
  928. $sql .= $this->parseOrder(!empty($options['order'])?$options['order']:'')
  929. .$this->parseLimit(!empty($options['limit'])?$options['limit']:'');
  930. }
  931. $sql .= $this->parseComment(!empty($options['comment'])?$options['comment']:'');
  932. return $this->execute($sql,!empty($options['fetch_sql']) ? true : false);
  933. }
  934.  
  935. /**
  936. * 查找记录
  937. * @access public
  938. * @param array $options 表达式
  939. * @return mixed
  940. */
  941. public function select($options=array()) {
  942. $this->model = $options['model'];
  943. $this->parseBind(!empty($options['bind'])?$options['bind']:array());
  944. $sql = $this->buildSelectSql($options);
  945. $result = $this->query($sql,!empty($options['fetch_sql']) ? true : false);
  946. return $result;
  947. }
  948.  
  949. /**
  950. * 生成查询SQL
  951. * @access public
  952. * @param array $options 表达式
  953. * @return string
  954. */
  955. public function buildSelectSql($options=array()) {
  956. if(isset($options['page'])) {
  957. // 根据页数计算limit
  958. list($page,$listRows) = $options['page'];
  959. $page = $page>0 ? $page : 1;
  960. $listRows= $listRows>0 ? $listRows : (is_numeric($options['limit'])?$options['limit']:20);
  961. $offset = $listRows*($page-1);
  962. $options['limit'] = $offset.','.$listRows;
  963. }
  964. $sql = $this->parseSql($this->selectSql,$options);
  965. return $sql;
  966. }
  967.  
  968. /**
  969. * 替换SQL语句中表达式
  970. * @access public
  971. * @param array $options 表达式
  972. * @return string
  973. */
  974. public function parseSql($sql,$options=array()){
  975. $sql = str_replace(
  976. array('%TABLE%','%DISTINCT%','%FIELD%','%JOIN%','%WHERE%','%GROUP%','%HAVING%','%ORDER%','%LIMIT%','%UNION%','%LOCK%','%COMMENT%','%FORCE%'),
  977. array(
  978. $this->parseTable($options['table']),
  979. $this->parseDistinct(isset($options['distinct'])?$options['distinct']:false),
  980. $this->parseField(!empty($options['field'])?$options['field']:'*'),
  981. $this->parseJoin(!empty($options['join'])?$options['join']:''),
  982. $this->parseWhere(!empty($options['where'])?$options['where']:''),
  983. $this->parseGroup(!empty($options['group'])?$options['group']:''),
  984. $this->parseHaving(!empty($options['having'])?$options['having']:''),
  985. $this->parseOrder(!empty($options['order'])?$options['order']:''),
  986. $this->parseLimit(!empty($options['limit'])?$options['limit']:''),
  987. $this->parseUnion(!empty($options['union'])?$options['union']:''),
  988. $this->parseLock(isset($options['lock'])?$options['lock']:false),
  989. $this->parseComment(!empty($options['comment'])?$options['comment']:''),
  990. $this->parseForce(!empty($options['force'])?$options['force']:'')
  991. ),$sql);
  992. return $sql;
  993. }
  994.  
  995. /**
  996. * 获取最近一次查询的sql语句
  997. * @param string $model 模型名
  998. * @access public
  999. * @return string
  1000. */
  1001. public function getLastSql($model='') {
  1002. return $model?$this->modelSql[$model]:$this->queryStr;
  1003. }
  1004.  
  1005. /**
  1006. * 获取最近插入的ID
  1007. * @access public
  1008. * @return string
  1009. */
  1010. public function getLastInsID() {
  1011. return $this->lastInsID;
  1012. }
  1013.  
  1014. /**
  1015. * 获取最近的错误信息
  1016. * @access public
  1017. * @return string
  1018. */
  1019. public function getError() {
  1020. return $this->error;
  1021. }
  1022.  
  1023. /**
  1024. * SQL指令安全过滤
  1025. * @access public
  1026. * @param string $str SQL字符串
  1027. * @return string
  1028. */
  1029. public function escapeString($str) {
  1030. return addslashes($str);
  1031. }
  1032.  
  1033. /**
  1034. * 设置当前操作模型
  1035. * @access public
  1036. * @param string $model 模型名
  1037. * @return void
  1038. */
  1039. public function setModel($model){
  1040. $this->model = $model;
  1041. }
  1042.  
  1043. /**
  1044. * 数据库调试 记录当前SQL
  1045. * @access protected
  1046. * @param boolean $start 调试开始标记 true 开始 false 结束
  1047. */
  1048. protected function debug($start) {
  1049. if($this->config['debug']) {// 开启数据库调试模式
  1050. if($start) {
  1051. G('queryStartTime');
  1052. }else{
  1053. $this->modelSql[$this->model] = $this->queryStr;
  1054. //$this->model = '_think_';
  1055. // 记录操作结束时间
  1056. G('queryEndTime');
  1057. trace($this->queryStr.' [ RunTime:'.G('queryStartTime','queryEndTime').'s ]','','SQL');
  1058. }
  1059. }
  1060. }
  1061.  
  1062. /**
  1063. * 初始化数据库连接
  1064. * @access protected
  1065. * @param boolean $master 主服务器
  1066. * @return void
  1067. */
  1068. protected function initConnect($master=true) {
  1069. if(!empty($this->config['deploy']))
  1070. // 采用分布式数据库
  1071. $this->_linkID = $this->multiConnect($master);
  1072. else
  1073. // 默认单数据库
  1074. if ( !$this->_linkID ) $this->_linkID = $this->connect();
  1075. }
  1076.  
  1077. /**
  1078. * 连接分布式服务器
  1079. * @access protected
  1080. * @param boolean $master 主服务器
  1081. * @return void
  1082. */
  1083. protected function multiConnect($master=false) {
  1084. // 分布式数据库配置解析
  1085. $_config['username'] = explode(',',$this->config['username']);
  1086. $_config['password'] = explode(',',$this->config['password']);
  1087. $_config['hostname'] = explode(',',$this->config['hostname']);
  1088. $_config['hostport'] = explode(',',$this->config['hostport']);
  1089. $_config['database'] = explode(',',$this->config['database']);
  1090. $_config['dsn'] = explode(',',$this->config['dsn']);
  1091. $_config['charset'] = explode(',',$this->config['charset']);
  1092.  
  1093. $m = floor(mt_rand(0,$this->config['master_num']-1));
  1094. // 数据库读写是否分离
  1095. if($this->config['rw_separate']){
  1096. // 主从式采用读写分离
  1097. if($master)
  1098. // 主服务器写入
  1099. $r = $m;
  1100. else{
  1101. if(is_numeric($this->config['slave_no'])) {// 指定服务器读
  1102. $r = $this->config['slave_no'];
  1103. }else{
  1104. // 读操作连接从服务器
  1105. $r = floor(mt_rand($this->config['master_num'],count($_config['hostname'])-1)); // 每次随机连接的数据库
  1106. }
  1107. }
  1108. }else{
  1109. // 读写操作不区分服务器
  1110. $r = floor(mt_rand(0,count($_config['hostname'])-1)); // 每次随机连接的数据库
  1111. }
  1112.  
  1113. if($m != $r ){
  1114. $db_master = array(
  1115. 'username' => isset($_config['username'][$m])?$_config['username'][$m]:$_config['username'][0],
  1116. 'password' => isset($_config['password'][$m])?$_config['password'][$m]:$_config['password'][0],
  1117. 'hostname' => isset($_config['hostname'][$m])?$_config['hostname'][$m]:$_config['hostname'][0],
  1118. 'hostport' => isset($_config['hostport'][$m])?$_config['hostport'][$m]:$_config['hostport'][0],
  1119. 'database' => isset($_config['database'][$m])?$_config['database'][$m]:$_config['database'][0],
  1120. 'dsn' => isset($_config['dsn'][$m])?$_config['dsn'][$m]:$_config['dsn'][0],
  1121. 'charset' => isset($_config['charset'][$m])?$_config['charset'][$m]:$_config['charset'][0],
  1122. );
  1123. }
  1124. $db_config = array(
  1125. 'username' => isset($_config['username'][$r])?$_config['username'][$r]:$_config['username'][0],
  1126. 'password' => isset($_config['password'][$r])?$_config['password'][$r]:$_config['password'][0],
  1127. 'hostname' => isset($_config['hostname'][$r])?$_config['hostname'][$r]:$_config['hostname'][0],
  1128. 'hostport' => isset($_config['hostport'][$r])?$_config['hostport'][$r]:$_config['hostport'][0],
  1129. 'database' => isset($_config['database'][$r])?$_config['database'][$r]:$_config['database'][0],
  1130. 'dsn' => isset($_config['dsn'][$r])?$_config['dsn'][$r]:$_config['dsn'][0],
  1131. 'charset' => isset($_config['charset'][$r])?$_config['charset'][$r]:$_config['charset'][0],
  1132. );
  1133. return $this->connect($db_config,$r,$r == $m ? false : $db_master);
  1134. }
  1135.  
  1136. /**
  1137. * 析构方法
  1138. * @access public
  1139. */
  1140. public function __destruct() {
  1141. // 释放查询
  1142. if ($this->PDOStatement){
  1143. $this->free();
  1144. }
  1145. // 关闭连接
  1146. $this->close();
  1147. }
  1148. }

  

PHP单例模式编写的更多相关文章

  1. PHP基于单例模式编写PDO类的方法

    一.单例模式简介 简单的说,一个对象(在学习设计模式之前,需要比较了解面向对象思想)只负责一个特定的任务: 二.为什么要使用PHP单例模式? 1.php的应用主要在于数据库应用, 所以一个应用中会存在 ...

  2. 设计模式之 -- 单例模式(Singleton)

    单例模式是一种常用的软件设计模式,通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问. 使用说明 1.使用时机  在某些系统中某些对象最多只能存在一个,例如Windows中只能打开一个 ...

  3. Spring IoC介绍与Bean的使用

    1. 介绍 IoC   IoC-Inversion of Control,即"控制反转",它不是什么技术,而是一种设计思想.在 Java 开发中, IoC意味着将设计好的对象交给容 ...

  4. 第一个NHibernateDemo

    什么是Nhibernate,Nhibernate是一个面向.Net环境的对 象/关系数据库映射工具.(ORM) 1.搭建项目基本框架: (1)创建MVC项目,命名为NHShop.Web. (2)依次分 ...

  5. Java上机试题1

    1. 有一串字符串String s = "ababab", 这个字符串可以看做由3个"ab"构成,即n=3, L = "ab", s = n ...

  6. C++Singleton的DCLP(双重锁)实现以及性能测评

      本文系原创,转载请注明:http://www.cnblogs.com/inevermore/p/4014577.html   根据维基百科,对单例模式的描述是: 确保一个类只有一个实例,并提供对该 ...

  7. java面试宝典第三弹

    Http和Https的区别 超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之 ...

  8. 不能继承于QObject的类就一定不能使用信号槽?(用一个代理类进行发射就行了)

    首先不能继承QObject的情况在开发中遇到得并不多,笔者在一年多的Qt项目开发中只遇到两三次.而且都是因为引进了第三方库导致编译过程中报错. 要想解决这个问题其实不难,因为笔者遇到的问题都是想定义一 ...

  9. Java 代码编写单例模式总结

    手写一个单例模式是 Java 面试中常见的问题,很多时候我们更偏向于简单的写一个饿汉或饱汉模式,深入研究的甚少,这里列举三种实现方式,并对各自的优缺进行分析. 1. 饿汉式 public class ...

随机推荐

  1. Linux读取文件路径问题

    问题是这样的: 首先终端上有当前路径显示,我有个可执行程序代码是这样的: FILE fp  = fopen(filename, "rb"); if(fp == NULL)     ...

  2. 大量客户反映wordpress的网站打开巨慢,经分析发现,这些网站大都使用了google的字体服务,由于最近google的服务已经被大陆屏蔽,所以wordpress的网站打开时,会卡在字体加载上。

     一会你安装完wp,发现打开巨卡的话,看看这个帖子:http://bbs.myhostcn.com/thread-1026-1-1.html最近一段时间,大量客户反映wordpress的网站打开巨慢, ...

  3. 【HDU 2586 How far away?】LCA问题 Tarjan算法

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 题意:给出一棵n个节点的无根树,每条边有各自的权值.给出m个查询,对于每条查询返回节点u到v的最 ...

  4. VMware 虚拟机的网络连接方式详解

         VMWare提供了三种工作模式,它们是bridged(桥接模式).NAT(网络地址转换模式)和host-only(主机模式).要想在网络管理和维护中合理应用它们,你就应该先了解一下这三种工作 ...

  5. 【剑指Offer学习】【面试题60:把二叉树打印出多行】

    题目:从上到下按层打印二叉树,同一层的结点按从左到右的顺序打印,每一层打印一行. 解题思路 用一个队列来保存将要打印的结点.为了把二叉树的每一行单独打印到一行里,我们须要两个变量:一个变量表示在当前的 ...

  6. socketpair的使用

    socketpair函数概要例如以下:#include <sys/types.h>#include <sys/socket.h>int socketpair(int domai ...

  7. LR实战之Discuz开源论坛——登录脚本

    脚本业务流:访问Discuz论坛首页——登录论坛——退出论坛.本次使用LoadRunner11版本. 一.录制脚本注意 1.确保Discuz论坛能在服务器运行正常. 2.录制前先试访问Discuz论坛 ...

  8. 【进制问题】【HDU2056】A + B Again

    A + B Again Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tota ...

  9. Godaddy主机从购买到开通的详细图文教程(2013年)

    http://bbs.zhujiusa.com/thread-10-1-1.html Godaddy主机从购买到开通的详细图文教程(2013年最新) Godaddy是全球域名注册商中的NO.1,同时也 ...

  10. Android布局控件

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout ...