PHP回顧之IO

轉載請註明文章出處: https://tlanyan.me/php-review...

PHP回顧系列目錄

不管哪一種編程語言,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

  1. file_exists/is_file/filesize/fileperms等獲取文件信息的輔助函數;
  2. fopen:打開文件,獲取文件句柄,第二個參數(arwcbx)的含義要弄清楚;
  3. flock:獲取文件鎖,可用其實現進程互斥鎖;
  4. fread/fgets/fscanf等獲取文件內容的函數;
  5. fwrite/fputs/fputcsv/ftruncate等寫入內容函數;
  6. feof/ftell/fseek/rewind等操做文件指針位置的函數;
  7. fclose: 關閉文件,釋放資源。

注意本節中的文件指是 本地文件,對於遠程文件,上述函數是否起做用取決於協議是否提供支持。例如fread/fwrite能夠操做http://協議的資源,但stat/filesize等函數不能正常工做。可參考官網的「協議和包裝器」 查看非本地普通文件時可用的函數信息。數據庫

命令行輸入和輸出

PHP主要用於web開發,命令行應用也比較常見,好比定時任務的腳本。命令行模式下,有很多與web開發不一樣的地方,好比可使用多進程/線程(web中的curl_multiple不算),沒有運行時間限制等。編程

命令行時php_sapi_name返回值爲cli,標準輸入輸出均指向終端(可用ll /proc/進程號/fd查看)。PHP定義了三個句柄常量:json

  1. STDIN: 標準輸入,只讀,等同於用fopen打開"php://stdin";
  2. STDOUT: 標準輸出,只寫,等同於用fopen打開"php://stdout";
  3. STDERR: 標準錯誤輸出,只寫,等同於fopen打開"php://stderr"。

注意標準輸入對應"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服務器的交互流程以下:

  1. 初始化CURL句柄
  2. 設置請求信息:請求URL、頭部信息、cookie、正文等;
  3. 發送請求
  4. 獲取執行結果
  5. 關閉CURL句柄,釋放資源

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等函數也能實現與遠程服務器的交互,這部份內容放在後續的流中闡述。

file_get_contents

上文廢話了半天,還沒說到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);

參考

  1. http://php.net/manual/en/ref....
  2. http://php.net/manual/en/feat...
  3. http://php.net/manual/en/book...
  4. https://github.com/yiisoft/yi...

感謝閱讀,感謝指正!

相關文章
相關標籤/搜索