1. 程式人生 > >PHP規範PSR7(HTTP訊息介面)介紹(三)

PHP規範PSR7(HTTP訊息介面)介紹(三)

1.5 服務端請求

RequestInterface提供HTTP請求訊息的一般表示。但是,由於伺服器端環境的性質,伺服器端請求需要額外的處理。伺服器端處理需要考慮通用閘道器介面(CGI),更具體地說,需要考慮PHP通過其伺服器API(SAPI)對CGI的抽象和擴充套件。 PHP通過超級全域性提供了關於輸入編組的簡化,例如:

  • $ _COOKIE,反序列化並提供對HTTP cookie的簡化訪問。
  • $ _GET,反序列化並提供對查詢字串引數的簡化訪問。
  • $ _POST,反序列化並提供對通過HTTP POST提交的urlencoded引數的簡化訪問;通常,它可以被認為是解析訊息體的結果。
  • $ _FILES,提供​​有關檔案上傳的序列化元資料。
  • $ _SERVER,提供對CGI / SAPI環境變數的訪問,這些變數通常包括請求方法,請求方案,請求URI和標頭。

ServerRequestInterface擴充套件了RequestInterface,以提供圍繞這些各種超級全域性的抽象。這種做法有助於減少消費者對超全球的耦合,並鼓勵和提升測試請求消費者的能力。

伺服器請求提供了一個附加屬性“屬性”,以允許消費者根據特定於應用程式的規則(例如路徑匹配,方案匹配,主機匹配等)內省,分解和匹配請求。因此,伺服器請求還可以在多個請求消費者之間提供訊息傳遞。

1.6 上傳檔案

ServerRequestInterface指定用於在規範化結構中檢索上載檔案樹的方法,每個葉子都是UploadedFileInterface的例項。

$ _FILES超全域性在處理檔案輸入陣列時有一些眾所周知的問題。例如,如果您有一個提交檔案陣列的表單 - 例如輸入名稱“files”,提交檔案[0]和檔案[1] - PHP將表示為:

array(
    'files' => array(
        'name' => array(
            0 => 'file0.txt',
            1 => 'file1.html',
        ),
        'type' => array(
            0 => 'text/plain',
            1 => 'text/html',
        ),
        /* etc. */
    ),
)

而不是預期的:

array(
    'files' => array(
        0 => array(
            'name' => 'file0.txt',
            'type' => 'text/plain',
            /* etc. */
        ),
        1 => array(
            'name' => 'file1.html',
            'type' => 'text/html',
            /* etc. */
        ),
    ),
)

結果是消費者需要知道這種語言實現細節,並編寫用於收集給定上載資料的程式碼。

此外,存在以下情況:在發生檔案上載時未填充$ _FILES:

  • 當HTTP方法不是POST時。
  • 單元測試時。
  • 在非SAPI環境下執行時,例如ReactPHP。

在這種情況下,資料需要以不同方式播種。例如:

  • 程序可能會解析郵件正文以發現檔案上載。在這種情況下,實現可以選擇不將檔案上載寫入檔案系統,而是將它們包裝在流中以減少記憶體,I / O和儲存開銷。
  • 在單元測試場景中,開發人員需要能夠存根和/或模擬檔案上載元資料,以便驗證和驗證不同的場景。

引用的樹結構應該模仿提交檔案的命名結構。

在最簡單的示例中,這可能是提交的單個命名錶單元素:

<input type="file" name="avatar" />

在這種情況下,$ _FILES中的結構如下所示:

array(
    'avatar' => array(
        'tmp_name' => 'phpUxcOty',
        'name' => 'my-avatar.png',
        'size' => 90996,
        'type' => 'image/png',
        'error' => 0,
    ),
)

getUploadedFiles()返回的規範化表單將是:

array(
    'avatar' => /* UploadedFileInterface instance */
)

對於使用陣列表示法輸入名稱的輸入:

<input type="file" name="my-form[details][avatar]" />

$ _FILES最終看起來像這樣:

array(
    'my-form' => array(
        'details' => array(
            'avatar' => array(
                'tmp_name' => 'phpUxcOty',
                'name' => 'my-avatar.png',
                'size' => 90996,
                'type' => 'image/png',
                'error' => 0,
            ),
        ),
    ),
)

getUploadedFiles()返回的相應樹應該是:

array(
    'my-form' => array(
        'details' => array(
            'avatar' => /* UploadedFileInterface instance */
        ),
    ),
)

在某些情況下,您可以指定一個檔案陣列:

Upload an avatar: <input type="file" name="my-form[details][avatars][]" />
Upload an avatar: <input type="file" name="my-form[details][avatars][]" />

(例如,JavaScript控制元件可能會產生額外的檔案上傳輸入,以允許一次上傳多個檔案。)

在這種情況下,規範實現必須聚合與給定索引處的檔案相關的所有資訊。原因是因為$ _FILES在這種情況下偏離其正常結構:

array(
    'my-form' => array(
        'details' => array(
            'avatars' => array(
                'tmp_name' => array(
                    0 => '...',
                    1 => '...',
                    2 => '...',
                ),
                'name' => array(
                    0 => '...',
                    1 => '...',
                    2 => '...',
                ),
                'size' => array(
                    0 => '...',
                    1 => '...',
                    2 => '...',
                ),
                'type' => array(
                    0 => '...',
                    1 => '...',
                    2 => '...',
                ),
                'error' => array(
                    0 => '...',
                    1 => '...',
                    2 => '...',
                ),
            ),
        ),
    ),
)

上面的$ _FILES陣列將對應於getUploadedFiles()返回的以下結構

array(
    'my-form' => array(
        'details' => array(
            'avatars' => array(
                0 => /* UploadedFileInterface instance */,
                1 => /* UploadedFileInterface instance */,
                2 => /* UploadedFileInterface instance */,
            ),
        ),
    ),
)

消費者將使用以下方法訪問巢狀陣列的索引1:

$request->getUploadedFiles()['my-form']['details']['avatars'][1];

 由於上傳的檔案資料是派生的(派生自$ _FILES或請求體),因此介面中也存在mutator方法withUploadedFiles(),允許將規範化委託給另一個程序。

在原始示例的情況下,消費類似於以下內容:

$file0 = $request->getUploadedFiles()['files'][0];
$file1 = $request->getUploadedFiles()['files'][1];

printf(
    "Received the files %s and %s",
    $file0->getClientFilename(),
    $file1->getClientFilename()
);

// "Received the files file0.txt and file1.html"

該提案還認識到實現可以在非SAPI環境中執行。因此,UploadedFileInterface提供了確保操作無論環境如何都可以工作的方法。特別是:

  • moveTo($ targetPath)作為直接在臨時上載檔案上呼叫move_uploaded_file()的安全和推薦的替代方法提供。實現將根據環境檢測正確的操作。
  • getStream()將返回一個StreamInterface例項。在非SAPI環境中,一種建議的可能性是將單個上載檔案解析為php:// temp streams而不是直接解析到檔案;在這種情況下,不存在上傳檔案。因此,無論環境如何,getStream()都可以保證工作。

例如:

// Move a file to an upload directory
$filename = sprintf(
    '%s.%s',
    create_uuid(),
    pathinfo($file0->getClientFilename(), PATHINFO_EXTENSION)
);
$file0->moveTo(DATA_DIR . '/' . $filename);

// Stream a file to Amazon S3.
// Assume $s3wrapper is a PHP stream that will write to S3, and that
// Psr7StreamWrapper is a class that will decorate a StreamInterface as a PHP
// StreamWrapper.
$stream = new Psr7StreamWrapper($file1->getStream());
stream_copy_to_stream($stream, $s3wrapper);

 2 包裝

所描述的介面和類作為psr / http-message包的一部分提供。