日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > php >内容正文

php

从PHP5到PHP7自我封装MongoDB以及平滑升级

發布時間:2024/9/20 php 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从PHP5到PHP7自我封装MongoDB以及平滑升级 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一.序言

使用PHP+MongoDB的企業級用戶很多,因為MongoDB對非結構化數據的存儲很方便。在PHP5及以前,官方提供了兩個擴展,Mongo和MongoDB,其中Mongo是對以MongoClient等幾個核心類為基礎的類群進行操作,封裝得很方便,所以基本上都會選擇Mongo擴展,詳情請見官方手冊:

http://php.net/manual/en/class.mongoclient.php
  • 1

但是隨著PHP5升級到PHP7,官方不再支持Mongo擴展,只支持MongoDB,而PHP7的性能提升巨大,讓人無法割舍,所以怎么把Mongo替換成MongoDB成為了一個亟待解決的問題。MongoDB引入了命名空間,但是功能封裝非常差,如果非要用原生的擴展,幾乎意味著寫原生的Mongo語句。這種想法很違背ORM簡化dbIO操作帶來的語法問題而專注邏輯優化的思路。詳情也可參見官方手冊;

http://php.net/manual/en/class.mongodb-driver-manager.php
  • 1

在這種情況之下,MongoDB官方忍不住了,為了方便使用,增加市場占有率,推出了基于MongoDB擴展的庫,詳情參見:

https://github.com/mongodb/mongo-php-library
  • 1

實際上在我們使用的過程中,總是希望能夠實現盡可能的解耦,于是分層清晰變得尤為重要。由于官方的庫并不能實現筆者分離和特定的功能需要,于是筆者自己造了一次輪子。

二.自我封裝的MongoDBClient類

1.構造函數
筆者希望構造函數能夠有兩種方式,一種以單例模式去實現對Model層繼承傳參構造,另一種是簡單地直接構造,以實現代碼的充分復用和封裝類的廣泛適用。

public $_client; public $_manager; public $_db; public $_collection;public function __construct(){$config=$this->getDbConnection();if(!empty($config['server']) && !empty($config['db'])){$uri=$config['server']."/".$config['db'];if(isset($config['urioptions'])){$urioptions=$config['urioptions'];}else{$urioptions=array();}if(isset($config['driveroptions'])){$driveroptions=$config['driveroptions'];}else{$driveroptions=array();}$this->setClient($uri,$urioptions,$driveroptions);$this->setDatabase($config['db']);}$collectionName=$this->collectionName();if($this->getDatabase()){$this->setCollection($collectionName);}}public function collectionName(){return '';}public function getDbConnection(){return array();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

以上為單例模式的構造方法,顯然定義了兩個沒有意義的獲取參數的函數,實際上初始化的入口應該在繼承的子類中完成重寫。每一個set函數,都會把實例傳給該對象的屬性以保存并在后續中調用。

public function initInstance($uri,$db,$collectionName){// $config = $this->getMongoConfig();// $tempStr='mongodb://'.$config['username'].':'.$config['password'].'@'.$config['host'].'/'.$config['db'];// $mongodbclient=new MongoDBClient();$this->setClient($uri);$this->setDatabase($db);$this->setCollection($collectionName);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

這里為簡單地直接初始化。而構造函數的設計決定了兩者并不沖突,至于為何要設計兩種初始化方法,是出于對清晰分層的更好支持的原因

2.filter過濾器的構造
從Mongodb官方的原生到php官方的擴展到Mongodb的依賴庫,對于filter的構建方法非常粗暴,就是讓人去直接寫Mongodb的filter的原生語句,這與ORM簡化語法耦合的思路大相徑庭。為了方便別人使用復雜的過濾器,筆者對過濾器進行了簡化的構造。具體思路是把一個語義化的過濾器看作一個算式,一組條件看作一個數,連接符則當作運算符,通過中綴表達式轉后綴表達式實現去括號,然后再執行后綴表達式,實現語義化的連接符的語法化,從而簡化了業務層的開發者的成本。詳情如下:

public function filterConstructor($key,$operator,$value,$connector=array()){$filter=array();$subfilter=array();switch ($operator) {case '=':$subfilter=array($key=>$value);break;case '>':$subfilter=array($key=>array('$gt'=>$value));break;case '>=':$subfilter=array($key=>array('$gte'=>$value));break;case '<':$subfilter=array($key=>array('$lt'=>$value));break;case '<=':$subfilter=array($key=>array('$lte'=>$value));break;case '!=':$subfilter=array($key=>array('$ne'=>$value));break;default:die();break;}$filter=array_merge($filter,$subfilter);return $filter;}/** construct a easy-and filter with double arrays via key-value input* @param (Array)$trible1 (Array)$trible2* @return an array of mongo-dialect filter* @author wangyang*/public function andFilterConstructor($trible1,$trible2){$ret1=$this->filterConstructor($trible1[0],$trible1[1],$trible1[2]);$ret2=$this->filterConstructor($trible2[0],$trible2[1],$trible2[2]);array_merge($ret1,$ret2);return $ret1;}/** construct a easy-or filter with double arrays via key-value input* @param (Array)$trible1 (Array)$trible2* @return an array of mongo-dialect filter* @author wangyang*/public function orFilterConstructor($trible1,$trible2){$ret1=$this->filterConstructor($trible1[0],$trible1[1],$trible1[2]);$ret2=$this->filterConstructor($trible2[0],$trible2[1],$trible2[2]);$ret=array('$or'=>array());array_push($ret['$or'],$ret1);array_push($ret['$or'],$ret2);return $ret;}/** construct a easy-and filter with double filters* @param (Array)$query1 (Array)$query2* @return an array of mongo-dialect filter* @author wangyang*/public function onlyAndFilterConstructor($query1,$query2){$query1=array_merge_recursive($query1,$query2);return $query1;}/** construct a easy-or filter with double filters* @param (Array)$query1 (Array)$query2* @return an array of mongo-dialect filter* @author wangyang*/public function onlyOrFilterConstructor($query1,$query2){$query=array('$or'=>array());array_push($query['$or'],$query1);array_push($query['$or'],$query2);return $query;}/** resolve the complicated connectors set filter* @param (Array)$query e.g. array(filterarray1(),$connector,filterarray2()) * e.g. array(arr1(),'or','(',arr2(),'and',arr3(),')')* @return an array of mongo-dialect filter* @author wangyang*/public function queryFilterConstructor($query){$priority=array('('=>3,'and'=>2,'or'=>2,')'=>1);$stack1=array();$stack2=array();//transfer nifix expression to postfix expressionforeach ($query as $key => $value) {if(is_array($value)){array_push($stack2,$value);}elseif($value=='('||empty($stack1)){array_push($stack1,$value);}elseif($value==')') {while(($top=array_pop($stack1))!=='('){array_push($stack2,$top);}}elseif(end($stack1)=='('){array_push($stack1,$value);}else{while($priority[$value]<$priority[end($stack1)]){$top=array_pop($stack1);array_push($stack2,$top);}array_push($stack1,$value);} }while(!empty($stack1)){$top=array_pop($stack1);array_push($stack2,$top);}foreach ($stack2 as $key => $value) {if(is_array($value)){$stack2[$key]=$this->filterConstructor($value[0],$value[1],$value[2]);}}//compute the postfix expressionforeach ($stack2 as $key => $value) {if(is_array($value)){array_push($stack1,$value);}else{$top=array_pop($stack1);$subtop=array_pop($stack1);if($value=='and'){$ret=$this->onlyAndFilterConstructor($top,$subtop);array_push($stack1,$ret);}elseif($value=='or'){$ret=$this->onlyOrFilterConstructor($top,$subtop);array_push($stack1,$ret);}else{die('undefined connector');}}}$ret=array_pop($stack1);return $ret;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139

在處理的時候用到了棧的思想,在PHP中使用數組進行代替,實際上,PHP的數組函數還是相當契合棧的思路的,比如插入array_push(),刪除頂部元素array_pop(),而在轉逆波蘭式的過程中,完成對最基礎的語句的拼裝,后面的復雜語句通過迭代來實現。
比如要實現{“likes”: {$gt:50}, $or: [{“by”: “菜鳥教程”},{“title”: “MongoDB 教程”}]}這樣一句查詢過濾器,我們就可以用 [[‘likes’,’>’,’50’],’or’,’(‘,[‘by’,’=’,’菜鳥教程’],’and’,[‘title’,’=’,’MongoDB教程’],’)’]來代替了。這大概是我在這個類里最得意的部分了。

3.從數據庫到聚合到具體文檔的CURD
這個就直接上demo吧~
值得注意的是官方的find()返回為一個cursor,通過foreach遍歷輸出的結果卻是一組documents,說明其實官方的對象設計得很不友好

require 'MongodbExtension.php'; $mongo=new MongoDBClient(); $mongo->setClient("mongodb://127.0.0.1:27017");function DataBase($mongo){//列出所有數據庫名$databases=$mongo->listDatabases();//創建數據庫,獲得數據庫實例$database=$mongo->createDatabase('BUPT');//刪除數據庫$mongo->dropDatabase('BUPT');//選擇數據庫,獲得數據庫實例$database=$mongo->selectDatabase('wangyang');} function Collection($mongo){//列出所有集合$collections=$mongo->listCollections();//創建集合,獲得集合實例$collection=$mongo->createCollection('BUPT');//刪除集合$mongo->dropCollection('BUPT');//選擇集合,獲得集合實例$collection=$mongo->selectCollection('test');} function DocumentInsert($mongo){//插入一條數據$insert=array('name'=>'BUPT');$mongo->collectionInsertOne($insert);//插入多條數據$inserts=array(array('name'=>'BUPT'),array('by'=>'wangyang'));$mongo->collectionInsertMany($inserts);} function DocumentDelete($mongo){//簡單的過濾器設置$filter=$mongo->filterConstructor('name','=','BUPT');//復雜的過濾器$filter=$mongo->queryFilterConstructor(array(array('by','=','me'),'or',array('title','=','BUPT')));//刪除很多條,返回刪了多少條$deletenum=$mongo->collectionDeleteMany($filter);//刪除一條$mongo->collectionDeleteOne($filter);} function DocumentUpdate($mongo){//簡單的過濾器設置$filter=$mongo->filterConstructor('name','=','BUPT');//復雜的過濾器$filter=$mongo->queryFilterConstructor(array(array('by','=','me'),'or',array('title','=','BUPT')));//更新后的鍵值對$update=$mongo->updateConstructor('title','THU');//更新一條$mongo->collectionUpdateOne($filter,$update);//更新很多條$mongo->collectionUpdateMany($filter,$update);} function DocumentFind($mongo){//簡單的過濾器設置$filter=$mongo->filterConstructor('name','=','BUPT');//復雜的過濾器$filter=$mongo->queryFilterConstructor(array(array('by','=','me'),'or',array('title','=','BUPT')));//選項,目前只提供limit和sort設置,可為空$option=$mongo->optionConstructor(4,array('key'=>'_id','value'=>'-1'));//查找一條,返回一條數據實例$document=$mongo->collectionFindOne($filter,$option);//查找許多條,返回數據實例數組$documents=$mongo->collectionFindMany($filter,$option);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79

4.異常處理
全程try catch,進入一個異常處理函數,詳情如下

public function throwException($e){if($e instanceof UnsupportedException){die("options are used and not supported by the selected server (e.g. collation, readConcern, writeConcern).");}elseif($e instanceof InvalidArgumentException){die("errors related to the parsing of parameters or options.");}elseif($e instanceof MongoDB\Driver\Exception\RuntimeException){die("other errors at the driver level (e.g. connection errors).");}elseif($e instanceof UnexpectedValueException){die(" the command response from the server was malformed.");}elseif($e instanceof MongoDB\Driver\Exception\BulkWriteException ){die("errors related to the write operation. Users should inspect the value returned by getWriteResult() to determine the nature of the error.");}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

三.平滑過度

話說的是平滑過度,但實際上并不可能。舉例而論,Mongo和MongoDB很多相同功能的函數的返回值都不一樣,設計上并不統一。其實這很能反應出老代碼的設計模式有沒有問題,如果分層清晰,那么在邏輯層里就根本不需要改動,需要改動的只是Model層到擴展層的關聯層,但是很遺憾的是往往事與愿違,筆者花費了很多時間在業務代碼上來做邏輯改動,只是為了適應新的版本。但是說到底,感覺自己單獨設計后臺邏輯時,也不會考慮這么多,畢竟誰能想到官方都這么坑呢?想得太多也是給自己挖坑,可能到了過度設計的范疇了。

需要注意的是Mongo的時間類MongoDate已經不再適用,而MongoDB的UTCDateTime的格式并不是簡單的unix時間戳,而是以微秒為單位的時間戳,升級的時候需要注意這一點,這意味新的時間戳是13位,而舊的時間戳是10位,而且在獲取時間戳的方式上也大不相同,MongoDate中設置了Get/set魔術方法,可以直接獲取時間戳屬性,而在UTCDateTime中則根本沒有public的成員,只能通過調用內部函數獲得時間戳。

此外,Mongo中的返回值是array結構的,MongoDB的返回值則是object結構的,需要通過BSONDocument類的getArrayCopy()方法進行轉換,筆者通過(array)強制轉換也是Ok的。

建議對這些部分也進行一個封裝,如下

public static function getUTCDateTime($timestamp=NULL){return new MongoDB\BSON\UTCDateTime($timestamp);}
  • 1
  • 2
  • 3

這樣通過靜態方法可以不實例化直接調用,使用起來很方便。

總的來說,這個從調研到封裝到修改測試上線,花了筆者很多的心血,造輪子是一個寂寞又不太容易獲取滿足感的事情,希望通過寫這篇博客,能有所幫助,實際上呢,獨立寫這樣一個封裝類,對我而言也是一個極大的鍛煉和提高


來源:http://blog.csdn.net/byr_wy/article/details/70768990

總結

以上是生活随笔為你收集整理的从PHP5到PHP7自我封装MongoDB以及平滑升级的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。