轉載請註明文章出處: https://tlanyan.me/php-review...
不管哪一種編程語言,IO操做都值得好好學習和理解。因爲PHP簡單的特性,很多人對此毫無概念也能寫出可用的代碼。本文總結PHP開發中幾個常見的IO場景並介紹對應的操做,但願能幫助PHP開發人員加深對IO的理解。後續文章中將介紹隱藏在簡單之下的本質:流。php
本文介紹的場景包括:讀寫文件、命令行輸入輸出、與遠程網址交互。廢話少說,直接開始吧!git
文件的讀寫是最常規的IO操做。打開文件、讀寫內容、關閉文件,一鼓作氣,沒什麼尿點。一個典型的讀取文件內容例子:github
function getFileContent(string $filename) : ?string { if (!is_file($filename)) { return null; } $fd = fopen($filename, "rb"); $content = fread($fd, filesize($filename); fclose($fd); return $content; }
讀寫文件So easy! 要想對文件操做玩得更高端點,至少須要熟練使用這些API:web
注意本節中的文件指是 本地文件,對於遠程文件,上述函數是否起做用取決於協議是否提供支持。例如fread/fwrite能夠操做http://協議的資源,但stat/filesize等函數不能正常工做。可參考官網的「協議和包裝器」 查看非本地普通文件時可用的函數信息。數據庫
PHP主要用於web開發,命令行應用也比較常見,好比定時任務的腳本。命令行模式下,有很多與web開發不一樣的地方,好比可使用多進程/線程(web中的curl_multiple不算),沒有運行時間限制等。編程
命令行時php_sapi_name
返回值爲cli,標準輸入輸出均指向終端(可用ll /proc/進程號/fd
查看)。PHP定義了三個句柄常量:json
注意標準輸入對應"php://stdin"而非"php://input",雖然這二者行爲在命令行模式下幾乎一致(區別可參考本人以前的文章php://output和php://stdout的區別)。api
操做三個讀寫通道,對應的函數是fread/fgetc/fscanf/fwrite/fputc/fputs等。PHP會在腳本執行完畢後關閉三個流,無需用戶手動關閉。下面用代碼簡要展現用法:服務器
function prompt(string $message) : string { fwrite(STDOUT, $message); // fgets會把換行符也讀入,可用rtrim過濾掉 return rtrim(fgets(STDIN)); } function println(string $message) : void { fputs(STDOUT, $message . PHP_EOL); } function error(string $message) : void { fputs(STDERR, $message . PHP_EOL); } $value = prompt("input your value:"); if ($value !== "") { println("your input: $value"); } else { error("invalid value!"); }
命令行模式時"php://output"連接到標準輸出,因此echo/print/var_dump
等輸出函數可正常使用。要交互式的從命令行獲取輸入,則須要用到fread/fgets等文件讀取函數。yii2
常量PHP_EOL
是預約義的跨平臺換行符,EOL是end of line
的縮寫,不是end of life
~
從網頁獲取內容,cURL拓展絕對值得大提特提。若是你熟悉curl
命令,對其功能的強大應該有所瞭解,那麼應該對使用PHP中的CURL系列函數會駕輕就熟。
與遠程網址交互是一個請求和響應的過程,其中細節可參考本人以前的文章:PHP回顧之web請求和PHP回顧之web響應,也可參考HTTP協議的權威文檔。使用CURL與遠程web服務器的交互流程以下:
CURL簡單好用,缺點是請求的設置參數繁雜難記。
實踐中推薦以類Java的HttpClient庫形式與遠程服務器交互。HttpClient類庫將請求、響應、傳輸等概念抽出來,徹底面向對象,更語義化,使用其能更好促進對HTTP協議的理解,缺點是代碼相對繁瑣。PHP有很多相似的HTTP請求庫,如下使用Yii2中的yii2-httpclient
類庫展現使用示例:
use yii\httpclient\Client; use yii\httpclient\Response; $url = "https://tlanyan.me"; $data = [ "key1" => "value1", "key2" => "value2", ]; $response = (new Client())->createRequest() ->setMethod("POST") ->setFormat(Client::FORMAT_JSON) ->setUrl($url) ->setData($data) ->send(); if ($response->isOk) { $response->setFormat(Client::FORMAT_JSON); // 獲取解析後的數據 $data = $response->data; .... }
使用fopen/fsocketopen等函數也能實現與遠程服務器的交互,這部份內容放在後續的流中闡述。
上文廢話了半天,還沒說到PHP中獲取內容的神器:file_get_contents
函數。該函數是PHP讀取內容當之無愧的神器,無論是常規文件、php://、http://、仍是標準輸入等,file_get_contents一句話搞定。相較於Java等語言中的client/connection/stream等一堆代碼,file_get_contents體現了PHP簡單實用的設計哲學。
想必PHP開發經常使用該函數,就用幾個簡單的示例結束本文(注意代碼中POST請求網頁已經涉及到了流的內容)。
// 讀取普通文件 file_get_contents("/etc/passwd"); // 獲取web請求的原始正文,可獲取json/xml等數據格式的原始內容,也可得到上傳文件的內容,注意該返回可能惟二進制 // 以json/xml數據格式交互時,推薦使用此方法而非經過$GLOBALS['HTTP_RAW_POST_DATA']獲取,$HTTP_RAW_POST_DATA在PHP 7.2中已被移除 file_get_contents("php://input"); // 獲取網址內容,可取代curl file_get_contents('https://tlanyan.me'); // 傳入context對象,可實現post請求 $contextOptions = [ "http" => [ "method" => "POST", "ignore_errors" => true, "content" => "username=tlanyan", "header" => "Content-type: application/x-www-form-urlencoded", "user_agent" => "MySpider/1.0", ], "ssl" => [ "verify_peer" => false, ], ]; $context = stream_context_create($contextOptions); file_get_contents("https://tlanyan.me", false, $context); // cli模式下從標準輸入讀取數據,此時換行符也被當作輸入的一部分,要以ctrl+d做爲結束輸入的標誌 file_get_contents(STDIN); // 寫入文件內容 file_put_contents("foo.txt", "Test function call\n", FILE_APPEND);
感謝閱讀,感謝指正!