您好,登錄后才能下訂單哦!
本篇內容介紹了“如何通過PHP無縫遷移原有項目到新服務”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
出于性能和安全方面的考慮,公司的平臺上禁用了本地文件讀寫和對外的數據抓取.相應的,我們提供了對應的服務來做同樣的事情.新服務的接口和原來不太一樣.
專門為我們平臺開發的程序當然不會存在問題,但是有大量的已有的程序和開源項目,就面臨著繁雜的遷移工作.
Wrapper
其實從PHP4.3開始,PHP就支持Wrapper了,這意味著用戶可以自定義和重載協議.
只需要使用 stream_wrapper_register
函數就可以注冊一個協議,對這個協議的相關操作,PHP都會回調相關的函數.
手冊上給了一個例子. 它注冊了一個叫var的協議,然后對這個協議操作都會回調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, "line1\n"); fwrite($fp, "line2\n"); fwrite($fp, "line3\n"); rewind($fp); while (!feof($fp)) { echo fgets($fp); } fclose($fp); var_dump($myvar); ?>
回調class里邊能實現的接口列表在這里: http://cn2.php.net/manual/en/class.streamwrapper.php
需要注意的一些問題
構造函數
首先是,wrapper class
很特別,它的構造函數并不是每次都調用的.只有在你的操作觸發了stream_open相關的操作時才會調用,比如你用file_get_contents
了.而當你的操作觸發和stream無關的函數時,比如file_exists會觸發url_stat方法,這個時候構造函數是不會被調用的.
讀實現
wrapper里邊有position和seek等概念,但是很多服務其實是一次性就讀取全部數據的,這個可以在stream_open
的時候一次性讀回,放到一個屬性中,以后seek和tell的時候直接操作屬性里邊存放的數據就可以了.
url_stat的實現
在wrapper class的實現中,url_stat的實現是個難點.必須正確的實現url_stat才能使is_writable
和is_readable等查詢文件元信息的函數正常工作.
而我們需要為我們的虛設備偽造這些值.以mc為例,我給大家一些參考數據.
url_stat應該返回一個數組,分13個項,內容如下:
dev 設備號- 寫0即可
ino inode號 - 寫0即可
mode 文件mode - 這個是文件的權限控制符號,稍后詳細說明
nlink link - 寫0即可.
uid uid - Linux上用posix_get_uid可以取到,windows上為0
gid gid - Linux上用posix_get_gid可以取到,windows上為0
rdev 設備類型 - 當為inode設備時有值
size 文件大小
atime 最后讀時間 格式為unix時間戳
mtime 最后寫時間
ctime 創建時間
blksize blocksize of filesystem IO 寫零即可
blocks number of 512-byte blocks allocated 寫零即可
其中mode的值必須寫對
如果是文件,其值為
0100000 + 文件權限 ; 如 0100000 + 0777;
如果是目錄,其值為
040000 + 目錄權限 ; 如 0400000 + 0777;
可以重載標準協議
根據實際測試來看,用stream_wrapper_unregister
可以卸載掉http等內置協議.這就方便我們完全無縫的替換用戶的一些操作,比如file_get_contents(‘http://sae.sina.com.cn')
到我們自己實現的服務上.
知識點補充:
php wrapper實現
【背景】
做一個thrift client的wrapper,用以實現對于服務器的重試邏輯。
【關鍵點】
1. wrapper要求跟用client一樣方便。
2. 當某個服務器掛掉之后可以隨機選另一臺重試。
3. 用到的php幾個關鍵特性: __call()(magic function,當訪問的對象函數不存在時會調用這個), ReflectionClass 反射類及其其成員函數newInstanceArgs , call_user_func_array回調函數。
直接看代碼吧(某位牛人寫的,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 Thrift\ClassLoader\ThriftClassLoader; $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 Thrift\Protocol\TBinaryProtocol; use Thrift\Transport\TSocket; use Thrift\Transport\THttpClient; use Thrift\Transport\TBufferedTransport; use Thrift\Exception\TException; 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("\xxx\xx\MessageServiceClient", $hosts, 3); $data = array('businessId' => 300100001, 'phones' => array('2','2','3'), 'message' => 'asdfqer') ; $message = new \xxx\xx\Message($data); print $wrapper->sendMessage($message); print "\n"; ?>
總結
“如何通過PHP無縫遷移原有項目到新服務”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。