<?php// +----------------------------------------------------------------------// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]// +----------------------------------------------------------------------// | Copyright (c) 2006-2014 http://thinkVeVb.com All rights reserved.// +----------------------------------------------------------------------// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )// +----------------------------------------------------------------------// | Author: liu21st <liu21st@gmail.com>// +----------------------------------------------------------------------namespace ThinkDb;use ThinkConfig;use ThinkDebug;use ThinkLog;use PDO;// 各種空間的集成 引入html' target='_blank'>class Lite { // 獲取 數據量 // PDO操作實例 protected $PDOStatement = null; // 獲取 PDO 狀態 // 當前操作所屬的模型名 protected $model = '_think_';// 模型 開始 // 當前SQL指令 protected $queryStr = ''; // 處理完成的 sql 語句 protected $modelSql = array(); // 數組記錄 // 最后插入ID protected $lastInsID = null; // 最后插入的 數據 // 返回或者影響記錄數 protected $numRows = 0; // 影響行數 // 事務指令數 protected $transTimes = 0;// 指定條數 // 錯誤信息 protected $error = ''; // 獲取對應的 錯誤信息 // 數據庫連接ID 支持多個連接 protected $linkID = array();// 支持分布式連接 ID // 當前連接ID protected $_linkID = null; // 當前 執行 數據連接 // 數據庫連接參數配置 protected $config = array( 'type' => '', // 數據庫類型 'hostname' => '127.0.0.1', // 服務器地址 'database' => '', // 數據庫名 'username' => '', // 用戶名 'password' => '', // 密碼 'hostport' => '', // 端口 'dsn' => '', // 另外一種 數據格式 進行 數據庫連接的解析的 'params' => array(), // 數據庫連接參數 其它參數 'charset' => 'utf8', // 數據庫編碼默認采用utf8 'prefix' => '', // 數據庫表前綴 第一次接觸到表 'debug' => false, // 數據庫調試模式 默認關閉 'deploy' => 0, // 數據庫部署方式:0 集中式(單一服務器),1 分布式(主從服務器) 是否可以 'rw_separate' => false, // 數據庫讀寫是否分離 主從式有效 'master_num' => 1, // 讀寫分離后 主服務器數量 'slave_no' => '', // 指定從服務器序號 主從備份 ); // 數據庫表達式 protected $comparison = array('eq'=>'=','neq'=>'<>','gt'=>'>','egt'=>'>=','lt'=>'<','elt'=>'<=','notlike'=>'NOT LIKE','like'=>'LIKE','in'=>'IN','notin'=>'NOT IN');// 簡單的數組翻譯功能 // 查詢表達式 protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%COMMENT%'; // 一個基礎的表達式查詢語句 // 查詢次數 protected $queryTimes = 0;//查詢的 次數,非數據庫執行次數 // 執行次數 protected $executeTimes = 0;// 執行次數 // PDO連接參數 protected $options = array( // PDO 的連接參數 PDO::ATTR_CASE => PDO::CASE_LOWER, // 在沒有看 應該是轉化為小寫 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,// 錯誤異常方式 PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,// PDO::ATTR_STRINGIFY_FETCHES => false, ); // 因為不同數據庫對返回的字段名稱大小寫處理不同,所以PDO提供了PDO::ATTR_CASE設置項(包括PDO::CASE_LOWER,PDO::CASE_NATURAL,PDO::CASE_UPPER),來確定返回的字段名稱的大小寫。 // 通過設置PDO::ATTR_ORACLE_NULLS類型(包括PDO::NULL_NATURAL,PDO::NULL_EmpTY_STRING,PDO::NULL_TO_STRING)來指定數據庫返回的NULL值在php中對應的數值。 /** * 架構函數 讀取數據庫配置信息 * @access public * @param array $config 數據庫配置數組 * 初始化配置選項而已 */ public function __construct($config=''){ if(!empty($config)) { $this->config = array_merge($this->config,$config); if(is_array($this->config['params'])){ $this->options += $this->config['params']; // 這個操作基本上跟array_merge 差不多的 } } } /** * 連接數據庫方法 * @access public */ public function connect($config='',$linkNum=0) { // 連接數據庫 是可以 指定連接 參數的 后面的 0 是為了 主從數據庫設置的 // 說實話,這個我不是很喜歡,但是 他的指定參數 對于臨時調整的數據連接 很有用的 if ( !isset($this->linkID[$linkNum]) ) { // 單例模式, if(empty($config)) $config = $this->config; // 跟三元運算符差不多 try{ if(empty($config['dsn'])) { $config['dsn'] = $this->parseDsn($config);// 獲取dsn 解析 } if(version_compare(PHP_VERSION,'5.3.6','<=')){ //禁用模擬預處理語句 $this->options[PDO::ATTR_EMULATE_PREPARES] = false; // 版本控制 } $this->linkID[$linkNum] = new PDO( $config['dsn'], $config['username'], $config['password'],$this->options);// 普通的PDO 連接參數 }catch (PDOException $e) { E($e->getMessage());// 報錯這個 是靠譜的 } } return $this->linkID[$linkNum]; // 返回連接資源 } /** * 解析pdo連接的dsn信息 * @access public * @param array $config 連接信息 * @return string */ protected function parseDsn($config){}// 這個不是騙人嗎,嘿嘿,么有了 /** * 釋放查詢結果 * @access public */ public function free() { $this->PDOStatement = null; // 清空結果,也就意味著釋放掉了內存 } // 明日繼續 /** * 執行查詢 返回數據集 * @access public * @param string $str sql指令 * @param array $bind 參數綁定 * @return mixed * 一個普通的查詢 */ public function query($str,$bind=array()) { $this->initConnect(false); // 一個多連接配置 if ( !$this->_linkID ) return false; // 如果默認沒有找到當前的正常的連接,那么,這個,就失效了,嘿嘿 $this->queryStr = $str;// 獲取其對應的數據 if(!empty($bind)){// 如果數據不為空 含有綁定的參數 相當于 特殊處理一下參數 了 $that = $this;// $arr = array('Hello' => 'Hi', 'world' => 'earth');// echo strtr('Hello world',$arr); // 居然還可以批量替換,嘿嘿 把字符串 'Hello world' 替換成 'Hi earth': //strtr() 函數轉換字符串中特定的字符。strtr(string,from,to) 應該是跟那個 str_replace 差不多的一個檔次了 $this->queryStr = strtr($this->queryStr,array_map(function($val) use($that){ return '''.$that->escapeString($val).'''; },$bind)); // 進行一個數組遍歷 其中的escapeString 號稱是對綁定參數的一個過濾,但是,其實就是加了 斜線而已了 } //釋放前次的查詢結果 if ( !empty($this->PDOStatement) ) $this->free();// 這個注釋很恰當 $this->queryTimes++;// 次數++ 為什么不這么寫 ++$this->queryTimes;嘿嘿,估計怕看起來不方便吧 N('db_query',1); // 兼容代碼 // 這不是記錄次數 嗎 // 調試開始 $this->debug(true);// 居然還開啟調試 好吧,這個我醉了,默認開始調試不也就是可以了嗎 $this->PDOStatement = $this->_linkID->prepare($str); //從翻譯的角度來說,這個就是個準備,prepare if(false === $this->PDOStatement)// 如果啥都沒有 ,那么 怎么弄呢 E($this->error());// 報錯啊,瞅啥呢? foreach ($bind as $key => $val) { // 有參數,則進行循環 綁定數值 if(is_array($val)){ $this->PDOStatement->bindValue($key, $val[0], $val[1]);// 你的地盤你做主吧兩個方式 }else{ $this->PDOStatement->bindValue($key, $val);// 還是單獨的 key value 靠譜一些 } } $result = $this->PDOStatement->execute();// 這里進行執行,執行后 // 調試結束 $this->debug(false);// 完成了,自動關閉調試,牛叉 if ( false === $result ) {// 沒有結果的話,還要報錯 $this->error(); return false; } else { return $this->getResult();// 否則的話,處理結果集 進行返回 } } /** * 執行語句 * @access public * @param string $str sql指令 * @param array $bind 參數綁定 * @return integer * 這個完全有點重寫的意味呢? */ public function execute($str,$bind=array()) { $this->initConnect(true);// 初始化連接 if ( !$this->_linkID ) return false; // 判讀報錯返回 $this->queryStr = $str;// 查詢傳入執行,這個我倒是,此刻不怎么贊成 if(!empty($bind)){// 針對于綁定的 參數 做 查詢語句的特殊處理 大致應該就是那個 過濾吧 $that = $this; $this->queryStr = strtr($this->queryStr,array_map(function($val) use($that){ return '''.$that->escapeString($val).'''; },$bind)); } //釋放前次的查詢結果 if ( !empty($this->PDOStatement) ) $this->free();// 清空倉庫 +1 然后 準備收貨 $this->executeTimes++; // 執行次數加一 N('db_write',1); // 兼容代碼 // 記錄開始執行時間 $this->debug(true);// 搞了半天,這個是 個調試記錄啊 $this->PDOStatement = $this->_linkID->prepare($str); if(false === $this->PDOStatement) { E($this->error()); } foreach ($bind as $key => $val) { if(is_array($val)){ $this->PDOStatement->bindValue($key, $val[0], $val[1]); }else{ $this->PDOStatement->bindValue($key, $val); } } $result = $this->PDOStatement->execute(); $this->debug(false); // 此處跟上面的一個函數,基本上沒什么大的區別呢 if ( false === $result) { $this->error(); return false; } else { $this->numRows = $this->PDOStatement->rowCount(); // 返回 影響的數據唄 if(preg_match('/^s*(INSERTs+INTO|REPLACEs+INTO)s+/i', $str)) { $this->lastInsID = $this->_linkID->lastInsertId(); // 根據不同的語句 看看是否有ID返回 } return $this->numRows; } }// 返回數據 // 總結: /** * 第一步:基本上先 單例連接 錯誤返回 * 第二步:處理特殊綁定的 參數 有則處理,沒有繼續向下 * 第三步:清空倉庫 等待收貨 * 第四步:記錄開始狀態,進行數據、語句準備 * 第五步:對執行結果審查,合適返回數據,否則報錯異常 */ /** * 啟動事務 * @access public * @return void * 就是干了一個,關閉自動提交的功能 */ public function startTrans() { $this->initConnect(true); if ( !$this->_linkID ) return false; //數據rollback 支持 if ($this->transTimes == 0) { $this->_linkID->beginTransaction(); /* 開始一個事務,關閉自動提交 */ } $this->transTimes++; return ; } // 總結,其它 沒什么了 /** * 用于非自動提交狀態下面的查詢提交 * @access public * @return boolean */ public function commit() { if ($this->transTimes > 0) { $result = $this->_linkID->commit(); // 進行個數據提交,僅此而已了 $this->transTimes = 0; if(!$result){ $this->error(); return false; } } return true; } /** * 事務回滾 * @access public * @return boolean */ public function rollback() { if ($this->transTimes > 0) { $result = $this->_linkID->rollback();// 普通的事務回滾 $this->transTimes = 0; if(!$result){ $this->error(); return false; } } return true; } /** * 獲得所有的查詢數據 * @access private * @return array */ private function getResult() { //返回數據集 $result = $this->PDOStatement->fetchAll(PDO::FETCH_ASSOC);// 獲取數據 $this->numRows = count( $result ); return $result; } /** * 獲得查詢次數 * @access public * @param boolean $execute 是否包含所有查詢 * @return integer */ public function getQueryTimes($execute=false){// 僅僅是個獲取查詢次數 return $execute?$this->queryTimes+$this->executeTimes:$this->queryTimes; } /** * 獲得執行次數 * @access public * @return integer */ public function getExecuteTimes(){ // 獲取執行次數 return $this->executeTimes; } /** * 關閉數據庫 * @access public */ public function close() { // 關閉連接,釋放資源 $this->_linkID = null; } /** * 數據庫錯誤信息 * 并顯示當前的SQL語句 * @access public * @return string */ public function error() { // 返回錯誤信息 if($this->PDOStatement) { $error = $this->PDOStatement->errorInfo(); $this->error = $error[1].':'.$error[2]; }else{ $this->error = ''; } if('' != $this->queryStr){ $this->error .= ' [ SQL語句 ] : '.$this->queryStr; } // 記錄錯誤日志 trace($this->error,'','ERR'); if($this->config['debug']) {// 開啟數據庫調試模式 記錄數據庫錯誤信息日志 E($this->error); }else{ return $this->error; } } /** * 獲取最近一次查詢的sql語句 * @param string $model 模型名 * @access public * @return string */ public function getLastSql($model='') {// 獲取最后一條語句 return $model?$this->modelSql[$model]:$this->queryStr; } /** * 獲取最近插入的ID * @access public * @return string */ public function getLastInsID() { return $this->lastInsID; } /** * 獲取最近的錯誤信息 * @access public * @return string */ public function getError() { return $this->error;// 這種基本的 錯誤信息 進行返回 } /** * SQL指令安全過濾 * @access public * @param string $str SQL字符串 * @return string */ public function escapeString($str) { return addslashes($str); //在每個雙引號(')前添加反斜杠: 這樣干,就預防了 注入了,因為 ' 可以構成 這個 注入的語句 } /** * 設置當前操作模型 * @access public * @param string $model 模型名 * @return void */ public function setModel($model){ $this->model = $model; // 設置模型 臨時設置的這種 } /** * 數據庫調試 記錄當前SQL * @access protected * @param boolean $start 調試開始標記 true 開始 false 結束 */ protected function debug($start) { if($this->config['debug']) {// 開啟數據庫調試模式 if($start) { G('queryStartTime'); // 才看見這個的真面目, 設置時間而已了 }else{ $this->modelSql[$this->model] = $this->queryStr;//記錄語句 //$this->model = '_think_'; // 記錄操作結束時間 G('queryEndTime'); //記錄結束時間 trace($this->queryStr.' [ RunTime:'.G('queryStartTime','queryEndTime').'s ]','','SQL');// 頁面運行記錄 } } } /** * 初始化數據庫連接 * @access protected * @param boolean $master 主服務器 * @return void */ protected function initConnect($master=true) { if(!empty($this->config['deploy'])) // 是否分布式 // 采用分布式數據庫 $this->_linkID = $this->multiConnect($master);// 分布式的 高大上 啊,先連接 主庫 else // 默認單數據庫 if ( !$this->_linkID ) $this->_linkID = $this->connect(); // 創建單數據庫連接 } /** * 連接分布式服務器 * @access protected * @param boolean $master 主服務器 * @return void */ protected function multiConnect($master=false) { // 分布式數據庫配置解析 $_config['username'] = explode(',',$this->config['username']); $_config['password'] = explode(',',$this->config['password']); $_config['hostname'] = explode(',',$this->config['hostname']); $_config['hostport'] = explode(',',$this->config['hostport']); $_config['database'] = explode(',',$this->config['database']); $_config['dsn'] = explode(',',$this->config['dsn']); $_config['charset'] = explode(',',$this->config['charset']); // 多數據庫 解析 // 數據庫讀寫是否分離 if($this->config['rw_separate']){ // 讀寫分離 // 主從式采用讀寫分離 if($master) // 主服務器寫入 $r = floor(mt_rand(0,$this->config['master_num']-1));// 隨機寫入主服務器 else{ if(is_numeric($this->config['slave_no'])) {// 指定服務器讀 // 如果指定 讀取從服務器 $r = $this->config['slave_no']; }else{ // 讀操作連接從服務器 隨機 連接從 服務器 $r = floor(mt_rand($this->config['master_num'],count($_config['hostname'])-1)); // 每次隨機連接的數據庫 } } }else{ // 讀寫不分離 // 讀寫操作不區分服務器 $r = floor(mt_rand(0,count($_config['hostname'])-1)); // 每次隨機連接的數據庫 } $db_config = array( 'username' => isset($_config['username'][$r])?$_config['username'][$r]:$_config['username'][0], 'password' => isset($_config['password'][$r])?$_config['password'][$r]:$_config['password'][0], 'hostname' => isset($_config['hostname'][$r])?$_config['hostname'][$r]:$_config['hostname'][0], 'hostport' => isset($_config['hostport'][$r])?$_config['hostport'][$r]:$_config['hostport'][0], 'database' => isset($_config['database'][$r])?$_config['database'][$r]:$_config['database'][0], 'dsn' => isset($_config['dsn'][$r])?$_config['dsn'][$r]:$_config['dsn'][0], 'charset' => isset($_config['charset'][$r])?$_config['charset'][$r]:$_config['charset'][0], ); return $this->connect($db_config,$r); // 連接指定配置的服務器, 這個主從,看來真的一點技術含量都沒有 // 估計是這想的,會用到主從的人,估計這個事情自己就可以搞定了,哈哈 } /** * 析構方法 * @access public */ public function __destruct() { // 釋放查詢 if ($this->PDOStatement){ $this->free(); // 釋放 內存空間 } // 關閉連接 $this->close(); // 釋放連接資源 }}// 總結:/** * 感覺這類也沒干什么大事,就是各種封裝,并且 弄了主從,完善了默認的 mysql 函數而已了。 */PHP編程
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。
新聞熱點
疑難解答