PSR-7 HTTP 消息接口规范

HTTP 消息接口

此文档描述了 RFC 7230

RFC 7231 HTTP 消息传递的接口,还有 RFC 3986 里对 HTTP 消息的 URIs 使用。

HTTP 消息是 Web 技术发展的基础。浏览器或 HTTP 客户端如 curl 生成发送 HTTP 请求消息到 Web 服务器,Web 服务器响应 HTTP 请求。服务端的代码接受 HTTP 请求消息后返回 HTTP 响应消息。

通常 HTTP 消息对于终端用户来说是不可见的,但是作为 Web 开发者,我们需要知道 HTTP 机制,如何发起、构建、取用还有操纵 HTTP 消息,知道这些原理,以助我们更好的完成开发任务,无论这个任务是发起一个 HTTP 请求,或者处理传入的请求。

每一个 HTTP 请求都有专属的格式:

  1. POST /path HTTP/1.1
  2. Host: example.com
  3. foo=bar&baz=bat

按照顺序,第一行的各个字段意义为: HTTP 请求方法、请求的目标地址(通常是一个绝对路径的 URI 或者路径),HTTP 协议。接下来是 HTTP 头信息,在这个例子中:目的主机。接下来是空行,然后是消息内容。

HTTP 返回消息有类似的结构:

  1. HTTP/1.1 200 OK
  2. Content-Type: text/plain
  3. 这是返回的消息内容

按照顺序,第一行为状态行,包括 HTTP 协议版本,HTTP 状态码,描述文本。和 HTTP 请求类似的,接下来是 HTTP 头信息,在这个例子中:内容类型。接下来是空行,然后是消息内容。

此文档探讨的是 HTTP 请求消息接口,和构建 HTTP 消息需要的元素数据定义。

本文件中的 必须不得需要不应应该不应该推荐可能可选 等能愿动词按照 RFC 2119 中的描述进行解释。

参考文献

1. 详细描述

1.1 消息

一个 HTTP 消息是指来自于客户端到服务端的请求或者服务端到客户端的响应。以下这两个文档分别为 HTTP 的消息接口做了详细定义 Psr\Http\Message\RequestInterfacePsr\Http\Message\ResponseInterface

Psr\Http\Message\RequestInterfacePsr\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 头对应的值。

  1. $message = $message->withHeader('foo', 'bar');
  2. echo $message->getHeaderLine('foo');
  3. // 输出: bar
  4. echo $message->getHeaderLine('FOO');
  5. // 输出: bar
  6. $message = $message->withHeader('fOO', 'baz');
  7. echo $message->getHeaderLine('foo');
  8. // 输出: baz

虽然头信息可以用大小写不敏感的方式取出,但是接口实现类 必须 保持自己的大小写规范,特别是用 getHeaders() 方法输出的内容。

因为一些非标准的 HTTP 应用程序,可能会依赖于大小写敏感的头信息,所以在此我们把主宰 HTTP 大小写的权利开放出来,以适用不同的场景。

对应多条数组的头信息

为了适用一个 HTTP 「键」可以对应多条数据的情况,我们使用字符串配合数组来实现,你可以从一个 MessageInterface 取出数组或字符串,使用 getHeaderLine($name) 方法可以获取通过逗号分割的不区分大小写的字符串形式的所有值。也可以通过 getHeader($name) 获取数组形式头信息的所有值。

  1. $message = $message
  2. ->withHeader('foo', 'bar')
  3. ->withAddedHeader('foo', 'baz');
  4. $header = $message->getHeaderLine('foo');
  5. // $header 包含: 'bar, baz'
  6. $header = $message->getHeader('foo');
  7. // ['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 请求 URI 中的 Host 信息2 传参进去 URI 的 Host3 结果
‘’ ‘’ ‘’ ‘’
‘’ 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() 返回的。

例如,用户可能想要向服务器发起一个星号形式的请求:

  1. $request = $request
  2. ->withMethod('OPTIONS')
  3. ->withRequestTarget('*')
  4. ->withUri(new Uri('https://example.org/'));

这个示例最终可能会导致 HTTP 请求类似下例:

  1. 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 变量形式如下:

  1. array(
  2. 'files' => array(
  3. 'name' => array(
  4. 0 => 'file0.txt',
  5. 1 => 'file1.html',
  6. ),
  7. 'type' => array(
  8. 0 => 'text/plain',
  9. 1 => 'text/html',
  10. ),
  11. /* 等等其他属性 */
  12. ),
  13. )

而不是预期的:

  1. array(
  2. 'files' => array(
  3. 0 => array(
  4. 'name' => 'file0.txt',
  5. 'type' => 'text/plain',
  6. /* 等等其他属性 */
  7. ),
  8. 1 => array(
  9. 'name' => 'file1.html',
  10. 'type' => 'text/html',
  11. /* 等等其他属性 */
  12. ),
  13. ),
  14. )

这样造成的结果是开发人员必须知道这种语言实现细节,并为之编写特定的代码。

另外,如果发生以下情况, $_FILES 会是空数组:

  • HTTP 方法不是 POST
  • 单元测试的时候。
  • 在非 SAPI 环境下运行的时候,比如 ReactPHP

在这些情况下,数据需要以不同的方式获取。比如:

  • 进程可以解析消息体来发现上传的文件。这种情况下,实现方式可以选择不将上传文件写入文件系统,而是将它们包装在流中以减少内存、I/O 和存储开销。
  • 在单元测试的场景下,开发人员需要能够对文件上桩或模仿的方式来验证和检查不同场景的情况。

getUploadedFiles() 将为开发者提供规范化的结构。实现方式的返回定义是:

  • 聚合上传文件的所有信息,并填充 Psr\Http\Message\UploadedFileInterface 实例。
  • 重新创建提交的树结构,相应位置的叶结点都是一个适当的 Psr\Http\Message\UploadedFileInterface 实例。

引用的树结构 应该 模仿提交的文件结构。

在最简单的示例中,这可能是单个被命名的提交表单元素:

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

在这种情况下,$_FILES 的结构如下:

  1. array(
  2. 'avatar' => array(
  3. 'tmp_name' => 'phpUxcOty',
  4. 'name' => 'my-avatar.png',
  5. 'size' => 90996,
  6. 'type' => 'image/png',
  7. 'error' => 0,
  8. ),
  9. )

getUploadedFiles() 返回的规范化形式将是:

  1. array(
  2. 'avatar' => /* UploadedFileInterface 实例 */
  3. )

input 名称是一种数组表示形式的情况:

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

$_FILES 最终看下来像是这样的:

  1. array(
  2. 'my-form' => array(
  3. 'details' => array(
  4. 'avatar' => array(
  5. 'tmp_name' => 'phpUxcOty',
  6. 'name' => 'my-avatar.png',
  7. 'size' => 90996,
  8. 'type' => 'image/png',
  9. 'error' => 0,
  10. ),
  11. ),
  12. ),
  13. )

getUploadedFiles() 的返回结果 应该 是:

  1. array(
  2. 'my-form' => array(
  3. 'details' => array(
  4. 'avatar' => /* UploadedFileInterface 实例 */
  5. ),
  6. ),
  7. )

在某些情况下,可以指定文件的 input 为一个数组:

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

(例如,JavaScript 控件可能会产生额外的文件上传输入,以允许一次上传多个文件。)

这种情况下,其实现 必须 按给定的索引聚合所有上传文件的信息。因为这种情况下的 $_FILES 偏离了正常结构:

  1. array(
  2. 'my-form' => array(
  3. 'details' => array(
  4. 'avatars' => array(
  5. 'tmp_name' => array(
  6. 0 => '...',
  7. 1 => '...',
  8. 2 => '...',
  9. ),
  10. 'name' => array(
  11. 0 => '...',
  12. 1 => '...',
  13. 2 => '...',
  14. ),
  15. 'size' => array(
  16. 0 => '...',
  17. 1 => '...',
  18. 2 => '...',
  19. ),
  20. 'type' => array(
  21. 0 => '...',
  22. 1 => '...',
  23. 2 => '...',
  24. ),
  25. 'error' => array(
  26. 0 => '...',
  27. 1 => '...',
  28. 2 => '...',
  29. ),
  30. ),
  31. ),
  32. ),
  33. )

上面的 $_FILES 将对应于 getUploadedFiles() 返回的如下结构:

  1. array(
  2. 'my-form' => array(
  3. 'details' => array(
  4. 'avatars' => array(
  5. 0 => /* UploadedFileInterface 实例 */,
  6. 1 => /* UploadedFileInterface 实例 */,
  7. 2 => /* UploadedFileInterface 实例 */,
  8. ),
  9. ),
  10. ),
  11. )

开发人员可以用以下形式访问嵌套数组的索引 1

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

因为上传的文件数据是派生的(派生于 $_FILES 或请求体),所以接口还有一个设置方法 withUploadedFiles(),允许修改其内容。

在原始示例的情形下,接口调用者的代码可能如下所示:

  1. $file0 = $request->getUploadedFiles()['files'][0];
  2. $file1 = $request->getUploadedFiles()['files'][1];
  3. printf(
  4. "Received the files %s and %s",
  5. $file0->getClientFilename(),
  6. $file1->getClientFilename()
  7. );
  8. // "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() 都可以保证工作。

例如:

  1. // 移动文件至上传目录
  2. $filename = sprintf(
  3. '%s.%s',
  4. create_uuid(),
  5. pathinfo($file0->getClientFilename(), PATHINFO_EXTENSION)
  6. );
  7. $file0->moveTo(DATA_DIR . '/' . $filename);
  8. // 将文件流式传输至 Amazon S3。
  9. // 假设 $s3wrapper 是一个将写入 S3 的 PHP 流,而 Psr7StreamWrapper 是一个将 StreamInterface 作为 PHP StreamWrapper 进行装饰的类。
  10. $stream = new Psr7StreamWrapper($file1->getStream());
  11. stream_copy_to_stream($stream, $s3wrapper);

2. 扩展包

上面讨论的接口和类库已经整合成为扩展包:psr/http-message

3. 接口

3.1 Psr\Http\Message\MessageInterface

  1. <?php
  2. namespace Psr\Http\Message;
  3. /**
  4. *
  5. * HTTP 消息包括客户端向服务器发起的「请求」和服务器端返回给客户端的「响应」。
  6. * 此接口定义了他们通用的方法。
  7. *
  8. * HTTP 消息是被视为无法修改的,所有能修改状态的方法,都 **必须** 有一套
  9. * 机制,在内部保持好原有的内容,然后把修改状态后的信息返回。
  10. *
  11. * @see http://www.ietf.org/rfc/rfc7230.txt
  12. * @see http://www.ietf.org/rfc/rfc7231.txt
  13. */
  14. interface MessageInterface
  15. {
  16. /**
  17. * 获取字符串形式的 HTTP 协议版本信息。
  18. *
  19. * 字符串 **必须** 包含 HTTP 版本数字(如:「1.1」, 「1.0」)。
  20. *
  21. * @return string HTTP 协议版本
  22. */
  23. public function getProtocolVersion();
  24. /**
  25. * 返回指定 HTTP 版本号的消息实例。
  26. *
  27. * 传参的版本号只 **必须** 包含 HTTP 版本数字,如:"1.1", "1.0"。
  28. *
  29. * 此方法在实现的时候,**必须** 保留原有的不可修改的 HTTP 消息对象,然后返回
  30. * 一个新的带有传参进去的 HTTP 版本的实例
  31. *
  32. * @param string $version HTTP 版本信息
  33. * @return self
  34. */
  35. public function withProtocolVersion($version);
  36. /**
  37. * 获取所有的报头信息
  38. *
  39. * 返回的二维数组中,第一维数组的「键」代表单条报头信息的名字,「值」是
  40. * 以数组形式返回的,见以下实例:
  41. *
  42. * // 把「值」的数据当成字串打印出来
  43. * foreach ($message->getHeaders() as $name => $values) {
  44. * echo $name . ': ' . implode(', ', $values);
  45. * }
  46. *
  47. * // 迭代的循环二维数组
  48. * foreach ($message->getHeaders() as $name => $values) {
  49. * foreach ($values as $value) {
  50. * header(sprintf('%s: %s', $name, $value), false);
  51. * }
  52. * }
  53. *
  54. * 虽然报头信息是没有大小写之分,但是使用 `getHeaders()` 会返回保留了原本
  55. * 大小写形式的内容。
  56. *
  57. * @return string[][] 返回一个两维数组,第一维数组的「键」 **必须** 为单条报头信息的
  58. * 名称,对应的是由字串组成的数组,请注意,对应的「值」 **必须** 是数组形式的。
  59. */
  60. public function getHeaders();
  61. /**
  62. * 检查是否报头信息中包含有此名称的值,不区分大小写
  63. *
  64. * @param string $name 不区分大小写的报头信息名称
  65. * @return bool 找到返回 true,未找到返回 false
  66. */
  67. public function hasHeader($name);
  68. /**
  69. * 根据给定的名称,获取一条报头信息,不区分大小写,以数组形式返回
  70. *
  71. * 此方法以数组形式返回对应名称的报头信息。
  72. *
  73. * 如果没有对应的报头信息,**必须** 返回一个空数组。
  74. *
  75. * @param string $name 不区分大小写的报头字段名称。
  76. * @return string[] 返回报头信息中,对应名称的,由字符串组成的数组值,如果没有对应
  77. * 的内容,**必须** 返回空数组。
  78. */
  79. public function getHeader($name);
  80. /**
  81. * 根据给定的名称,获取一条报头信息,不区分大小写,以逗号分隔的形式返回
  82. *
  83. * 此方法返回所有对应的报头信息,并将其使用逗号分隔的方法拼接起来。
  84. *
  85. * 注意:不是所有的报头信息都可使用逗号分隔的方法来拼接,对于那些报头信息,请使用
  86. * `getHeader()` 方法来获取。
  87. *
  88. * 如果没有对应的报头信息,此方法 **必须** 返回一个空字符串。
  89. *
  90. * @param string $name 不区分大小写的报头字段名称。
  91. * @return string 返回报头信息中,对应名称的,由逗号分隔组成的字串,如果没有对应
  92. * 的内容,**必须** 返回空字符串。
  93. */
  94. public function getHeaderLine($name);
  95. /**
  96. * 返回替换指定报头信息「键/值」对的消息实例。
  97. *
  98. * 虽然报头信息是不区分大小写的,但是此方法必须保留其传参时的大小写状态,并能够在
  99. * 调用 `getHeaders()` 的时候被取出。
  100. *
  101. * 此方法在实现的时候,**必须** 保留原有的不可修改的 HTTP 消息对象,然后返回
  102. * 一个更新后带有传参进去报头信息的实例
  103. *
  104. * @param string $name 不区分大小写的报头字段名称。
  105. * @param string|string[] $value 报头信息或报头信息数组。
  106. * @return self
  107. * @throws \InvalidArgumentException 无效的报头字段或报头信息时抛出
  108. */
  109. public function withHeader($name, $value);
  110. /**
  111. * 返回一个报头信息增量的 HTTP 消息实例。
  112. *
  113. * 原有的报头信息会被保留,新的值会作为增量加上,如果报头信息不存在的话,字段会被加上。
  114. *
  115. * 此方法在实现的时候,**必须** 保留原有的不可修改的 HTTP 消息对象,然后返回
  116. * 一个新的修改过的 HTTP 消息实例。
  117. *
  118. * @param string $name 不区分大小写的报头字段名称。
  119. * @param string|string[] $value 报头信息或报头信息数组。
  120. * @return self
  121. * @throws \InvalidArgumentException 报头字段名称非法时会被抛出。
  122. * @throws \InvalidArgumentException 报头头信息的值非法的时候会被抛出。
  123. */
  124. public function withAddedHeader($name, $value);
  125. /**
  126. * 返回被移除掉指定报头信息的 HTTP 消息实例。
  127. *
  128. * 报头信息字段在解析的时候,**必须** 保证是不区分大小写的。
  129. *
  130. * 此方法在实现的时候,**必须** 保留原有的不可修改的 HTTP 消息对象,然后返回
  131. * 一个新的修改过的 HTTP 消息实例。
  132. *
  133. * @param string $name 不区分大小写的头部字段名称。
  134. * @return self
  135. */
  136. public function withoutHeader($name);
  137. /**
  138. * 获取 HTTP 消息的内容。
  139. *
  140. * @return StreamInterface 以数据流的形式返回。
  141. */
  142. public function getBody();
  143. /**
  144. * 返回指定内容的 HTTP 消息实例。
  145. *
  146. * 内容 **必须** 是 `StreamInterface` 接口的实例。
  147. *
  148. * 此方法在实现的时候,**必须** 保留原有的不可修改的 HTTP 消息对象,然后返回
  149. * 一个新的修改过的 HTTP 消息实例。
  150. *
  151. * @param StreamInterface $body 数据流形式的内容。
  152. * @return self
  153. * @throws \InvalidArgumentException 当消息内容不正确的时候抛出。
  154. */
  155. public function withBody(StreamInterface $body);
  156. }

3.2 Psr\Http\Message\RequestInterface

  1. <?php
  2. namespace Psr\Http\Message;
  3. /**
  4. * 代表客户端向服务器发起请求的 HTTP 消息对象。
  5. *
  6. * 根据 HTTP 规范,此接口包含以下属性:
  7. *
  8. * - HTTP 协议版本号
  9. * - HTTP 请求方法
  10. * - URI
  11. * - 报头信息
  12. * - 消息内容
  13. *
  14. * 在构造 HTTP 请求对象的时候,如果没有提供 Host 信息,
  15. * 实现类库 **必须** 从给出的 URI 中去提取 Host 信息。
  16. *
  17. * HTTP 请求是被视为无法修改的,所有能修改状态的方法,都 **必须** 有一套机制,在内部保
  18. * 持好原有的内容,然后把修改状态后的新的 HTTP 请求实例返回。
  19. */
  20. interface RequestInterface extends MessageInterface
  21. {
  22. /**
  23. * 获取消息的请求目标。
  24. *
  25. * 获取消息的请求目标的使用场景,可能是在客户端,也可能是在服务器端,也可能是在指定信息的时候
  26. * (参阅下方的 `withRequestTarget()`)。
  27. *
  28. * 在大部分情况下,此方法会返回组合 URI 的原始形式,除非被指定过(参阅下方的 `withRequestTarget()`)。
  29. *
  30. * 如果没有可用的 URI,并且没有设置过请求目标,此方法 **必须** 返回 「/」。
  31. *
  32. * @return string
  33. */
  34. public function getRequestTarget();
  35. /**
  36. * 返回一个指定目标的请求实例。
  37. *
  38. * 如果请求需要非原始形式的请求目标——例如指定绝对形式、认证形式或星号形式——则此方法
  39. * 可用于创建指定请求目标的实例。
  40. *
  41. * 此方法在实现的时候,**必须** 保留原有的不可修改的 HTTP 请求实例,然后返回
  42. * 一个新的修改过的 HTTP 请求实例。
  43. *
  44. * @see [http://tools.ietf.org/html/rfc7230#section-2.7](http://tools.ietf.org/html/rfc7230#section-2.7)
  45. * (关于请求目标的各种允许的格式)
  46. *
  47. * @param mixed $requestTarget
  48. * @return self
  49. */
  50. public function withRequestTarget($requestTarget);
  51. /**
  52. * 获取当前请求使用的 HTTP 方法
  53. *
  54. * @return string HTTP 方法字符串
  55. */
  56. public function getMethod();
  57. /**
  58. * 返回更改了请求方法的消息实例。
  59. *
  60. * 虽然,在大部分情况下,HTTP 请求方法都是使用大写字母来标示的,但是,实现类库 **不应该**
  61. * 修改用户传参的大小格式。
  62. *
  63. * 此方法在实现的时候,**必须** 保留原有的不可修改的 HTTP 请求实例,然后返回
  64. * 一个新的修改过的 HTTP 请求实例。
  65. *
  66. * @param string $method 大小写敏感的方法名
  67. * @return self
  68. * @throws \InvalidArgumentException 当非法的 HTTP 方法名传入时会抛出异常。
  69. */
  70. public function withMethod($method);
  71. /**
  72. * 获取 URI 实例。
  73. *
  74. * 此方法 **必须** 返回 `UriInterface` 的 URI 实例。
  75. *
  76. * @see http://tools.ietf.org/html/rfc3986#section-4.3
  77. * @return UriInterface 返回与当前请求相关的 `UriInterface` 类型的 URI 实例。
  78. */
  79. public function getUri();
  80. /**
  81. * 返回修改了 URI 的消息实例。
  82. *
  83. * 当传入的 URI 包含有 HOST 信息时,此方法 **必须** 更新 HOST 信息。如果 URI
  84. * 实例没有附带 HOST 信息,任何之前存在的 HOST 信息 **必须** 作为候补,应用
  85. * 更改到返回的消息实例里。
  86. *
  87. * 你可以通过传入第二个参数来,来干预方法的处理,当 `$preserveHost` 设置为 `true`
  88. * 的时候,会保留原来的 HOST 信息。当 `$preserveHost` 设置为 `true` 时,此方法
  89. * 会如下处理 HOST 信息:
  90. *
  91. * - 如果 HOST 信息不存在或为空,并且新 URI 包含 HOST 信息,则此方法 **必须** 更新返回请求中的 HOST 信息。
  92. * - 如果 HOST 信息不存在或为空,并且新 URI 不包含 HOST 信息,则此方法 **不得** 更新返回请求中的 HOST 信息。
  93. * - 如果HOST 信息存在且不为空,则此方法 **不得** 更新返回请求中的 HOST 信息。
  94. *
  95. * 此方法在实现的时候,**必须** 保留原有的不可修改的 HTTP 请求实例,然后返回
  96. * 一个新的修改过的 HTTP 请求实例。
  97. *
  98. * @see http://tools.ietf.org/html/rfc3986#section-4.3
  99. * @param UriInterface $uri `UriInterface` 新的 URI 实例
  100. * @param bool $preserveHost 是否保留原有的 HOST 头信息
  101. * @return self
  102. */
  103. public function withUri(UriInterface $uri, $preserveHost = false);
  104. }

3.2.1 Psr\Http\Message\ServerRequestInterface

  1. <?php
  2. namespace Psr\Http\Message;
  3. /**
  4. * 表示服务器端接收到的 HTTP 请求。
  5. *
  6. * 根据 HTTP 规范,此接口包含以下属性:
  7. *
  8. * - HTTP 协议版本号
  9. * - HTTP 请求方法
  10. * - URI
  11. * - 报头信息
  12. * - 消息内容
  13. *
  14. * 此外,它封闭了从 CGI 和/或 PHP 环境变量,包括:
  15. *
  16. * - `$_SERVER` 中表示的值。
  17. * - 提供的任意 Cookie 信息(通常通过 `$_COOKIE` 获取)
  18. * - 查询字符串参数(通常通过 `$_GET` 获取,或者通过 `parse_str()` 解析)
  19. * - 如果存在的话,上传文件的信息(通常通过 `$_FILES` 获取)
  20. * - 反序列化的消息体参数(通常来自于 `$_POST`)
  21. *
  22. * `$_SERVER` 的值 **必须** 被视为不可变的,因为代表了请求时应用程序的状态;因此,没有允许修改的方法。
  23. * 其他值则提供了修改的方法,因为可以从 `$_SERVER` 或请求体中恢复,并且可能在应用程序中被处理
  24. * (比如可能根据内容类型对消息体参数进行反序列化)。
  25. *
  26. * 此外,这个接口要识别请求的扩展信息和匹配其他的参数。
  27. * (例如,通过 URI 进行路径匹配,解析 Cookie 值,反序列化非表单编码的消息体,报头中的用户名进行匹配认证)
  28. * 这些参数存储在「attributes」中。
  29. *
  30. * HTTP 请求是被视为无法修改的,所有能修改状态的方法,都 **必须** 有一套机制,在内部保
  31. * 持好原有的内容,然后把修改状态后的,新的 HTTP 请求实例返回。
  32. */
  33. interface ServerRequestInterface extends RequestInterface
  34. {
  35. /**
  36. * 返回服务器参数。
  37. *
  38. * 返回与请求环境相关的数据,通常从 PHP 的 `$_SERVER` 超全局变量中获取,但不是必然的。
  39. *
  40. * @return array
  41. */
  42. public function getServerParams();
  43. /**
  44. * 获取 Cookie 数据。
  45. *
  46. * 获取从客户端发往服务器的 Cookie 数据。
  47. *
  48. * 这个数据的结构 **必须** 和超全局变量 `$_COOKIE` 兼容。
  49. *
  50. * @return array
  51. */
  52. public function getCookieParams();
  53. /**
  54. * 返回具体指定 Cookie 的实例。
  55. *
  56. * 这个数据不是一定要来源于 `$_COOKIE`,但是 **必须** 与之结构兼容。通常在实例化时注入。
  57. *
  58. * 这个方法 **禁止** 更新实例中的 Cookie 报头和服务器参数中的相关值。
  59. *
  60. * 此方法在实现的时候,**必须** 保留原有的不可修改的 HTTP 消息实例,然后返回
  61. * 一个新的修改过的 HTTP 消息实例。
  62. *
  63. * @param array $cookies 表示 Cookie 的键值对。
  64. * @return self
  65. */
  66. public function withCookieParams(array $cookies);
  67. /**
  68. * 获取查询字符串参数。
  69. *
  70. * 如果可以的话,返回反序列化的查询字符串参数。
  71. *
  72. * 注意:查询参数可能与 URI 或服务器参数不同步。如果你需要确保只获取原始值,则可能需要调用
  73. * `getUri()->getQuery()` 或服务器参数中的 `QUERY_STRING` 获取原始的查询字符串并自行解析。
  74. *
  75. * @return array
  76. */
  77. public function getQueryParams();
  78. /**
  79. * 返回具体指定查询字符串参数的实例。
  80. *
  81. * 这些值 **应该** 在传入请求的闭包中保持不变。它们 **可能** 在实例化的时候注入,
  82. * 例如来自 `$_GET` 或者其他一些值(例如 URI)中得到。如果是通过解析 URI 获取,则
  83. * 数据结构必须与 `parse_str()` 返回的内容兼容,以便处理查询参数、嵌套的代码可以复用。
  84. *
  85. * 设置查询字符串参数 **不得** 更改存储的 URI 和服务器参数中的值。
  86. *
  87. * 此方法在实现的时候,**必须** 保留原有的不可修改的 HTTP 消息实例,然后返回
  88. * 一个新的修改过的 HTTP 消息实例。
  89. *
  90. * @param array $query 查询字符串参数数组,通常来源于 `$_GET`。
  91. * @return self
  92. */
  93. public function withQueryParams(array $query);
  94. /**
  95. * 获取规范化的上传文件数据。
  96. *
  97. * 这个方法会规范化返回的上传文件元数据树结构,每个叶子结点都是 `Psr\Http\Message\UploadedFileInterface` 实例。
  98. *
  99. * 这些值 **可能** 在实例化的时候从 `$_FILES` 或消息体中获取,或者通过 `withUploadedFiles()` 获取。
  100. *
  101. * @return array `UploadedFileInterface` 的实例数组;如果没有数据则必须返回一个空数组。
  102. */
  103. public function getUploadedFiles();
  104. /**
  105. * 返回使用指定的上传文件数据的新实例。
  106. *
  107. * 此方法在实现的时候,**必须** 保留原有的不可修改的 HTTP 消息实例,然后返回
  108. * 一个新的修改过的 HTTP 消息实例。
  109. *
  110. * @param array `UploadedFileInterface` 实例的树结构,类似于 `getUploadedFiles()` 的返回值。
  111. * @return self
  112. * @throws \InvalidArgumentException 如果提供无效的结构时抛出。
  113. */
  114. public function withUploadedFiles(array $uploadedFiles);
  115. /**
  116. * 获取请求消息体中的参数。
  117. *
  118. * 如果请求的 Content-Type 是 application/x-www-form-urlencoded 或 multipart/form-data 且请求方法是 POST,
  119. * 则此方法 **必须** 返回 $_POST 的内容。
  120. *
  121. * 如果是其他情况,此方法可能返回反序列化请求正文内容的任何结果;当解析返回返回的结构化内容时,潜在的类型 **必须**
  122. * 只能是数组或 `object` 类型。`null` 表示没有消息体内容。
  123. *
  124. * @return null|array|object 如果存在则返回反序列化消息体参数。一般是一个数组或 `object`。
  125. */
  126. public function getParsedBody();
  127. /**
  128. * 返回具有指定消息体参数的实例。
  129. *
  130. * **可能** 在实例化时注入。
  131. *
  132. * 如果请求的 Content-Type 是 application/x-www-form-urlencoded 或 multipart/form-data 且请求方法是 POST,
  133. * 则方法的参数只能是 $_POST。
  134. *
  135. * 数据不一定要来自 $_POST,但是 **必须** 是反序列化请求正文内容的结果。由于需要反序列化/解析返回的结构化数据,
  136. * 所以这个方法只接受数组、 `object` 类型和 `null`(如果没有可用的数据解析)。
  137. *
  138. * 例如,如果确定请求数据是一个 JSON,可以使用此方法创建具有反序列化参数的请求实例。
  139. *
  140. * 此方法在实现的时候,**必须** 保留原有的不可修改的 HTTP 消息实例,然后返回
  141. * 一个新的修改过的 HTTP 消息实例。
  142. *
  143. * @param null|array|object $data 反序列化的消息体数据,通常是数组或 `object`。
  144. * @return self
  145. * @throws \InvalidArgumentException 如果提供的数据类型不支持。
  146. */
  147. public function withParsedBody($data);
  148. /**
  149. * 获取从请求派生的属性。
  150. *
  151. * 请求「attributes」可用于从请求导出的任意参数:比如路径匹配操作的结果;解密 Cookie 的结果;
  152. * 反序列化非表单编码的消息体的结果;属性将是应用程序与请求特定的,并且可以是可变的。
  153. *
  154. * @return mixed[] 从请求派生的属性。
  155. */
  156. public function getAttributes();
  157. /**
  158. * 获取单个派生的请求属性。
  159. *
  160. * 获取 getAttributes() 中声明的某一个属性,如果不存在则返回提供的默认值。
  161. *
  162. * 这个方法不需要 hasAttribute 方法,因为允许在找不到指定属性的时候返回默认值。
  163. *
  164. * @see getAttributes()
  165. * @param string $name 属性名称。
  166. * @param mixed $default 如果属性不存在时返回的默认值。
  167. * @return mixed
  168. */
  169. public function getAttribute($name, $default = null);
  170. /**
  171. * 返回具有指定派生属性的实例。
  172. *
  173. * 此方法允许设置 getAttributes() 中声明的单个派生的请求属性。
  174. *
  175. * 此方法在实现的时候,**必须** 保留原有的不可修改的 HTTP 消息实例,然后返回
  176. * 一个新的修改过的 HTTP 消息实例。
  177. *
  178. * @see getAttributes()
  179. * @param string $name 属性名。
  180. * @param mixed $value 属性值。
  181. * @return self
  182. */
  183. public function withAttribute($name, $value);
  184. /**
  185. * 返回移除指定属性的实例。
  186. *
  187. * 此方法允许移除 getAttributes() 中声明的单个派生的请求属性。
  188. *
  189. * 此方法在实现的时候,**必须** 保留原有的不可修改的 HTTP 消息实例,然后返回
  190. * 一个新的修改过的 HTTP 消息实例。
  191. *
  192. * @see getAttributes()
  193. * @param string $name 属性名。
  194. * @return self
  195. */
  196. public function withoutAttribute($name);
  197. }

3.3 Psr\Http\Message\ResponseInterface

  1. <?php
  2. namespace Psr\Http\Message;
  3. /**
  4. * 表示服务器返回的响应消息。
  5. *
  6. * 根据 HTTP 规范,此接口包含以下各项的属性:
  7. *
  8. * - 协议版本
  9. * - 状态码和原因短语
  10. * - 报头
  11. * - 消息体
  12. *
  13. * HTTP 响应是被视为无法修改的,所有能修改状态的方法,都 **必须** 有一套机制,在内部保
  14. * 持好原有的内容,然后把修改状态后的,新的 HTTP 响应实例返回。
  15. */
  16. interface ResponseInterface extends MessageInterface
  17. {
  18. /**
  19. * 获取响应状态码。
  20. *
  21. * 状态码是一个三位整数,用于理解请求。
  22. *
  23. * @return int 状态码。
  24. */
  25. public function getStatusCode();
  26. /**
  27. * 返回具有指定状态码和原因短语(可选)的实例。
  28. *
  29. * 如果未指定原因短语,实现代码 **可能** 选择 RFC7231 或 IANA 为状态码推荐的原因短语。
  30. *
  31. * 此方法在实现的时候,**必须** 保留原有的不可修改的 HTTP 消息实例,然后返回
  32. * 一个新的修改过的 HTTP 消息实例。
  33. *
  34. * @see http://tools.ietf.org/html/rfc7231#section-6
  35. * @see http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
  36. * @param int $code 三位整数的状态码。
  37. * @param string $reasonPhrase 为状态码提供的原因短语;如果未提供,实现代码可以使用 HTTP 规范建议的默认代码。
  38. * @return self
  39. * @throws \InvalidArgumentException 如果传入无效的状态码,则抛出。
  40. */
  41. public function withStatus($code, $reasonPhrase = '');
  42. /**
  43. * 获取与响应状态码关联的响应原因短语。
  44. *
  45. * 因为原因短语不是响应状态行中的必需元素,所以原因短语 **可能** 是空。
  46. * 实现代码可以选择返回响应的状态代码的默认 RFC 7231 推荐原因短语(或 IANA HTTP 状态码注册表中列出的原因短语)。
  47. *
  48. * @see http://tools.ietf.org/html/rfc7231#section-6
  49. * @see http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
  50. * @return string 原因短语;如果不存在,则 **必须** 返回空字符串。
  51. */
  52. public function getReasonPhrase();
  53. }

3.4 Psr\Http\Message\StreamInterface

  1. <?php
  2. namespace Psr\Http\Message;
  3. /**
  4. * 描述数据流。
  5. *
  6. * 通常,实例将包装PHP流; 此接口提供了最常见操作的包装,包括将整个流序列化为字符串。
  7. */
  8. interface StreamInterface
  9. {
  10. /**
  11. * 从头到尾将流中的所有数据读取到字符串。
  12. *
  13. * 这个方法 **必须** 在开始读数据前定位到流的开头,并读取出所有的数据。
  14. *
  15. * 警告:这可能会尝试将大量数据加载到内存中。
  16. *
  17. * 这个方法 **不得** 抛出异常以符合 PHP 的字符串转换操作。
  18. *
  19. * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
  20. * @return string
  21. */
  22. public function __toString();
  23. /**
  24. * 关闭流和任何底层资源。
  25. *
  26. * @return void
  27. */
  28. public function close();
  29. /**
  30. * 从流中分离任何底层资源。
  31. *
  32. * 分离之后,流处于不可用状态。
  33. *
  34. * @return resource|null 如果存在的话,返回底层 PHP 流。
  35. */
  36. public function detach();
  37. /**
  38. * 如果可知,获取流的数据大小。
  39. *
  40. * @return int|null 如果可知,返回以字节为单位的大小,如果未知返回 `null`。
  41. */
  42. public function getSize();
  43. /**
  44. * 返回当前读/写的指针位置。
  45. *
  46. * @return int 指针位置。
  47. * @throws \RuntimeException 产生错误时抛出。
  48. */
  49. public function tell();
  50. /**
  51. * 返回是否位于流的末尾。
  52. *
  53. * @return bool
  54. */
  55. public function eof();
  56. /**
  57. * 返回流是否可随机读取。
  58. *
  59. * @return bool
  60. */
  61. public function isSeekable();
  62. /**
  63. * 定位流中的指定位置。
  64. *
  65. * @see http://www.php.net/manual/en/function.fseek.php
  66. * @param int $offset 要定位的流的偏移量。
  67. * @param int $whence 指定如何根据偏移量计算光标位置。有效值与 PHP 内置函数 `fseek()` 相同。
  68. * SEEK_SET:设定位置等于 $offset 字节。默认。
  69. * SEEK_CUR:设定位置为当前位置加上 $offset。
  70. * SEEK_END:设定位置为文件末尾加上 $offset (要移动到文件尾之前的位置,offset 必须是一个负值)。
  71. * @throws \RuntimeException 失败时抛出。
  72. */
  73. public function seek($offset, $whence = SEEK_SET);
  74. /**
  75. * 定位流的起始位置。
  76. *
  77. * 如果流不可以随机访问,此方法将引发异常;否则将执行 seek(0)。
  78. *
  79. * @see seek()
  80. * @see http://www.php.net/manual/en/function.fseek.php
  81. * @throws \RuntimeException 失败时抛出。
  82. */
  83. public function rewind();
  84. /**
  85. * 返回流是否可写。
  86. *
  87. * @return bool
  88. */
  89. public function isWritable();
  90. /**
  91. * 向流中写数据。
  92. *
  93. * @param string $string 要写入流的数据。
  94. * @return int 返回写入流的字节数。
  95. * @throws \RuntimeException 失败时抛出。
  96. */
  97. public function write($string);
  98. /**
  99. * 返回流是否可读。
  100. *
  101. * @return bool
  102. */
  103. public function isReadable();
  104. /**
  105. * 从流中读取数据。
  106. *
  107. * @param int $length 从流中读取最多 $length 字节的数据并返回。如果数据不足,则可能返回少于
  108. * $length 字节的数据。
  109. * @return string 返回从流中读取的数据,如果没有可用的数据则返回空字符串。
  110. * @throws \RuntimeException 失败时抛出。
  111. */
  112. public function read($length);
  113. /**
  114. * 返回字符串中的剩余内容。
  115. *
  116. * @return string
  117. * @throws \RuntimeException 如果无法读取则抛出异常。
  118. * @throws \RuntimeException 如果在读取时发生错误则抛出异常。
  119. */
  120. public function getContents();
  121. /**
  122. * 获取流中的元数据作为关联数组,或者检索指定的键。
  123. *
  124. * 返回的键与从 PHP 的 stream_get_meta_data() 函数返回的键相同。
  125. *
  126. * @see http://php.net/manual/en/function.stream-get-meta-data.php
  127. * @param string $key 要检索的特定元数据。
  128. * @return array|mixed|null 如果没有键,则返回关联数组。如果提供了键并且找到值,
  129. * 则返回特定键值;如果未找到键,则返回 null。
  130. */
  131. public function getMetadata($key = null);
  132. }

3.5 Psr\Http\Message\UriInterface

  1. <?php
  2. namespace Psr\Http\Message;
  3. /**
  4. * URI 数据对象。
  5. *
  6. * 此接口按照 RFC 3986 来构建 HTTP URI,提供了一些通用的操作,你可以自由的对此接口
  7. * 进行扩展。你可以使用此 URI 接口来做 HTTP 相关的操作,也可以使用此接口做任何 URI
  8. * 相关的操作。
  9. *
  10. * 此接口的实例化对象被视为无法修改的,所有能修改状态的方法,都 **必须** 有一套机制,在内部保
  11. * 持好原有的内容,然后把修改状态后的,新的实例返回。
  12. *
  13. * 通常,HOST 信息也将出现在请求消息中。对于服务器端的请求,通常可以在服务器参数中发现此信息。
  14. *
  15. * @see [URI 通用标准规范](http://tools.ietf.org/html/rfc3986)
  16. */
  17. interface UriInterface
  18. {
  19. /**
  20. * 从 URI 中取出 scheme。
  21. *
  22. * 如果不存在 Scheme,此方法 **必须** 返回空字符串。
  23. *
  24. * 根据 RFC 3986 规范 3.1 章节,返回的数据 **必须** 是小写字母。
  25. *
  26. * 最后部分的「:」字串不属于 Scheme,**不得** 作为返回数据的一部分。
  27. *
  28. * @see https://tools.ietf.org/html/rfc3986#section-3.1
  29. * @return string URI Ccheme 的值。
  30. */
  31. public function getScheme();
  32. /**
  33. * 返回 URI 认证信息。
  34. *
  35. * 如果没有 URI 认证信息的话,**必须** 返回一个空字符串。
  36. *
  37. * URI 的认证信息语法是:
  38. *
  39. * <pre>
  40. * [user-info@]host[:port]
  41. * </pre>
  42. *
  43. * 如果端口部分没有设置,或者端口不是标准端口,**不应该** 包含在返回值内。
  44. *
  45. * @see https://tools.ietf.org/html/rfc3986#section-3.2
  46. * @return string URI 认证信息,格式为:「[user-info@]host[:port]」。
  47. */
  48. public function getAuthority();
  49. /**
  50. * 从 URI 中获取用户信息。
  51. *
  52. * 如果不存在用户信息,此方法 **必须** 返回一个空字符串。
  53. *
  54. * 如果 URI 中存在用户,则返回该值;此外,如果密码也存在,它将附加到用户值,用冒号(「:」)分隔。
  55. *
  56. * 用户信息后面跟着的 "@" 字符,不是用户信息里面的一部分,**不得** 在返回值里出现。
  57. *
  58. * @return string URI 的用户信息,格式:"username[:password]"
  59. */
  60. public function getUserInfo();
  61. /**
  62. * 从 URI 中获取 HOST 信息。
  63. *
  64. * 如果 URI 中没有此值,**必须** 返回空字符串。
  65. *
  66. * 根据 RFC 3986 规范 3.2.2 章节,返回的数据 **必须** 是小写字母。
  67. *
  68. * @see http://tools.ietf.org/html/rfc3986#section-3.2.2
  69. * @return string URI 中的 HOST 信息。
  70. */
  71. public function getHost();
  72. /**
  73. * 从 URI 中获取端口信息。
  74. *
  75. * 如果端口信息是与当前 Scheme 的标准端口不匹配的话,就使用整数值的格式返回,如果是一
  76. * 样的话,**应该** 返回 `null` 值。
  77. *
  78. * 如果不存在端口和 Scheme 信息,**必须** 返回 `null` 值。
  79. *
  80. * 如果不存在端口数据,但是存在 Scheme 的话,**可能** 返回 Scheme 对应的
  81. * 标准端口,但是 **应该** 返回 `null`。
  82. *
  83. * @return null|int URI 中的端口信息。
  84. */
  85. public function getPort();
  86. /**
  87. * 从 URI 中获取路径信息。
  88. *
  89. * 路径可以是空的,或者是绝对的(以斜线「/」开头),或者相对路径(不以斜线开头)。
  90. * 实现 **必须** 支持所有三种语法。
  91. *
  92. * 根据 RFC 7230 第 2.7.3 节,通常空路径「」和绝对路径「/」被认为是相同的。
  93. * 但是这个方法 **不得** 自动进行这种规范化,因为在具有修剪的基本路径的上下文中,
  94. * 例如前端控制器中,这种差异将变得显著。用户的任务就是可以将「」和「/」都处理好。
  95. *
  96. * 返回的值 **必须** 是百分号编码,但 **不得** 对任何字符进行双重编码。
  97. * 要确定要编码的字符,请参阅 RFC 3986 第 2 节和第 3.3 节。
  98. *
  99. * 例如,如果值包含斜线(「/」)而不是路径段之间的分隔符,则该值必须以编码形式(例如「%2F」)
  100. * 传递给实例。
  101. *
  102. * @see https://tools.ietf.org/html/rfc3986#section-2
  103. * @see https://tools.ietf.org/html/rfc3986#section-3.3
  104. * @return string URI 路径信息。
  105. */
  106. public function getPath();
  107. /**
  108. * 获取 URI 中的查询字符串。
  109. *
  110. * 如果不存在查询字符串,则此方法必须返回空字符串。
  111. *
  112. * 前导的「?」字符不是查询字符串的一部分,**不得** 添加在返回值中。
  113. *
  114. * 返回的值 **必须** 是百分号编码,但 **不得** 对任何字符进行双重编码。
  115. * 要确定要编码的字符,请参阅 RFC 3986 第 2 节和第 3.4 节。
  116. *
  117. * 例如,如果查询字符串的键值对中的值包含不做为值之间分隔符的(「&」),则该值必须
  118. * 以编码形式传递(例如「%26」)到实例。
  119. *
  120. * @see https://tools.ietf.org/html/rfc3986#section-2
  121. * @see https://tools.ietf.org/html/rfc3986#section-3.4
  122. * @return string URI 中的查询字符串
  123. */
  124. public function getQuery();
  125. /**
  126. * 获取 URI 中的片段(Fragment)信息。
  127. *
  128. * 如果没有片段信息,此方法 **必须** 返回空字符串。
  129. *
  130. * 前导的「#」字符不是片段的一部分,**不得** 添加在返回值中。
  131. *
  132. * 返回的值 **必须** 是百分号编码,但 **不得** 对任何字符进行双重编码。
  133. * 要确定要编码的字符,请参阅 RFC 3986 第 2 节和第 3.5 节。
  134. *
  135. * @see https://tools.ietf.org/html/rfc3986#section-2
  136. * @see https://tools.ietf.org/html/rfc3986#section-3.5
  137. * @return string URI 中的片段信息。
  138. */
  139. public function getFragment();
  140. /**
  141. * 返回具有指定 Scheme 的实例。
  142. *
  143. * 此方法 **必须** 保留当前实例的状态,并返回包含指定 Scheme 的实例。
  144. *
  145. * 实现 **必须** 支持大小写不敏感的「http」和「https」的 Scheme,并且在
  146. * 需要的时候 **可能** 支持其他的 Scheme。
  147. *
  148. * 空的 Scheme 相当于删除 Scheme。
  149. *
  150. * @param string $scheme 给新实例使用的 Scheme。
  151. * @return self 具有指定 Scheme 的新实例。
  152. * @throws \InvalidArgumentException 使用无效的 Scheme 时抛出。
  153. * @throws \InvalidArgumentException 使用不支持的 Scheme 时抛出。
  154. */
  155. public function withScheme($scheme);
  156. /**
  157. * 返回具有指定用户信息的实例。
  158. *
  159. * 此方法 **必须** 保留当前实例的状态,并返回包含指定用户信息的实例。
  160. *
  161. * 密码是可选的,但用户信息 **必须** 包括用户;用户信息的空字符串相当于删除用户信息。
  162. *
  163. * @param string $user 用于认证的用户名。
  164. * @param null|string $password 密码。
  165. * @return self 具有指定用户信息的新实例。
  166. */
  167. public function withUserInfo($user, $password = null);
  168. /**
  169. * 返回具有指定 HOST 信息的实例。
  170. *
  171. * 此方法 **必须** 保留当前实例的状态,并返回包含指定 HOST 信息的实例。
  172. *
  173. * 空的 HOST 信息等同于删除 HOST 信息。
  174. *
  175. * @param string $host 用于新实例的 HOST 信息。
  176. * @return self 具有指定 HOST 信息的实例。
  177. * @throws \InvalidArgumentException 使用无效的 HOST 信息时抛出。
  178. */
  179. public function withHost($host);
  180. /**
  181. * 返回具有指定端口的实例。
  182. *
  183. * 此方法 **必须** 保留当前实例的状态,并返回包含指定端口的实例。
  184. *
  185. * 实现 **必须** 为已建立的 TCP 和 UDP 端口范围之外的端口引发异常。
  186. *
  187. * 为端口提供的空值等同于删除端口信息。
  188. *
  189. * @param null|int $port 用于新实例的端口;`null` 值将删除端口信息。
  190. * @return self 具有指定端口的实例。
  191. * @throws \InvalidArgumentException 使用无效端口时抛出异常。
  192. */
  193. public function withPort($port);
  194. /**
  195. * 返回具有指定路径的实例。
  196. *
  197. * 此方法 **必须** 保留当前实例的状态,并返回包含指定路径的实例。
  198. *
  199. * 路径可以是空的、绝对的(以斜线开头)或者相对路径(不以斜线开头),实现必须支持这三种语法。
  200. *
  201. * 如果 HTTP 路径旨在与 HOST 相对而不是路径相对,,那么它必须以斜线开头。
  202. * 假设 HTTP 路径不以斜线开头,对应该程序或开发人员来说,相对于一些已知的路径。
  203. *
  204. * 用户可以提供编码和解码的路径字符,要确保实现了 `getPath()` 中描述的正确编码。
  205. *
  206. * @param string $path 用于新实例的路径。
  207. * @return self 具有指定路径的实例。
  208. * @throws \InvalidArgumentException 使用无效的路径时抛出。
  209. */
  210. public function withPath($path);
  211. /**
  212. * 返回具有指定查询字符串的实例。
  213. *
  214. * 此方法 **必须** 保留当前实例的状态,并返回包含查询字符串的实例。
  215. *
  216. * 用户可以提供编码和解码的查询字符串,要确保实现了 `getQuery()` 中描述的正确编码。
  217. *
  218. * 空查询字符串值等同于删除查询字符串。
  219. *
  220. * @param string $query 用于新实例的查询字符串。
  221. * @return self 具有指定查询字符串的实例。
  222. * @throws \InvalidArgumentException 使用无效的查询字符串时抛出。
  223. */
  224. public function withQuery($query);
  225. /**
  226. * 返回具有指定 URI 片段(Fragment)的实例。
  227. *
  228. * 此方法 **必须** 保留当前实例的状态,并返回包含片段的实例。
  229. *
  230. * 用户可以提供编码和解码的片段,要确保实现了 `getFragment()` 中描述的正确编码。
  231. *
  232. * 空片段值等同于删除片段。
  233. *
  234. * @param string $fragment 用于新实例的片段。
  235. * @return self 具有指定 URI 片段的实例。
  236. */
  237. public function withFragment($fragment);
  238. /**
  239. * 返回字符串表示形式的 URI。
  240. *
  241. * 根据 RFC 3986 第 4.1 节,结果字符串是完整的 URI 还是相对引用,取决于 URI 有哪些组件。
  242. * 该方法使用适当的分隔符连接 URI 的各个组件:
  243. *
  244. * - 如果存在 Scheme 则 **必须** 以「:」为后缀。
  245. * - 如果存在认证信息,则必须以「//」作为前缀。
  246. * - 路径可以在没有分隔符的情况下连接。但是有两种情况需要调整路径以使 URI 引用有效,因为 PHP
  247. * 不允许在 `__toString()` 中引发异常:
  248. * - 如果路径是相对的并且有认证信息,则路径 **必须** 以「/」为前缀。
  249. * - 如果路径以多个「/」开头并且没有认证信息,则起始斜线 **必须** 为一个。
  250. * - 如果存在查询字符串,则 **必须** 以「?」作为前缀。
  251. * - 如果存在片段(Fragment),则 **必须** 以「#」作为前缀。
  252. *
  253. * @see http://tools.ietf.org/html/rfc3986#section-4.1
  254. * @return string
  255. */
  256. public function __toString();
  257. }

3.6 Psr\Http\Message\UploadedFileInterface

  1. <?php
  2. namespace Psr\Http\Message;
  3. /**
  4. * 通过 HTTP 请求上传的一个文件内容。
  5. *
  6. * 此接口的实例是被视为无法修改的,所有能修改状态的方法,都 **必须** 有一套机制,在内部保
  7. * 持好原有的内容,然后把修改状态后的,新的实例返回。
  8. */
  9. interface UploadedFileInterface
  10. {
  11. /**
  12. * 获取上传文件的数据流。
  13. *
  14. * 此方法必须返回一个 `StreamInterface` 实例,此方法的目的在于允许 PHP 对获取到的数
  15. * 据流直接操作,如 stream_copy_to_stream() 。
  16. *
  17. * 如果在调用此方法之前调用了 `moveTo()` 方法,此方法 **必须** 抛出异常。
  18. *
  19. * @return StreamInterface 上传文件的数据流
  20. * @throws \RuntimeException 没有数据流的情形下。
  21. * @throws \RuntimeException 无法创建数据流。
  22. */
  23. public function getStream();
  24. /**
  25. * 把上传的文件移动到新目录。
  26. *
  27. * 此方法保证能同时在 `SAPI` 和 `non-SAPI` 环境下使用。实现类库 **必须** 判断
  28. * 当前处在什么环境下,并且使用合适的方法来处理,如 move_uploaded_file(), rename()
  29. * 或者数据流操作。
  30. *
  31. * $targetPath 可以是相对路径,也可以是绝对路径,使用 rename() 解析起来应该是一样的。
  32. *
  33. * 当这一次完成后,原来的文件 **必须** 会被移除。
  34. *
  35. * 如果此方法被调用多次,一次以后的其他调用,都要抛出异常。
  36. *
  37. * 如果在 SAPI 环境下的话,$_FILES 内有值,当使用 moveTo(), is_uploaded_file()
  38. * 和 move_uploaded_file() 方法来移动文件时 **应该** 确保权限和上传状态的准确性。
  39. *
  40. * 如果你希望操作数据流的话,请使用 `getStream()` 方法,因为在 SAPI 场景下,无法
  41. * 保证书写入数据流目标。
  42. *
  43. * @see http://php.net/is_uploaded_file
  44. * @see http://php.net/move_uploaded_file
  45. * @param string $targetPath 目标文件路径。
  46. * @throws \InvalidArgumentException 参数有问题时抛出异常。
  47. * @throws \RuntimeException 发生任何错误,都抛出此异常。
  48. * @throws \RuntimeException 多次运行,也抛出此异常。
  49. */
  50. public function moveTo($targetPath);
  51. /**
  52. * 获取文件大小。
  53. *
  54. * 实现类库 **应该** 优先使用 $_FILES 里的 `size` 数值。
  55. *
  56. * @return int|null 以 bytes 为单位,或者 null 未知的情况下。
  57. */
  58. public function getSize();
  59. /**
  60. * 获取上传文件时出现的错误。
  61. *
  62. * 返回值 **必须** 是 PHP 的 UPLOAD_ERR_XXX 常量。
  63. *
  64. * 如果文件上传成功,此方法 **必须** 返回 UPLOAD_ERR_OK。
  65. *
  66. * 实现类库 **必须** 返回 $_FILES 数组中的 `error` 值。
  67. *
  68. * @see http://php.net/manual/en/features.file-upload.errors.php
  69. * @return int PHP 的 UPLOAD_ERR_XXX 常量。
  70. */
  71. public function getError();
  72. /**
  73. * 获取客户端上传的文件的名称。
  74. *
  75. * 永远不要信任此方法返回的数据,客户端有可能发送了一个恶意的文件名来攻击你的程序。
  76. *
  77. * 实现类库 **应该** 返回存储在 $_FILES 数组中 `name` 的值。
  78. *
  79. * @return string|null 用户上传的名字,或者 null 如果没有此值。
  80. */
  81. public function getClientFilename();
  82. /**
  83. * 客户端提交的文件类型。
  84. *
  85. * 永远不要信任此方法返回的数据,客户端有可能发送了一个恶意的文件类型名称来攻击你的程序。
  86. *
  87. * 实现类库 **应该** 返回存储在 $_FILES 数组中 `type` 的值。
  88. *
  89. * @return string|null 用户上传的类型,或者 null 如果没有此值。
  90. */
  91. public function getClientMediaType();
  92. }