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包的一部分提供。