當前位置:
首頁 > 知識 > PHP 統一資源處理 API——流的概述與使用詳解

PHP 統一資源處理 API——流的概述與使用詳解

在現代PHP 特性中,流或許是最出色但使用率最低的。雖然 PHP 4.3 就引入了流,但是很多開發者並不知道流的存在,因為人們很少提及流,而且流的文檔也很匱乏。PHP 官方文檔對流的解釋如下:

流的作用是提供統一的公共函數來處理文件、網路和數據壓縮等操作。簡單而言,流是具有流式行為的資源對象,也就是說,流可以線性讀寫,並且可以通過 fseek() 之類的函數定位到流中的任何位置。

可能看完這段解釋後還是雲里霧裡,我們簡化一下,流的作用是在出發地和目的地之間傳輸數據。出發地和目的地可以是文件、命令行進程、網路連接、ZIP 或 TAR 壓縮文件、臨時內存、標準輸入或輸出,或者是通過 PHP 流封裝協議實現的任何其他資源。

如果你讀寫過文件,就用過流;如果你從 讀取過數據,或者把輸入寫入 ,也用過流。流為 PHP 的很多 IO 函數提供了底層實現,如 、 、 和 等。PHP 的流函數提供了不同資源的統一介面。

我們可以把流比作管道,把水(資源數據)從一個地方引到另一個地方。在水從出發地到目的地的過程中,我們可以過濾水,可以改變水質,可以添加水,也可以排出水。

流封裝協議

流式數據的種類各異,每種類型需要獨特的協議,以便讀寫數據,我們稱這些協議為 流封裝協議 。例如,我們可以讀寫文件系統,可以通過 HTTP、HTTPS 或 SSH 與遠程 Web 伺服器通信,還可以打開並讀寫 ZIP、RAR 或 PHAR 壓縮文件。這些通信方式都包含下述相同的過程:

開始通信

讀取數據

寫入數據

結束通信

雖然過程是一樣的,但是讀寫文件系統中文件的方式與收發 HTTP 消息的方式有所不同,流封裝協議的作用是使用通用的介面封裝這種差異。

每個流都有一個協議和一個目標。指定協議和目標的方法是使用流標識符: ,其中 是流的封裝協議, 是流的數據源。

http://流封裝協議

下面使用 HTTP 流封裝協議創建了一個與 Flicker API 通信的 PHP 流:

$json = file_get_contents(

"http://api.flickr.com/services/feeds/photos_public.gne?format=json");

不要以為這是普通的網頁 URL, 函數的字元串參數其實是一個流標識符。 協議會讓 PHP 使用 HTTP 流封裝協議,在這個參數中, 之後是流的目標。

註:很多 PHP 開發者可能並不知道普通的 URL 其實是 PHP 流封裝協議標識符的偽裝。

file://流封裝協議

我們通常使用 、 、 和 等函數讀寫文件系統,因為 PHP 默認使用的流封裝協議是 ,所以我們很少認為這些函數使用的是 PHP 流。下面的示例演示了使用 流封裝協議創建一個讀寫 文件的流:

$handle = fopen("file:///etc/hosts","rb");

while(feof($handle) !==TRUE) {

echofgets($handle);}fclose($handle);

我們通常會省略掉 協議,因為這是 PHP 使用的默認值。

php://流封裝協議

編寫命令行腳本的 PHP 開發者會感激 流封裝協議,這個流封裝協議的作用是與 PHP 腳本的標準輸入、標準輸出和標準錯誤文件描述符通信。我們可以使用 PHP 提供的文件系統函數打開、讀取或寫入下面四個流:

:這是個只讀 PHP 流,其中的數據來自標準輸入。PHP 腳本可以使用這個流接收命令行傳入腳本的信息;

:把數據寫入當前的輸出緩衝區,這個流只能寫,無法讀或定址;

:從系統內存中讀取數據,或者把數據寫入系統內存。缺點是系統內存有限,所有使用 更安全;

:和 類似,不過,沒有可用內存時,PHP 會把數據寫入這個臨時文件。

其他流封裝協議

PHP 和 PHP 擴展還提供了很多其他流封裝協議,例如,與 ZIP 和 TAR 壓縮文件、FTP 伺服器、數據壓縮庫、Amazon API、Dropbox API 等通信的流封裝協議。需要注意的是,PHP 中的 、 、 、 以及 等函數不僅可以用來處理文件系統中的文件,還可以在所有支持這些函數的流封裝協議中使用。

註:更多流封裝協議,請參考官方網站: http://php.net/manual/zh/wrappers.php

自定義流封裝協議

我們還可以自己編寫 PHP 流封裝協議。PHP 提供了一個示例 類,演示如何編寫自定義的流封裝協議,支持部分或全部 PHP 文件系統函數。關於如何編寫,具體請參考以下文檔:

http://php.net/manual/zh/class.streamwrapper.php

http://php.net/manual/zh/stream.streamwrapper.example-1.php


流上下文

有些 PHP 流能夠接受一系列可選的參數,這些參數叫流上下文,用於定製流的行為。不同的流封裝協議使用的流上下文有所不同,流上下文使用 函數創建,這個函數返回的上下文對象可以傳入大多數文件系統函數。

例如,你知道可以使用 file_get_contents() 發送 HTTP POST 請求嗎?使用一個流上下文對象即可實現:

$requestBody ="{"username":"nonfu"}";$context = stream_context_create([

"http"=> [

"method"=>"POST",

"header"=>"Content-Type: application/json;charset=utf-8;


Content-Length: ". mb_strlen($requestBody),

"content"=> $requestBody ]]);$response = file_get_contents("https://my-api.com/users",

false, $context);

流上下文是個關聯數組,最外層鍵是流封裝協議的名稱,流上下文數組中的值針對不同的流封裝協議有所不同,可用的設置參考各個 PHP 流封裝協議的文檔。


流 過濾器

目前為止我們討論了如何打開流,讀取流中的數據,以及把數據寫入流。不過,PHP 流真正強大的地方在於過濾、轉換、添加或刪除流中傳輸的數據,例如,我們可以打開一個流處理 Markdown 文件,在把文件內容讀入內存的過程中自動將其轉化為 HTML。

註:PHP 所有可用流過濾器請參考官方文檔: http://php.net/manual/zh/filters.php 。

若想把過濾器附加到現有的流上,要使用 函數,下面我們以 過濾器演示如何把文件中的內容轉換成大寫字母:

$handle = fopen("test.txt","rb");stream_filter_append($handle,"string.toupper");

while(feof($handle) !==true) { echo fgets($handle);}fclose($handle);

運行該腳本,輸出的都是大寫字母:

ABCDEEFGHIJKLMNHELLO LARAVELACADEMY!

我們還可以使用 流封裝協議把過濾器附加到流上,不過,使用這種方式之前必須先打開 PHP 流:

$handle = fopen("php://filter/read=

string.toupper/resource=test.txt","rb");

while(feof($handle) !==true) { echo fgets($handle);}fclose($handle);

這個方式實現效果和 函數一樣,但是相比之下更為繁瑣。不過,PHP 的某些文件系統函數在調用後無法附加過濾器,例如 和 ,使用這些函數時只能使用 流封裝協議附加流過濾器。


自定義流過濾器

我們還可以編寫自定義的流過濾器。其實,大多數情況下都要使用自定義的流過濾器,自定義的流過濾器是個 PHP 類,繼承內置的 類( http://php.net/manual/zh/class.php-user-filter.php ),且必須實現 、 和 方法,最後,必須使用 函數註冊自定義的流過濾器。

註:PHP 流會把數據分成按次序排列的桶,一個桶中盛放的流數據是固定的(如 4096 位元組),如果還用管道比喻,就是把水放在一個個水桶中,順著管道從出發地漂流到目的地,在漂流過程中會經過過濾器,過濾器一次可以接收並處理一個或多個桶,一定時間內過濾器接收到的桶叫做桶隊列。桶隊列中的每個桶對象都有兩個公共屬性: 和 ,分別表示桶的內容和長度。

下面我們自定義一個流過濾器 ,把流數據讀入內存時審查其中的髒字:

classDirtyWordsFilterextendsphp_user_filter{

/** *@paramresource $in 流入的桶隊列 *@paramresource $out 流出的桶隊列 *@paramint $consumed 處理的位元組數 *@parambool $closing 是否是流中最後一個桶隊列 *@returnint * 接收、處理再轉運桶中的流數據,在該方法中,

我們迭代桶隊列對象,把髒字替換成審查後的值 */publicfunctionfilter($in, $out, &$consumed, $closing){ $words = ["grime","dirt","grease"]; $wordData = [];

foreach($wordsas$word) { $replacement = array_fill(, mb_strlen($word),"*"); $wordData[$word] = implode("", $replacement); } $bad = array_keys($wordData); $good = array_values($wordData);

// 迭代桶隊列中的每個桶while($bucket = stream_bucket_make_writeable($in)) {

// 審查桶對象中的髒字$bucket->data = str_replace($bad, $good, $bucket->data);

// 增加已處理的數據量$consumed += $bucket->datalen;

// 把桶放入流向下游的隊列中stream_bucket_append($out, $bucket); }

returnPSFS_PASS_ON; }}

然後,我們必須使用 函數註冊這個自定義的 流過濾器:

stream_filter_register("dirty_words_filter","DirtyWordsFilter");

第一個參數用於標識這個自定義過濾器的過濾器名,第二個參數是這個自定義過濾器的類名。接下來就可以使用這個自定義的流過濾器了:

$handle = fopen("test.txt","rb");stream_filter_append($handle,"dirty_words_filter");

while(feof($handle) !==true) { echo fgets($handle);}fclose($handle);

修改 內容如下:

abcdeefghijklmnHello LaravelAcademy!grimeI hate dirty things!

運行上面的自定義過濾器腳本,結果如下:

abcdeefghijklmnHello LaravelAcademy!*****I hate****y things!

更多分享,敬請關注

本文來源網路,侵立刪!

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 PHP技術大全 的精彩文章:

TP5驗證碼實現
模仿KOA,用php來寫一個極簡的開發框架

TAG:PHP技術大全 |