通過(guò)PHP的Wrapper無(wú)縫遷移原有項(xiàng)目到新服務(wù)的實(shí)現(xiàn)方法
出于性能和安全方面的考慮,公司的平臺(tái)上禁用了本地文件讀寫和對(duì)外的數(shù)據(jù)抓取.相應(yīng)的,我們提供了對(duì)應(yīng)的服務(wù)來(lái)做同樣的事情.新服務(wù)的接口和原來(lái)不太一樣.
專門為我們平臺(tái)開(kāi)發(fā)的程序當(dāng)然不會(huì)存在問(wèn)題,但是有大量的已有的程序和開(kāi)源項(xiàng)目,就面臨著繁雜的遷移工作.
Wrapper
其實(shí)從PHP4.3開(kāi)始,PHP就支持Wrapper了,這意味著用戶可以自定義和重載協(xié)議.
只需要使用 stream_wrapper_register 函數(shù)就可以注冊(cè)一個(gè)協(xié)議,對(duì)這個(gè)協(xié)議的相關(guān)操作,PHP都會(huì)回調(diào)相關(guān)的函數(shù).
手冊(cè)上給了一個(gè)例子. 它注冊(cè)了一個(gè)叫var的協(xié)議,然后對(duì)這個(gè)協(xié)議操作都會(huì)回調(diào)VariableStream class里邊定義的方法.
varname = $url['host'];$this->position = 0;return true;}function stream_read($count){$ret = substr($GLOBALS[$this->varname], $this->position, $count);$this->position += strlen($ret);return $ret;}function stream_write($data){$left = substr($GLOBALS[$this->varname], 0, $this->position);$right = substr($GLOBALS[$this->varname], $this->position + strlen($data));$GLOBALS[$this->varname] = $left . $data . $right;$this->position += strlen($data);return strlen($data);}function stream_tell(){return $this->position;}function stream_eof(){return $this->position >= strlen($GLOBALS[$this->varname]);}function stream_seek($offset, $whence){switch ($whence) {case SEEK_SET:if ($offset < strlen($GLOBALS[$this->varname]) && $offset >= 0) {$this->position = $offset;return true;} else {return false;}break;case SEEK_CUR:if ($offset >= 0) {$this->position += $offset;return true;} else {return false;}break;case SEEK_END:if (strlen($GLOBALS[$this->varname]) + $offset >= 0) {$this->position = strlen($GLOBALS[$this->varname]) + $offset;return true;} else {return false;}break;default:return false;}}}stream_wrapper_register('var', 'VariableStream')or die('Failed to register protocol');$myvar = '';$fp = fopen('var://myvar', 'r+');fwrite($fp, 'line1n');fwrite($fp, 'line2n');fwrite($fp, 'line3n');rewind($fp);while (!feof($fp)) {echo fgets($fp);}fclose($fp);var_dump($myvar);?>
回調(diào)class里邊能實(shí)現(xiàn)的接口列表在這里: http://cn2.php.net/manual/en/class.streamwrapper.php
需要注意的一些問(wèn)題
構(gòu)造函數(shù)
首先是,wrapper class很特別,它的構(gòu)造函數(shù)并不是每次都調(diào)用的.只有在你的操作觸發(fā)了stream_open相關(guān)的操作時(shí)才會(huì)調(diào)用,比如你用file_get_contents了.而當(dāng)你的操作觸發(fā)和stream無(wú)關(guān)的函數(shù)時(shí),比如file_exists會(huì)觸發(fā)url_stat方法,這個(gè)時(shí)候構(gòu)造函數(shù)是不會(huì)被調(diào)用的.
讀實(shí)現(xiàn)
wrapper里邊有position和seek等概念,但是很多服務(wù)其實(shí)是一次性就讀取全部數(shù)據(jù)的,這個(gè)可以在stream_open的時(shí)候一次性讀回,放到一個(gè)屬性中,以后seek和tell的時(shí)候直接操作屬性里邊存放的數(shù)據(jù)就可以了.
url_stat的實(shí)現(xiàn)
在wrapper class的實(shí)現(xiàn)中,url_stat的實(shí)現(xiàn)是個(gè)難點(diǎn).必須正確的實(shí)現(xiàn)url_stat才能使is_writable和is_readable等查詢文件元信息的函數(shù)正常工作.
而我們需要為我們的虛設(shè)備偽造這些值.以mc為例,我給大家一些參考數(shù)據(jù).
url_stat應(yīng)該返回一個(gè)數(shù)組,分13個(gè)項(xiàng),內(nèi)容如下:
dev 設(shè)備號(hào)- 寫0即可
ino inode號(hào) - 寫0即可
mode 文件mode - 這個(gè)是文件的權(quán)限控制符號(hào),稍后詳細(xì)說(shuō)明
nlink link - 寫0即可.
uid uid - Linux上用posix_get_uid可以取到,windows上為0
gid gid - Linux上用posix_get_gid可以取到,windows上為0
rdev 設(shè)備類型 - 當(dāng)為inode設(shè)備時(shí)有值
size 文件大小
atime 最后讀時(shí)間 格式為unix時(shí)間戳
mtime 最后寫時(shí)間
ctime 創(chuàng)建時(shí)間
blksize blocksize of filesystem IO 寫零即可
blocks number of 512-byte blocks allocated 寫零即可
其中mode的值必須寫對(duì)
如果是文件,其值為
0100000 + 文件權(quán)限 ; 如 0100000 + 0777;
如果是目錄,其值為
040000 + 目錄權(quán)限 ; 如 0400000 + 0777;
可以重載標(biāo)準(zhǔn)協(xié)議
根據(jù)實(shí)際測(cè)試來(lái)看,用stream_wrapper_unregister可以卸載掉http等內(nèi)置協(xié)議.這就方便我們完全無(wú)縫的替換用戶的一些操作,比如file_get_contents(‘http://sae.sina.com.cn’)到我們自己實(shí)現(xiàn)的服務(wù)上.
知識(shí)點(diǎn)補(bǔ)充:
php wrapper實(shí)現(xiàn)
【背景】
做一個(gè)thrift client的wrapper,用以實(shí)現(xiàn)對(duì)于服務(wù)器的重試邏輯。
【關(guān)鍵點(diǎn)】
1. wrapper要求跟用client一樣方便。
2. 當(dāng)某個(gè)服務(wù)器掛掉之后可以隨機(jī)選另一臺(tái)重試。
3. 用到的php幾個(gè)關(guān)鍵特性: __call()(magic function,當(dāng)訪問(wèn)的對(duì)象函數(shù)不存在時(shí)會(huì)調(diào)用這個(gè)), ReflectionClass 反射類及其其成員函數(shù)newInstanceArgs , call_user_func_array回調(diào)函數(shù)。
直接看代碼吧(某位牛人寫的,not me):
#!/usr/bin/env php<?php namespace wrapper; error_reporting(E_ALL); require_once ’/usr/local/Cellar/thrift/0.9.1/Thrift/ClassLoader/ThriftClassLoader.php’; use ThriftClassLoaderThriftClassLoader; $GEN_DIR = realpath(dirname(__FILE__).’/..’).’/gen-php’; $loader = new ThriftClassLoader();$loader->registerNamespace(’Thrift’, ’/usr/local/Cellar/thrift/0.9.1/’);$loader->registerDefinition(’xiaoju’, $GEN_DIR);$loader->register(); use ThriftProtocolTBinaryProtocol;use ThriftTransportTSocket;use ThriftTransportTHttpClient;use ThriftTransportTBufferedTransport;use ThriftExceptionTException; class RetryWrapper { public function __construct($classname, $hosts) { $this->clazz = new ReflectionClass($classname); $this->hosts = $hosts; } public function __call($method, $args) { shuffle($this->hosts); foreach ($this->hosts as $key => $host) { try {return $this->inner_call($host, $method, $args); } catch (TException $ex) {$msg = $ex->getMessage();if (!strstr($msg, ’TSocket’)) { throw $ex;} } } throw new TException('all server down!'); } public function inner_call($host, $method, $args) { $tmp = explode(':', $host); $socket = new TSocket($tmp[0], (int)$tmp[1]); $transport = new TBufferedTransport($socket, 1024, 1024); $protocol = new TBinaryProtocol($transport); $client = $this->clazz->newInstanceArgs(array($protocol)); $transport->open(); $result = call_user_func_array(array($client, $method), $args); $transport->close(); return $result; }} $hosts = array(’localhost:9090’, ’localhost:9091’);$wrapper = new RetryWrapper('xxxxxMessageServiceClient', $hosts, 3); $data = array(’businessId’ => 300100001, ’phones’ => array(’2’,’2’,’3’), ’message’ => ’asdfqer’) ;$message = new xxxxxMessage($data); print $wrapper->sendMessage($message);print 'n'; ?>
總結(jié)
到此這篇關(guān)于通過(guò)PHP的Wrapper無(wú)縫遷移原有項(xiàng)目到新服務(wù)的實(shí)現(xiàn)方法的文章就介紹到這了,更多相關(guān)php wrapper 遷移新服務(wù)內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!
相關(guān)文章:
1. 在Android中使用WebSocket實(shí)現(xiàn)消息通信的方法詳解2. .NET中l(wèi)ambda表達(dá)式合并問(wèn)題及解決方法3. Python importlib動(dòng)態(tài)導(dǎo)入模塊實(shí)現(xiàn)代碼4. 淺談python出錯(cuò)時(shí)traceback的解讀5. windows服務(wù)器使用IIS時(shí)thinkphp搜索中文無(wú)效問(wèn)題6. ASP 信息提示函數(shù)并作返回或者轉(zhuǎn)向7. Nginx+php配置文件及原理解析8. 利用promise及參數(shù)解構(gòu)封裝ajax請(qǐng)求的方法9. python matplotlib:plt.scatter() 大小和顏色參數(shù)詳解10. JSP數(shù)據(jù)交互實(shí)現(xiàn)過(guò)程解析
