深入理解PHP中的流(stream)


之前我和同事業余時間做過單位內部使用的訂餐App,我給ios端提供數據接口,當時無法直接使用PHP的$_POST獲取ios發送的數據,必須使用php中的流php://input才能獲取到(當然Java調整傳輸類型也能做到),當時只是把問題解決了,沒有深入了解流,今天我們梳理一下。PHP官方的介紹比較難理解,我從網上找了好多關於流的介紹和使用,也結合java中的流的概念幫助理解(php好多思想是借鑒java的)。

流是什么:

流是個抽象的概念,是對輸入輸出設備的抽象,Java程序中,對於數據的輸入/輸出操作都是以“流”的方式進行,設備可以是文件,網絡,內存等。

 
2017年03月20日 - zhu329599788@126 - Mindy
 
流具有方向性,至於是輸入流還是輸出流則是一個相對的概念,一般以程序為參考,如果數據的流向是程序至設備,我們成為輸出流,反之我們稱為輸入流。

當程序需要從某個數據源讀入數據的時候,就會開啟一個輸入流,數據源可以是文件、內存或網絡等等。相反地,需要寫出數據到某個數據源目的地的時候,也會開啟一個輸出流,這個數據源目的地也可以是文件、內存或網絡等等。\
2017年03月20日 - zhu329599788@126 - Mindy
 
 

PHP中對流的描述如下:
每一種流都實現了一個包裝器(wrapper),包裝器包含一些額外的代碼用來處理特殊的協議和編碼。PHP提供了一些內置的包裝器,我們也可以很輕松的創建和注冊自定義的包裝器。我們甚至可以使用上下文(contexts)和過濾器來改變和增強包裝器。

PHP中流的形式:<scheme>://<target>。其中<scheme>是包裝器的名字,<target>中的內容是由包裝器的語法指定,不同的包裝器的語法會有所不同。

默認的包裝器是file://,也就是說每次我們訪問文件系統的時候都使用了流。例如,我們可以使用如下兩種方式來讀取文件:readfile('/path/to/somefile.txt')和readfile('file:///path/to/somefile.txt'),使用這兩種方式讀取文件,可以得到相同的結果。

正如前面所說,PHP提供了一些內置的包裝器、協議和過濾器。查看我們的機器上安裝了哪些包裝器,可以使用如下幾個函數:
<?php
var_dump(stream_get_transports()); 


// 獲取已注冊的套接字傳輸協議列表 
array(7) {
  [0]=>
  string(3) "tcp"
  [1]=>
  string(3) "udp"
  [2]=>
  string(4) "unix"
  [3]=>
  string(3) "udg"
  [4]=>
  string(3) "ssl"
  [5]=>
  string(5) "sslv3"
  [6]=>
  string(3) "tls"
}

var_dump(stream_get_wrappers());
// 獲取已注冊的流類型
array(12) {
  [0]=>
  string(5) "https"
  [1]=>
  string(4) "ftps"
  [2]=>
  string(13) "compress.zlib"
  [3]=>
  string(14) "compress.bzip2"
  [4]=>
  string(3) "php"
  [5]=>
  string(4) "file"
  [6]=>
  string(4) "glob"
  [7]=>
  string(4) "data"
  [8]=>
  string(4) "http"
  [9]=>
  string(3) "ftp"
  [10]=>
  string(4) "phar"
  [11]=>
  string(3) "zip"
}

var_dump(stream_get_filters()); 
// 獲取已注冊的數據流過濾器列表
array(12) {
  [0]=>
  string(6) "zlib.*"
  [1]=>
  string(7) "bzip2.*"
  [2]=>
  string(15) "convert.iconv.*"
  [3]=>
  string(12) "string.rot13"
  [4]=>
  string(14) "string.toupper"
  [5]=>
  string(14) "string.tolower"
  [6]=>
  string(17) "string.strip_tags"
  [7]=>
  string(9) "convert.*"
  [8]=>
  string(8) "consumed"
  [9]=>
  string(7) "dechunk"
  [10]=>
  string(8) "mcrypt.*"
  [11]=>
  string(10) "mdecrypt.*"
}

另外,我們可以自定義或者使用第三方的流。

php://包裝器
PHP有它自己的訪問輸入/輸出(I/O)流的包裝器。PHP有基本的php://stdin,php://stdout,php://stderr包裝器對應默認的I/O資源。還有一個php://input流,它是一個只讀的流,流內容是post請求的數據。當我們將數據放在一個post請求的body體內用來請求一個遠程服務的時候,這個流特別好用。

因為php://input是最常用到的流,所以這里列出一些知識點:
1.php://input可以讀取沒有處理過的POST數據。相較於$HTTP_RAW_POST_DATA而言,它給內存帶來的壓力較小,並且不需要特殊的php.ini設置。php://input不能用於enctype=multipart/form-data

2.僅當Content-Type為application/x-www-form-urlencoded且提交方法是POST方法時,$_POST數據與php://input數據才是”一致”(打上引號,表示它們格式不一致,內容一致)的。其它情況,它們都不一致

3.php://input讀取不到GET數據。是因為_GET數據作為query_path寫在http請求頭部(header)的PATH字段,而不是寫在http請求的body部分。

流上下文(Stream Contexts)
PHP還可以通過context和filter對包裝類進行修飾和增強。
(1)關於context,如PHP通過stream_context_create()來設置獲取文件超時時間,這段代碼大家肯定用過:
<?php
    $opts = array(
      'http'=>array(
        'method'=>"GET",
        'timeout'=>60,
      )
    );
    $context = stream_context_create($opts);
    $html =file_get_contents('http://www.jb51.net', false, $context);
?>

(2)關於filter過濾器,首先來看看PHP有哪些內置的過濾器:
    print_r(stream_get_filters());

 

提供PHP中streams函數列表如下:
stream_bucket_append函數:為隊列添加數據 
stream_bucket_make_writeable函數:從操作的隊列中返回一個數據對象
stream_bucket_new函數:為當前隊列創建一個新的數據
stream_bucket_prepend函數:預備數據到隊列 
stream_context_create函數:創建數據流上下文
stream_context_get_default函數:獲取默認的數據流上下文
stream_context_get_options函數:獲取數據流的設置
stream_context_set_option函數:對數據流、數據包或者上下文進行設置
stream_context_set_params函數:為數據流、數據包或者上下文設置參數
stream_copy_to_stream函數:在數據流之間進行復制操作
stream_filter_append函數:為數據流添加過濾器
stream_filter_prepend函數:為數據流預備添加過濾器
stream_filter_register函數:注冊一個數據流的過濾器並作為PHP類執行
stream_filter_remove函數:從一個數據流中移除過濾器
stream_get_contents函數:讀取數據流中的剩余數據到字符串
stream_get_filters函數:返回已經注冊的數據流過濾器列表
stream_get_line函數:按照給定的定界符從數據流資源中獲取行
stream_get_meta_data函數:從封裝協議文件指針中獲取報頭/元數據
stream_get_transports函數:返回注冊的Socket傳輸列表
stream_get_wrappers函數:返回注冊的數據流列表
stream_register_wrapper函數:注冊一個用PHP類實現的URL封裝協議
stream_select函數:接收數據流數組並等待它們狀態的改變
stream_set_blocking函數:將一個數據流設置為堵塞或者非堵塞狀態
stream_set_timeout函數:對數據流進行超時設置
stream_set_write_buffer函數:為數據流設置緩沖區
stream_socket_accept函數:接受由函數stream_ socket_server()創建的Socket連接
stream_socket_client函數:打開網絡或者UNIX主機的Socket連接
stream_socket_enable_crypto函數:為一個已經連接的Socket打開或者關閉數據加密
stream_socket_get_name函數:獲取本地或者網絡Socket的名稱
stream_socket_pair函數:創建兩個無區別的Socket數據流連接
stream_socket_recvfrom函數:從Socket獲取數據,不管其連接與否
stream_socket_sendto函數:向Socket發送數據,不管其連接與否
stream_socket_server函數:創建一個網絡或者UNIX Socket服務端
stream_wrapper_restore函數:恢復一個事先注銷的數據包
stream_wrapper_unregister函數:注銷一個URL地址包

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM