# HTTP 消息接口
此文檔描述了[RFC 7230](http://tools.ietf.org/html/rfc7230)和
[RFC 7231](http://tools.ietf.org/html/rfc7231)HTTP 消息傳遞的接口,還有[RFC 3986](http://tools.ietf.org/html/rfc3986)里對 HTTP 消息的 URIs 使用。
HTTP 消息是 Web 技術發展的基礎。瀏覽器或 HTTP 客戶端如`curl`生成發送 HTTP 請求消息到 Web 服務器,Web 服務器響應 HTTP 請求。服務端的代碼接受 HTTP 請求消息后返回 HTTP 響應消息。
通常 HTTP 消息對于終端用戶來說是不可見的,但是作為 Web 開發者,我們需要知道 HTTP 機制,如何發起、構建、取用還有操縱 HTTP 消息,知道這些原理,以助我們剛好的完成開發任務,無論這個任務是發起一個 HTTP 請求,或者處理傳入的請求。
每一個 HTTP 請求都有專屬的格式:
~~~http
POST /path HTTP/1.1
Host: example.com
foo=bar&baz=bat
~~~
按照順序,第一行的各個字段意義為: HTTP 請求方法、請求的目標地址(通常是一個絕對路徑的 URI 或者路徑),HTTP 協議。接下來是 HTTP 頭信息,在這個例子中:目的主機。接下來是空行,然后是消息內容。
HTTP 返回消息有類似的結構:
~~~http
HTTP/1.1 200 OK
Content-Type: text/plain
這是返回的消息內容
~~~
按照順序,第一行為狀態行,包括 HTTP 協議版本,HTTP 狀態碼,描述文本。和 HTTP 請求類似的,接下來是 HTTP 頭信息,在這個例子中:內容類型。接下來是空行,然后是消息內容。
此文檔探討的是 HTTP 請求消息接口,和構建 HTTP 消息需要的元素數據定義。
本文件中的`必須`,`不得`,`需要`,`應`,`不應`,`應該`,`不應該`,`推薦`,`可能`和`可選`等能愿動詞按照?[RFC 2119](http://www.ietf.org/rfc/rfc2119.txt)中的描述進行解釋。
### 參考文獻
* [RFC 2119](http://tools.ietf.org/html/rfc2119)
* [RFC 3986](http://tools.ietf.org/html/rfc3986)
* [RFC 7230](http://tools.ietf.org/html/rfc7230)
* [RFC 7231](http://tools.ietf.org/html/rfc7231)
## 1\. 詳細描述
### 1.1 消息
一個 HTTP 消息是指來自于客戶端到服務端的請求或者服務端到客戶端的響應。以下這兩個文檔分別為 HTTP 的消息接口做了詳細定義`Psr\Http\Message\RequestInterface`和`Psr\Http\Message\ResponseInterface`。
`Psr\Http\Message\RequestInterface`?和?`Psr\Http\Message\ResponseInterface`繼承于`Psr\Http\Message\MessageInterface`。當接口?`Psr\Http\Message\MessageInterface`?可能被直接實現的時候,實現者應該實現?`Psr\Http\Message\RequestInterface`?接口和?`Psr\Http\Message\ResponseInterface`接口。
從這里開始,當描述這些接口時,命名空間`Psr\Http\Message`?將會被省略。
### 1.2 HTTP 請求頭信息
#### 大小寫不敏感的字段名字
HTTP 消息包含大小寫不敏感頭信息。使用`MessageInterface`接口來設置和獲取頭信息,大小寫不敏感的定義在于,如果你設置了一個`Foo`的頭信息,`foo`的值會被重寫,你也可以通過`foo`來拿到`FoO`頭對應的值。
~~~php
$message = $message->withHeader('foo', 'bar');
echo $message->getHeaderLine('foo');
// 輸出: bar
echo $message->getHeaderLine('FOO');
// 輸出: bar
$message = $message->withHeader('fOO', 'baz');
echo $message->getHeaderLine('foo');
// 輸出: baz
~~~
雖然頭信息可以用大小寫不敏感的方式取出,但是接口實現類**必須**保持自己的大小寫規范,特別是用`getHeaders()`方法輸出的內容。
因為一些非標準的 HTTP 應用程序,可能會依賴于大小寫敏感的頭信息,所有在此我們把主宰 HTTP 大小寫的權利開放出來,以適用不同的場景。
#### 對應多條數組的頭信息
為了適用一個 HTTP 「鍵」可以對應多條數據的情況,我們使用字符串配合數組來實現,你可以從一個`MessageInterface`取出數組或字符串,使用`getHeaderLine($name)`方法可以獲取通過逗號分割的不區分大小寫的字符串形式的所有值。也可以通過`getHeader($name)`獲取數組形式頭信息的所有值。
~~~php
$message = $message
->withHeader('foo', 'bar')
->withAddedHeader('foo', 'baz');
$header = $message->getHeaderLine('foo');
// $header 包含: 'bar, baz'
$header = $message->getHeader('foo');
// ['bar', 'baz']
~~~
注意:并不是所有的頭信息都可以適用逗號分割(例如`Set-Cookie`),當處理這種頭信息時候,`MessageInterace`的繼承類**應該**使用`getHeader($name)`方法來獲取這種多值的情況。
#### 主機信息
在請求中,`Host`頭信息通常和 URI 的 host 信息,還有建立起 TCP 連接使用的 Host 信息一致。然而,HTTP 標準規范允許主機`host`信息與其他兩個不一樣。
在構建請求的時候,如果`host`頭信息未提供的話,實現類庫**必須**嘗試著從 URI 中提取`host`信息。
`RequestInterface::withUri()`會默認的,從傳參的`UriInterface`實例中提取`host`,并替代請求中原有的`host`信息。
你可以提供傳參第二個參數為`true`來保證返回的消息實例中,原有的`host`頭信息不會被替代掉。
以下表格說明了當`withUri()`的第二個參數被設置為`true`的時,返回的消息實例中調用`getHeaderLine('Host')`方法會返回的內容:
| 請求 Host 頭信息[1](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message.md#rhh) | 請求 URI 中的 Host 信息[2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message.md#rhc) | 傳參進去 URI 的 Host[3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message.md#uhc) | 結果 |
| --- | --- | --- | --- |
| '' | '' | '' | '' |
| '' | foo.com | '' | foo.com |
| '' | foo.com | bar.com | foo.com |
| foo.com | '' | bar.com | foo.com |
| foo.com | bar.com | baz.com | foo.com |
* 1?當前請求的`Host`頭信息。
* 2?當前請求`URI`中的`Host`信息。
* 3?通過`withUri()`傳參進入的 URI 中的`host`信息。
### 1.3 數據流
HTTP 消息包含開始的一行、頭信息、還有消息的內容。HTTP 的消息內容有時候可以很小,有時候確是非常巨大。嘗試使用字符串的形式來展示消息內容,會消耗大量的內存,使用數據流的形式來讀取消息
可以解決此問題。`StreamInterface`接口用來隱藏具體的數據流讀寫實現。在一些情況下,消息類型的讀取方式為字符串是能容許的,可以使用`php://memory`或者`php://temp`。
`StreamInterface`暴露出來幾個接口,這些接口允許你讀取、寫入,還有高效的遍歷內容。
數據流使用這個三個接口來闡明對他們的操作能力:`isReadable()`、`isWritable()`和`isSeekable()`。這些方法可以讓數據流的操作者得知數據流能否能提供他們想要的功能。
每一個數據流的實例,都會有多種功能:可以只讀、可以只寫、可以讀和寫,可以隨機讀取,可以按順序讀取等。
最終,`StreamInterface`定義了一個`__toString()`的方法,用來一次性以字符串的形式輸出所有消息內容。
與請求和響應的接口不同的是,`StreamInterface`并不強調不可修改性。因為在 PHP 的實現內,基本上沒有辦法保證不可修改性,因為指針的指向,內容的變更等狀態,都是不可控的。作為讀取者,可以
調用只讀的方法來返回數據流,以最大程度上保證數據流的不可修改性。使用者要時刻明確的知道數據流的可修改性,建議把數據流附加到消息實例中,來強迫不可修改的特性。
### 1.4 請求目標和 URI
根據 RFC7230,請求消息包含「請求目標」做為請求行的第二個段落。請求目標可以是以下形式之一:
* **原始形式**,由路徑和查詢字符串(如果存在)組成;這通常被稱為相對 URL。通過 TCP 傳輸的消息通常是原始形式;scheme 和認證數據通常僅通過 CGI 變量存在。
* **絕對形式**,包括 scheme 、認證數據(「\[user-info@\] host \[:port\]」,其中括號中的項是可選的),路徑(如果存在),查詢字符串(如果存在)。這通常被稱為絕對 URI,并且是 RFC 3986 中詳細說明的唯一指定 URI 的形式。這個形式通常在向 HTTP 代理發出請求時使用。
* **認證形式**,只包含認證信息。通常僅用于從 HTTP 客戶端和代理服務器之間建立連接請求時使用。
* **星號形式**,僅由字符串`*`組成,并與 OPTIONS 方法一起使用,以確定 Web 服務器的性能。
除了這些請求目標之外,通常還有一個不同于請求目標的「有效 URL」。有效 URL 不在 HTTP 消息中傳輸,但它用于確定發出請求的協議(Http 或 Https)、端口和主機名。
有效 URL 由`UriInterface`接口表示。`UriInterface`是 RFC 3986 (主要用例)中指定的 HTTP 和 HTTPS URI 的模型。該接口提供了與各種 URI 部分交互的方法,這將消除重復解析 URI 的需要。還定義了一個`__toString()`方法,用于將建模的 URI 轉換為其字符串表示形式。
當使用`getRequestTarget()`方法檢索請求目標時,默認情況下此方法將使用 URI 對象并提取所有必要的組件來構建*原始形式*。*原始形式*是迄今為止最常見的請求目標。
如果用戶希望使用其他三種形式中,或者如果想要顯式覆蓋請求目標,則可以使用`withRequestTarget()`來實現。
調用此方法不會影響 URI,因為 URI 是從`getUri()`返回的。
例如,用戶可能想要向服務器發起一個星號形式的請求:
~~~php
$request = $request
->withMethod('OPTIONS')
->withRequestTarget('*')
->withUri(new Uri('https://example.org/'));
~~~
這個示例最終可能會導致 HTTP 請求類似下例:
~~~source-httpspec
OPTIONS * HTTP/1.1
~~~
但是 HTTP 客戶端將能夠使用有效的 URL (來自`getUri()`)來確定協議、主機名和 TCP 端口號。
一個 HTTP 客戶端`必須`忽略`Uri::getPath()`和`Uri::getQuery()`的值,而是用`getRequestTarget()`返回的值,默認為連接前面兩個值。
選擇未實現上面四種請求目標形式的客戶端,`必須`依然使用`getRequestTarget()`。這些客戶端`必須`拒絕它們不支持的請求目標,并且`不得`依賴于`getUri()`的值。
`RequestInterface`提供了檢索請求目標或用提供的請求目標創建一個新實例的方法。默認情況下,如果實例中沒有專門組合請求目標,`getRequestTarget()`將會返回組合 URI 的原始形式(如果沒有組成 URI 則返回「/」)。`withRequestTarget($requestTarget)`使用指定的請求目標創建一個新實例,從而允許開發人員創建表示其他三個請求目標形式(絕對形式、認證形式和星號形式)。使用時,組合的 URI 實例仍然可以使用,特別是在客戶端中,它可以用于創建與服務器的連接。
### 1.5 服務端請求
`RequestInterface`提供了 HTTP 請求消息的通常表示形式。但是,由于服務器端環境的性質,服務器端請求需要額外的處理。服務器端處理需要考慮通用網關接口( CGI ),更具體地說,需要考慮 PHP 通過其服務器 API ( SAPI )對 CGI 的抽象和擴展。PHP 通過超級全局變量提供了關于輸入編組的簡化,例如:
* `$_COOKIE`,反序列化了 HTTP cookie,并提供了簡化的訪問方式。
* `$_GET`,反序列化了查詢字符串并提供了簡化的訪問方式。
* `$_POST`,對通過 urlencode 編碼提交的 HTTP POST 信息進化反序列化并提供了簡化的訪問方式;通常可以認為是解析消息體的結果。
* `$_FILES`,關于文件上傳的元數據反序列化結果。
* `$_SERVER`,提供了 CGI/SAPI 環境變量的訪問,這些變量通常包括請求方法、請求 scheme、請求 URI 和報頭。
`ServerRequestInterface`繼承于`RequestInterface`,提供圍繞這些超全局變量的抽象訪問。這種做法有助于減少開發人員對超全局的耦合,鼓勵對代碼的測試,并提升了測試人員對相應代碼的測試能力。
服務器請求提供了一個附加的屬性,「attributes」,以便于開發人員可以根據應用程序的特定規則(例如路徑匹配、scheme 匹配、主機匹配等)自檢、分解和匹配請求。這樣,服務器請求還可以在多段請求邏輯中進行消息傳遞。
### 1.6 文件上傳
`ServerRequestInterface`指定了一種在規范化結構中檢索上傳文件樹的方法,每個葉子都是一個`UploadedFileInterface`的實例。
超全局變量`$_FILES`在處理文件數組式的時候存在一些眾所周知的問題。具體而言,頁面的表單里有多個 input 框,name 屬性是`files[]`,然后提交文件,PHP 的`$_FILES`變量形式如下:
~~~php
array(
'files' => array(
'name' => array(
0 => 'file0.txt',
1 => 'file1.html',
),
'type' => array(
0 => 'text/plain',
1 => 'text/html',
),
/* 等等其他屬性 */
),
)
~~~
而不是預期的:
~~~php
array(
'files' => array(
0 => array(
'name' => 'file0.txt',
'type' => 'text/plain',
/* 等等其他屬性 */
),
1 => array(
'name' => 'file1.html',
'type' => 'text/html',
/* 等等其他屬性 */
),
),
)
~~~
這樣造成的結果是開發人員必須知道這種語言實現細節,并為之編寫特定的代碼。
另外,如果發生以下情況,`$_FILES`會是空數組:
* HTTP 方法不是`POST`。
* 單元測試的時候。
* 在非 SAPI 環境下運行的時候,比如[ReactPHP](http://reactphp.org/)。
在這些情況下,數據需要以不同的方式獲取。比如:
* 進程可以解析消息體來發現上傳的文件。這種情況下,實現方式可以選擇不將上傳文件寫入文件系統,而是將它們包裝在流中以減少內存、I/O 和存儲開銷。
* 在單元測試的場景下,開發人員需要能夠對文件上樁或模仿的方式來驗證和檢查不同場景的情況。
`getUploadedFiles()`將為開發者提供規范化的結構。實現方式的返回定義是:
* 聚合上傳文件的所有信息,并填充`Psr\Http\Message\UploadedFileInterface`實例。
* 重新創建提交的樹結構,相應位置的葉結點都是一個適當的`Psr\Http\Message\UploadedFileInterface`實例。
引用的樹結構`應該`模仿提交的文件結構。
在最簡單的示例中,這可能是單個被命名的提交表單元素:
~~~html
<input type="file" name="avatar" />
~~~
在這種情況下,`$_FILES`的結構如下:
~~~php
array(
'avatar' => array(
'tmp_name' => 'phpUxcOty',
'name' => 'my-avatar.png',
'size' => 90996,
'type' => 'image/png',
'error' => 0,
),
)
~~~
`getUploadedFiles()`返回的規范化形式將是:
~~~php
array(
'avatar' => /* UploadedFileInterface 實例 */
)
~~~
input 名稱是一種數組表示形式的情況:
~~~html
<input type="file" name="my-form[details][avatar]" />
~~~
`$_FILES`最終看下來像是這樣的:
~~~php
array(
'my-form' => array(
'details' => array(
'avatar' => array(
'tmp_name' => 'phpUxcOty',
'name' => 'my-avatar.png',
'size' => 90996,
'type' => 'image/png',
'error' => 0,
),
),
),
)
~~~
`getUploadedFiles()`的返回結果`應該`是:
~~~php
array(
'my-form' => array(
'details' => array(
'avatar' => /* UploadedFileInterface 實例 */
),
),
)
~~~
在某些情況下,可以指定文件的 input 為一個數組:
~~~html
Upload an avatar: <input type="file" name="my-form[details][avatars][]" />
Upload an avatar: <input type="file" name="my-form[details][avatars][]" />
~~~
(例如,JavaScript 控件可能會產生額外的文件上傳輸入,以允許一次上傳多個文件。)
這種情況下,其實現`必須`按給定的索引聚合所有上傳文件的信息。因為這種情況下的`$_FILES`偏離了正常結構:
~~~php
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()`返回的如下結構:
~~~php
array(
'my-form' => array(
'details' => array(
'avatars' => array(
0 => /* UploadedFileInterface 實例 */,
1 => /* UploadedFileInterface 實例 */,
2 => /* UploadedFileInterface 實例 */,
),
),
),
)
~~~
開發人員可以用以下形式訪問嵌套數組的索引`1`:
~~~php
$request->getUploadedFiles()['my-form']['details']['avatars'][1];
~~~
因為上傳的文件數據是派生的(派生于`$_FILES`或請求體),所以接口還有一個設置方法`withUploadedFiles()`,允許修改其內容。
在原始示例的情形下,接口調用者的代碼可能如下所示:
~~~php
$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 環境中運行。 As such,`UploadedFileInterface`provides methods for ensuring operations will work regardless of environment. 特別是:
* `moveTo($targetPath)`用來做為一個安全且推薦的代替在臨時上傳文件上調用`move_uploaded_file()`的方法。實現將根據環境檢查正確的操作。
* `getStream()`將會返回一個`StreamInterface`實例。在非 SAPI 環境中,提出的一種可能性是將單個上傳文件解析為`php://temp`流而不是直接解析到文件;在這種情況下,不存在上傳文件。 因此,無論環境如何,`getStream()`都可以保證工作。
例如:
~~~php
// 移動文件至上傳目錄
$filename = sprintf(
'%s.%s',
create_uuid(),
pathinfo($file0->getClientFilename(), PATHINFO_EXTENSION)
);
$file0->moveTo(DATA_DIR . '/' . $filename);
// 將文件流式傳輸至 Amazon S3。
// 假設 $s3wrapper 是一個將寫入 S3 的 PHP 流,而 Psr7StreamWrapper 是一個將 StreamInterface 作為 PHP StreamWrapper 進行裝飾的類。
$stream = new Psr7StreamWrapper($file1->getStream());
stream_copy_to_stream($stream, $s3wrapper);
~~~
### 3.2?`Psr\Http\Message\RequestInterface`
~~~php
<?php
namespace Psr\Http\Message;
/**
* 代表客戶端向服務器發起請求的 HTTP 消息對象。
*
* 根據 HTTP 規范,此接口包含以下屬性:
*
* - HTTP 協議版本號
* - HTTP 請求方法
* - URI
* - 報頭信息
* - 消息內容
*
* 在構造 HTTP 請求對象的時候,如果沒有提供 Host 信息,
* 實現類庫 **必須** 從給出的 URI 中去提取 Host 信息。
*
* HTTP 請求是被視為無法修改的,所有能修改狀態的方法,都 **必須** 有一套機制,在內部保
* 持好原有的內容,然后把修改狀態后的新的 HTTP 請求實例返回。
*/
interface RequestInterface extends MessageInterface
{
/**
* 獲取消息的請求目標。
*
* 獲取消息的請求目標的使用場景,可能是在客戶端,也可能是在服務器端,也可能是在指定信息的時候
* (參閱下方的 `withRequestTarget()`)。
*
* 在大部分情況下,此方法會返回組合 URI 的原始形式,除非被指定過(參閱下方的 `withRequestTarget()`)。
*
* 如果沒有可用的 URI,并且沒有設置過請求目標,此方法 **必須** 返回 「/」。
*
* @return string
*/
public function getRequestTarget();
/**
* 返回一個指定目標的請求實例。
*
* 如果請求需要非原始形式的請求目標——例如指定絕對形式、認證形式或星號形式——則此方法
* 可用于創建指定請求目標的實例。
*
* 此方法在實現的時候,**必須** 保留原有的不可修改的 HTTP 請求實例,然后返回
* 一個新的修改過的 HTTP 請求實例。
*
* @see [http://tools.ietf.org/html/rfc7230#section-2.7](http://tools.ietf.org/html/rfc7230#section-2.7)
* (關于請求目標的各種允許的格式)
*
* @param mixed $requestTarget
* @return self
*/
public function withRequestTarget($requestTarget);
/**
* 獲取當前請求使用的 HTTP 方法
*
* @return string HTTP 方法字符串
*/
public function getMethod();
/**
* 返回更改了請求方法的消息實例。
*
* 雖然,在大部分情況下,HTTP 請求方法都是使用大寫字母來標示的,但是,實現類庫 **不應該**
* 修改用戶傳參的大小格式。
*
* 此方法在實現的時候,**必須** 保留原有的不可修改的 HTTP 請求實例,然后返回
* 一個新的修改過的 HTTP 請求實例。
*
* @param string $method 大小寫敏感的方法名
* @return self
* @throws \InvalidArgumentException 當非法的 HTTP 方法名傳入時會拋出異常。
*/
public function withMethod($method);
/**
* 獲取 URI 實例。
*
* 此方法 **必須** 返回 `UriInterface` 的 URI 實例。
*
* @see http://tools.ietf.org/html/rfc3986#section-4.3
* @return UriInterface 返回與當前請求相關的 `UriInterface` 類型的 URI 實例。
*/
public function getUri();
/**
* 返回修改了 URI 的消息實例。
*
* 當傳入的 URI 包含有 HOST 信息時,此方法 **必須** 更新 HOST 信息。如果 URI
* 實例沒有附帶 HOST 信息,任何之前存在的 HOST 信息 **必須** 作為候補,應用
* 更改到返回的消息實例里。
*
* 你可以通過傳入第二個參數來,來干預方法的處理,當 `$preserveHost` 設置為 `true`
* 的時候,會保留原來的 HOST 信息。當 `$preserveHost` 設置為 `true` 時,此方法
* 會如下處理 HOST 信息:
*
* - 如果 HOST 信息不存在或為空,并且新 URI 包含 HOST 信息,則此方法 **必須** 更新返回請求中的 HOST 信息。
* - 如果 HOST 信息不存在或為空,并且新 URI 不包含 HOST 信息,則此方法 **不得** 更新返回請求中的 HOST 信息。
* - 如果HOST 信息存在且不為空,則此方法 **不得** 更新返回請求中的 HOST 信息。
*
* 此方法在實現的時候,**必須** 保留原有的不可修改的 HTTP 請求實例,然后返回
* 一個新的修改過的 HTTP 請求實例。
*
* @see http://tools.ietf.org/html/rfc3986#section-4.3
* @param UriInterface $uri `UriInterface` 新的 URI 實例
* @param bool $preserveHost 是否保留原有的 HOST 頭信息
* @return self
*/
public function withUri(UriInterface $uri, $preserveHost = false);
}
~~~
#### 3.2.1?`Psr\Http\Message\ServerRequestInterface`
~~~php
<?php
namespace Psr\Http\Message;
/**
* 表示服務器端接收到的 HTTP 請求。
*
* 根據 HTTP 規范,此接口包含以下屬性:
*
* - HTTP 協議版本號
* - HTTP 請求方法
* - URI
* - 報頭信息
* - 消息內容
*
* 此外,它封閉了從 CGI 和/或 PHP 環境變量,包括:
*
* - `$_SERVER` 中表示的值。
* - 提供的任意 Cookie 信息(通常通過 `$_COOKIE` 獲取)
* - 查詢字符串參數(通常通過 `$_GET` 獲取,或者通過 `parse_str()` 解析)
* - 如果存在的話,上傳文件的信息(通常通過 `$_FILES` 獲取)
* - 反序列化的消息體參數(通常來自于 `$_POST`)
*
* `$_SERVER` 的值 **必須** 被視為不可變的,因為代表了請求時應用程序的狀態;因此,沒有允許修改的方法。
* 其他值則提供了修改的方法,因為可以從 `$_SERVER` 或請求體中恢復,并且可能在應用程序中被處理
* (比如可能根據內容類型對消息體參數進行反序列化)。
*
* 此外,這個接口要識別請求的擴展信息和匹配其他的參數。
* (例如,通過 URI 進行路徑匹配,解析 Cookie 值,反序列化非表單編碼的消息體,報頭中的用戶名進行匹配認證)
* 這些參數存儲在「attributes」中。
*
* HTTP 請求是被視為無法修改的,所有能修改狀態的方法,都 **必須** 有一套機制,在內部保
* 持好原有的內容,然后把修改狀態后的,新的 HTTP 請求實例返回。
*/
interface ServerRequestInterface extends RequestInterface
{
/**
* 返回服務器參數。
*
* 返回與請求環境相關的數據,通常從 PHP 的 `$_SERVER` 超全局變量中獲取,但不是必然的。
*
* @return array
*/
public function getServerParams();
/**
* 獲取 Cookie 數據。
*
* 獲取從客戶端發往服務器的 Cookie 數據。
*
* 這個數據的結構 **必須** 和超全局變量 `$_COOKIE` 兼容。
*
* @return array
*/
public function getCookieParams();
/**
* 返回具體指定 Cookie 的實例。
*
* 這個數據不是一定要來源于 `$_COOKIE`,但是 **必須** 與之結構兼容。通常在實例化時注入。
*
* 這個方法 **禁止** 更新實例中的 Cookie 報頭和服務器參數中的相關值。
*
* 此方法在實現的時候,**必須** 保留原有的不可修改的 HTTP 消息實例,然后返回
* 一個新的修改過的 HTTP 消息實例。
*
* @param array $cookies 表示 Cookie 的鍵值對。
* @return self
*/
public function withCookieParams(array $cookies);
/**
* 獲取查詢字符串參數。
*
* 如果可以的話,返回反序列化的查詢字符串參數。
*
* 注意:查詢參數可能與 URI 或服務器參數不同步。如果你需要確保只獲取原始值,則可能需要調用
* `getUri()->getQuery()` 或服務器參數中的 `QUERY_STRING` 獲取原始的查詢字符串并自行解析。
*
* @return array
*/
public function getQueryParams();
/**
* 返回具體指定查詢字符串參數的實例。
*
* 這些值 **應該** 在傳入請求的閉包中保持不變。它們 **可能** 在實例化的時候注入,
* 例如來自 `$_GET` 或者其他一些值(例如 URI)中得到。如果是通過解析 URI 獲取,則
* 數據結構必須與 `parse_str()` 返回的內容兼容,以便處理查詢參數、嵌套的代碼可以復用。
*
* 設置查詢字符串參數 **不得** 更改存儲的 URI 和服務器參數中的值。
*
* 此方法在實現的時候,**必須** 保留原有的不可修改的 HTTP 消息實例,然后返回
* 一個新的修改過的 HTTP 消息實例。
*
* @param array $query 查詢字符串參數數組,通常來源于 `$_GET`。
* @return self
*/
public function withQueryParams(array $query);
/**
* 獲取規范化的上傳文件數據。
*
* 這個方法會規范化返回的上傳文件元數據樹結構,每個葉子結點都是 `Psr\Http\Message\UploadedFileInterface` 實例。
*
* 這些值 **可能** 在實例化的時候從 `$_FILES` 或消息體中獲取,或者通過 `withUploadedFiles()` 獲取。
*
* @return array `UploadedFileInterface` 的實例數組;如果沒有數據則必須返回一個空數組。
*/
public function getUploadedFiles();
/**
* 返回使用指定的上傳文件數據的新實例。
*
* 此方法在實現的時候,**必須** 保留原有的不可修改的 HTTP 消息實例,然后返回
* 一個新的修改過的 HTTP 消息實例。
*
* @param array `UploadedFileInterface` 實例的樹結構,類似于 `getUploadedFiles()` 的返回值。
* @return self
* @throws \InvalidArgumentException 如果提供無效的結構時拋出。
*/
public function withUploadedFiles(array $uploadedFiles);
/**
* 獲取請求消息體中的參數。
*
* 如果請求的 Content-Type 是 application/x-www-form-urlencoded 或 multipart/form-data 且請求方法是 POST,
* 則此方法 **必須** 返回 $_POST 的內容。
*
* 如果是其他情況,此方法可能返回反序列化請求正文內容的任何結果;當解析返回返回的結構化內容時,潛在的類型 **必須**
* 只能是數組或 `object` 類型。`null` 表示沒有消息體內容。
*
* @return null|array|object 如果存在則返回反序列化消息體參數。一般是一個數組或 `object`。
*/
public function getParsedBody();
/**
* 返回具有指定消息體參數的實例。
*
* **可能** 在實例化時注入。
*
* 如果請求的 Content-Type 是 application/x-www-form-urlencoded 或 multipart/form-data 且請求方法是 POST,
* 則方法的參數只能是 $_POST。
*
* 數據不一定要來自 $_POST,但是 **必須** 是反序列化請求正文內容的結果。由于需要反序列化/解析返回的結構化數據,
* 所以這個方法只接受數組、 `object` 類型和 `null`(如果沒有可用的數據解析)。
*
* 例如,如果確定請求數據是一個 JSON,可以使用此方法創建具有反序列化參數的請求實例。
*
* 此方法在實現的時候,**必須** 保留原有的不可修改的 HTTP 消息實例,然后返回
* 一個新的修改過的 HTTP 消息實例。
*
* @param null|array|object $data 反序列化的消息體數據,通常是數組或 `object`。
* @return self
* @throws \InvalidArgumentException 如果提供的數據類型不支持。
*/
public function withParsedBody($data);
/**
* 獲取從請求派生的屬性。
*
* 請求「attributes」可用于從請求導出的任意參數:比如路徑匹配操作的結果;解密 Cookie 的結果;
* 反序列化非表單編碼的消息體的結果;屬性將是應用程序與請求特定的,并且可以是可變的。
*
* @return mixed[] 從請求派生的屬性。
*/
public function getAttributes();
/**
* 獲取單個派生的請求屬性。
*
* 獲取 getAttributes() 中聲明的某一個屬性,如果不存在則返回提供的默認值。
*
* 這個方法不需要 hasAttribute 方法,因為允許在找不到指定屬性的時候返回默認值。
*
* @see getAttributes()
* @param string $name 屬性名稱。
* @param mixed $default 如果屬性不存在時返回的默認值。
* @return mixed
*/
public function getAttribute($name, $default = null);
/**
* 返回具有指定派生屬性的實例。
*
* 此方法允許設置 getAttributes() 中聲明的單個派生的請求屬性。
*
* 此方法在實現的時候,**必須** 保留原有的不可修改的 HTTP 消息實例,然后返回
* 一個新的修改過的 HTTP 消息實例。
*
* @see getAttributes()
* @param string $name 屬性名。
* @param mixed $value 屬性值。
* @return self
*/
public function withAttribute($name, $value);
/**
* 返回移除指定屬性的實例。
*
* 此方法允許移除 getAttributes() 中聲明的單個派生的請求屬性。
*
* 此方法在實現的時候,**必須** 保留原有的不可修改的 HTTP 消息實例,然后返回
* 一個新的修改過的 HTTP 消息實例。
*
* @see getAttributes()
* @param string $name 屬性名。
* @return self
*/
public function withoutAttribute($name);
}
~~~
- 前言
- 捐贈ESD項目
- 使用篇-通用
- 環境
- 安裝
- 規范
- 壓力測試
- 配置
- 如何設置YML配置
- server配置
- 端口配置
- 項目結構
- 事件派發
- 日志
- 注解
- DI容器
- 自定義進程
- 并發及協程池
- Console插件
- Scheduled插件
- Redis插件
- AOP插件
- Saber插件
- Mysql插件
- mysql事務
- Actuator插件
- Whoops插件
- Cache插件
- PHPUnit插件
- Security插件
- Session插件
- EasyRoute插件
- http路由
- ProcessRpc插件
- AutoReload插件
- AnnotationsScan插件
- Tracing-plugin插件
- MQTT插件
- Pack插件
- AMQP插件
- Validate插件
- Uid插件
- Topic插件
- Blade插件
- CsvReader插件
- hashed-wheel-timer-plugin插件
- 使用篇-HTTP
- 路由
- 靜態文件
- 路由定義
- 修飾方法
- 路由分組
- 資源路由
- 端口作用域
- 異常處理
- 跨域請求
- 路由緩存
- 控制器
- 控制器初始化
- 前置操作
- 跳轉和重定向
- 異常處理
- 請求
- 請求對象
- 請求信息
- request消息
- response消息
- stream消息
- url接口
- 驗證器
- 內置驗證器
- 內置過濾器
- 使用篇-WS
- 如何使用
- 路由
- 使用篇-TCP
- 插件篇-PluginSystem
- 微服務篇-ESDCloud
- CircuitBreaker插件
- SaberCloud插件
- 分布式鏈路追蹤系統
- Consul插件