PHP Standards Recommendations [6,7,13]

2016年12月26日 沈伯伟

接前篇:PSR 0-4

哪里的翻译或者理解有问题,欢迎留言指正~

PSR-6 Caching Interface

本规范总结下就是:定义了一个通用的缓存系统接口,解决不同系统、框架兼容性,减少了使用者的学习成本,避免了开发者为了兼容性而耗费精力在适配器上。

目的

让开发者创建一个cache-aware库,这个库可以被直接整合进现有的框架和系统,而不必再根据环境去定制开发。

定义

  • Calling Library(调用库):这个库或者代码是实际需要使用缓存服务的(也就是使用者)。这个库会利用那些已经 实现了标准接口的缓存服务,但是不必知道缓存服务是如何实现的。

  • Implementing Library(实现库):这个库是负责实现这个标准的类库(即缓存的实现者)。 它负责为任何的Calling Library(调用库)提供缓存服务。这个实现库必须提供实现了Cache\CacheItemPoolInterfaceCache\CacheItemInterface的类(这两个接口会在下边给出)。 且实现库必须支持最小TTL功能(如下描述,秒级精确)。

  • TTL(Time To Live 生存期):一个item的生存期(TTL)即它从存储到“过期”(stale)的这段时间。 一般地,TTL是用一个整数来代表秒级的时间的,或者用一个DateInterval的对象来定义。

  • Expiration(到期时间):这是一个item被设置为过期(stale)的时间时间。一般可以通过存储时刻加上TTL 来得到这个时间。但是,也可以明确地指定一个DateTime对象作为到期时间(Expiration)。

      一个item的TTL=300s,存储时刻为1:30:00,那么Expiration为1:35:00
    

    实现库可以item在其到期时间前失效。一旦到达到期时间,必须处理这个item为失效。 如果调用库调用实现库的相关方法来缓存一个item,而未指定到期时间(或者指定一个空的到期时间和TTL)。 此时,实现库可以使用一个配置的默认时间段来存储。如果没有配置默认的时间段,那么实现库必须永久 缓存这个item,或者按底层支持的最长时间来缓存。

  • Key(键值):长度至少为1的字符串,用以唯一标识缓冲项。实现库(Implementing Library)必须支持 支持键值满足:可以由A-Z,a-z,0-9,_.任何顺序组合,UTF-8编码,长度最大64位。 实现库(Implementing Library)可以支持额外的字符、编码或者最大长度,但是必须支持上边的最低要求。 实现库可以适当地实现对键值的转义,但是必须保证能够返回原始未被修改的键值字串。 以下的字符为未来拓展而保留,一定不可作为键名的命名支持: {}()/\@:

  • Hit(命中):一次缓存命中定义为—-当调用库通过键值请求一条缓存时,可以找到匹配键值的缓存,且该缓存并未 过期,并保证这个值合法(不会因为其他原因到时不合法)。 调用库应该保证在调用所有get()前验证isHit()(保证可以命中再获取)。

  • Miss(未命中):和命中相反,一次缓存的未命中定义为:当调用库通过键值请求一个缓存项时,出现:

    • 该键值对应的缓存值没有被找到。

    • 该缓存值已经过期了。

    • 由于某些原因,该缓存不合法。

    一个过期的缓存值必须总是被认为该缓存未命中(miss)。

  • Deferred(延迟):一个延迟缓存指的是有可能没有被缓存池(pool)立即持久化的缓存项。 一个缓存池对象可能会延迟持久化一个延迟缓存,是为了利用一些存储引擎所支持的批量存储操作。 一个缓存池必须保证任何的延迟缓存项最终都能被持久化,且数据不会丢失,并可以持久化它们 在一个调用库请求持久化之前。 当一个调用库调用commit()方法时,所有未完成的延迟缓存必须持久化。 实现库可以使用任何恰当的逻辑来决定何时来持久化延迟缓存项, 例如可以在对象析构的时候,持久化所有延迟缓存(在save()方法实现),或者也可以设置一个最大的缓存时间、 最大的缓存条目,当达到后再缓存。 当请求一个在延迟的缓存项的时候,必须返回延迟中还没有被持久化的缓存项。

数据

实现库必须支持所有可序列化的PHP数据类型,包括:

  • 字符串(Strings):任意长度的PHP兼容编码的字符串。

  • 整型(Integers):PHP支持的低于64位的有符号整数值。

  • 浮点型(Floats):有符号的浮点数值。

  • Boolean:TrueFalse

  • Null:空值。

  • 数组(Arrays):索引、关联型数组,和任意深度的多维数组。

  • 对象(Object):支持无损序列化和反序列化的任何对象,例如$o == unserialize(serialize($o))。 对象可以借助PHP的序列化相关接口,__sleep()或者__wakeup()魔术方法,或者其他合适的语言功能。

所有缓存到实现库(Implementing Library)必须能够被完全准确地返回,包括数据类型。 比如,如果缓存的是整型的5,而返回的是string类型的”5”,这就是错误的。 实现库可以使用PHP的序列化和反序列函数serialize()/unserialize(),但不强求使用。 对于兼容性,以能支持所有数据类型作为基准。

如果由于某种原因,不可能返回准确的缓存值,那么实现库必须告知缓存未命中(miss),而不是返回错误数据。

关键概念

  • 缓存池(Pool):

    一个缓存池表示缓存系统中众多缓存项(items)的集合。缓存池是逻辑上包含所有缓存项的仓库。 所有可以被缓存的项目从缓存池中被当作一个缓存对象(item object)取回。 而且,所有与缓存对象的交互,都是发生在缓存池中的。

  • 缓存项(Items):

    一个缓存项表示缓存池中单独的一个键值对(key/value pair)。其中的key是缓存项的唯一标识(主键), 且必须不可改变。value可以在任何时间被改变。

错误处理

缓存通常对应用程序的性能是很重要的,但是它不应该成为应用程序功能不可或缺的一部分。 因此,缓存系统中的错误不应该导致应用程序运行失败。出于这个目的, 实现库必须不能抛出被接口定义之外的任何其他异常, 且应该捕获任何错误或者由底层数据存储引发的异常,不让其冒泡至超出缓存系统。

实现库应该通过日志(log)的方式或者其他类似的报告方式记录这些错误,以通知相关管理员。

如果调用库请求一个或多个已经被删除的缓存项,或者缓冲池已经被清空, 一定不能把指定的不存在当作是错误。 后置条件(post-condition)是相同的(不存在,或者缓冲池为空),因此不存在错误发生。

接口

  • CacheItemInterface

CacheItemInterface定义了缓存系统中的一个缓存项。每一个缓存项对象必须关联一个指定的键值, 可以通过实现系统(implementing system)来设置, 且典型地是通过Cache\CacheItemPoolInterface对象传递过来。

Cache\CacheItemInterface对象封装了缓存项的存储和获取。每一个Cache\CacheItemInterface是由 Cache\CacheItemPoolInterface对象生成的,这个Cache\CacheItemPoolInterface负责任何需要的设置工作, 和联系一个对象到唯一的键值一样。Cache\CacheItemInterface对象必须能够存储和获取任何类型的PHP值 (在上边数据一节中列出的)。

调用库必须不能自己实例化缓存项Item对象。它只能通过缓存池Pool对象的getItem()方法获得。 调用库不应该假设由一个实现类库创建的缓存项能被另一个实现类库的缓存池所兼容。

<?php

namespace Psr\Cache;

/**
 * CacheItemInterface defines an interface for interacting with objects inside a cache.
 */
interface CacheItemInterface
{
    /**
     * Returns the key for the current cache item.
     *
     * The key is loaded by the Implementing Library, but should be available to
     * the higher level callers when needed.
     *
     * @return string
     *   The key string for this cache item.
     */
    public function getKey();

    /**
     * Retrieves the value of the item from the cache associated with this object's key.
     *
     * The value returned must be identical to the value originally stored by set().
     *
     * If isHit() returns false, this method MUST return null. Note that null
     * is a legitimate cached value, so the isHit() method SHOULD be used to
     * differentiate between "null value was found" and "no value was found."
     *
     * @return mixed
     *   The value corresponding to this cache item's key, or null if not found.
     */
    public function get();

    /**
     * Confirms if the cache item lookup resulted in a cache hit.
     *
     * Note: This method MUST NOT have a race condition between calling isHit()
     * and calling get().
     *
     * @return bool
     *   True if the request resulted in a cache hit. False otherwise.
     */
    public function isHit();

    /**
     * Sets the value represented by this cache item.
     *
     * The $value argument may be any item that can be serialized by PHP,
     * although the method of serialization is left up to the Implementing
     * Library.
     *
     * @param mixed $value
     *   The serializable value to be stored.
     *
     * @return static
     *   The invoked object.
     */
    public function set($value);

    /**
     * Sets the expiration time for this cache item.
     *
     * @param \DateTimeInterface|null $expiration
     *   The point in time after which the item MUST be considered expired.
     *   If null is passed explicitly, a default value MAY be used. If none is set,
     *   the value should be stored permanently or for as long as the
     *   implementation allows.
     *
     * @return static
     *   The called object.
     */
    public function expiresAt($expiration);

    /**
     * Sets the expiration time for this cache item.
     *
     * @param int|\DateInterval|null $time
     *   The period of time from the present after which the item MUST be considered
     *   expired. An integer parameter is understood to be the time in seconds until
     *   expiration. If null is passed explicitly, a default value MAY be used.
     *   If none is set, the value should be stored permanently or for as long as the
     *   implementation allows.
     *
     * @return static
     *   The called object.
     */
    public function expiresAfter($time);

}
  • CacheItemPoolInterface

Cache\CacheItemPoolInterface的最主要目的是接收一个来自调用库的键值, 并返回其相关联的Cache\CacheItemInterface对象。 Cache\CacheItemPoolInterface同时也是与整个缓存交互的最主要的点。 所有缓存池的配置和初始化都留给实现库来完成。

<?php

namespace Psr\Cache;

/**
 * CacheItemPoolInterface generates CacheItemInterface objects.
 */
interface CacheItemPoolInterface
{
    /**
     * Returns a Cache Item representing the specified key.
     *
     * This method must always return a CacheItemInterface object, even in case of
     * a cache miss. It MUST NOT return null.
     *
     * @param string $key
     *   The key for which to return the corresponding Cache Item.
     *
     * @throws InvalidArgumentException
     *   If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
     *   MUST be thrown.
     *
     * @return CacheItemInterface
     *   The corresponding Cache Item.
     */
    public function getItem($key);

    /**
     * Returns a traversable set of cache items.
     *
     * @param string[] $keys
     *   An indexed array of keys of items to retrieve.
     *
     * @throws InvalidArgumentException
     *   If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException
     *   MUST be thrown.
     *
     * @return array|\Traversable
     *   A traversable collection of Cache Items keyed by the cache keys of
     *   each item. A Cache item will be returned for each key, even if that
     *   key is not found. However, if no keys are specified then an empty
     *   traversable MUST be returned instead.
     */
    public function getItems(array $keys = array());

    /**
     * Confirms if the cache contains specified cache item.
     *
     * Note: This method MAY avoid retrieving the cached value for performance reasons.
     * This could result in a race condition with CacheItemInterface::get(). To avoid
     * such situation use CacheItemInterface::isHit() instead.
     *
     * @param string $key
     *   The key for which to check existence.
     *
     * @throws InvalidArgumentException
     *   If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
     *   MUST be thrown.
     *
     * @return bool
     *   True if item exists in the cache, false otherwise.
     */
    public function hasItem($key);

    /**
     * Deletes all items in the pool.
     *
     * @return bool
     *   True if the pool was successfully cleared. False if there was an error.
     */
    public function clear();

    /**
     * Removes the item from the pool.
     *
     * @param string $key
     *   The key to delete.
     *
     * @throws InvalidArgumentException
     *   If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
     *   MUST be thrown.
     *
     * @return bool
     *   True if the item was successfully removed. False if there was an error.
     */
    public function deleteItem($key);

    /**
     * Removes multiple items from the pool.
     *
     * @param string[] $keys
     *   An array of keys that should be removed from the pool.

     * @throws InvalidArgumentException
     *   If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException
     *   MUST be thrown.
     *
     * @return bool
     *   True if the items were successfully removed. False if there was an error.
     */
    public function deleteItems(array $keys);

    /**
     * Persists a cache item immediately.
     *
     * @param CacheItemInterface $item
     *   The cache item to save.
     *
     * @return bool
     *   True if the item was successfully persisted. False if there was an error.
     */
    public function save(CacheItemInterface $item);

    /**
     * Sets a cache item to be persisted later.
     *
     * @param CacheItemInterface $item
     *   The cache item to save.
     *
     * @return bool
     *   False if the item could not be queued or if a commit was attempted and failed. True otherwise.
     */
    public function saveDeferred(CacheItemInterface $item);

    /**
     * Persists any deferred cache items.
     *
     * @return bool
     *   True if all not-yet-saved items were successfully saved or there were none. False otherwise.
     */
    public function commit();
}
  • CacheException

这个异常接口在发生严重错误的时候被使用,包括但是不局限于缓存配置,例如连接缓存服务或者不合法的证书验证。

任何被实现库抛出的异常必须实现这个接口。

<?php

namespace Psr\Cache;

/**
 * Exception interface for all exceptions thrown by an Implementing Library.
 */
interface CacheException
{
}
  • InvalidArgumentException
<?php

namespace Psr\Cache;

/**
 * Exception interface for invalid cache arguments.
 *
 * Any time an invalid argument is passed into a method it must throw an
 * exception class which implements Psr\Cache\InvalidArgumentException.
 */
interface InvalidArgumentException extends CacheException
{
}

相关英文原文请查看:PSR-6 Caching Interface

PSR-7 HTTP Message Interface

本规范主要描述了表示HTTP报文(RFC 7230RFC 7231)的通用接口, 以及HTTP报文所使用的URI(统一资源标识符Uniform Resource Identifier, 见RFC 3986)。

HTTP报文是web开发的基础。 网络浏览器和HTTP客户端(如cURL)创建HTTP请求报文(request message), 并发送给web服务器。web服务器返回一个HTTP响应报文(response message)。 服务器端的代码会收到一个HTTP的请求报文,并返回一个HTTP的响应报文。

HTTP报文通常抽象自终端用户,但是作为开发者,我们通常需要知道它们的结构、访问和操作, 以便执行我们的任务,可能是向HTTP API发送一个请求或者是处理一个过来的报文。

每一个HTTP请求报文有指定的形式:

POST /path HTTP/1.1
Host: example.com

foo=bar&baz=bat

第一行是请求行,从左到右依次包含HTTP的请求方式,请求的目标 (通常或者是一个绝对的URI,或者是web服务器上的路径),HTTP协议的版本。 接下来是一行或者多行的HTTP请求报头。空一行后,是消息主体。

HTTP响应报文是如下的结构:

HTTP/1.1 200 OK
Content-Type: text/plain

This is the response body

第一行是状态行,从左到右依次包含HTTP协议版本,HTTP状态码,以及一个可理解的状态码描述。 类似请求消息,接下来是一行或者多行的HTTP响应报头。空一行后市消息主体。

本规范中的接口描述了HTTP报文的相关抽象和组成元素。

详述

  1. 报文 (Messages)

    一个HTTP报文可能是客户端到服务器的请求,也可能是服务器到客户端的响应。 本规范分别定义Psr\Http\Message\RequestInterfacePsr\Http\Message\ResponseInterface来表示HTTP报文。

    Psr\Http\Message\RequestInterfacePsr\Http\Message\ResponseInterface都继承自 Psr\Http\Message\MessageInterface。虽然Psr\Http\Message\MessageInterface可以被直接实现, 但是实现者应该去实现Psr\Http\Message\RequestInterfacePsr\Http\Message\ResponseInterface

    从这里开始,在引用这些接口的时候,命名空间Psr\Http\Message将会被省略。

  2. HTTP头(headers)

    • 不区分大小写的头部字段名称 (Case-insensitive header field names)

      HTTP报文包括不区分大小写的头部字段。头部各字段的信息通过实现了MessageInterface接口的类中获取, 需要通过指定获取字段的名称,不区别大小写。 例如,获取头部foo字段的值将返回和获取FoO字段同样的结果。 同样地,设置头部Foo字段的值也将覆盖掉foo字段的值。

        $message = $message->withHeader('foo', 'bar');
      
        echo $message->getHeaderLine('foo');
        // Outputs: bar
      
        echo $message->getHeaderLine('FOO');
        // Outputs: bar
      
        $message = $message->withHeader('fOO', 'baz');
        echo $message->getHeaderLine('foo');
        // Outputs: baz
      

      尽管头部字段的获取是大小写不敏感的,但是在实现时必须保存原始的字段(包括大小写), 特别是通过getHeaders()获取的时候。

    • 具有多个值的头部字段 (Headers with multiple values)

      为了让头部字段适应多值的情况,且方便让其以字符串(string)的形式工作, 头部可以通过MessageInterface的实例按照数组(array)或者字符串(string)两种形式获取。 getHeaderLine()方法可以获取头部的值(某字段所有的值,字段名称不区分大小写)拼接后的字符串 (以,分割); getHeader()方法可以根据不区分大小写的字段名称获取头部所有值的数组。如下例所示

        $message = $message
            ->withHeader('foo', 'bar')
            ->withAddedHeader('foo', 'baz');
      
        $header = $message->getHeaderLine('foo');
        // $header contains: 'bar, baz'
      
        $header = $message->getHeader('foo');
        // ['bar', 'baz']
      

      注意:不是头部中的所有值都能够使用,号拼接为字符串(例如该值中就包含逗号)。 当遇到这种情况时,基于MessageInterface类的用户应该使用getHeader()方法来获取多值的头部。

    • 主机头(Host header)

      关于Host header的相关规范,感觉还是看原文比较清楚,结合后边的例子理解。下面是原文:

      In requests, the Host header typically mirrors the host component of the URI, as well as the host used when establishing the TCP connection. However, the HTTP specification allows the Host header to differ from each of the two.

      During construction, implementations MUST attempt to set the Host header from a provided URI if no Host header is provided.

      RequestInterface::withUri() will, by default, replace the returned request’s Host header with a Host header matching the host component of the passed UriInterface.

      You can opt-in to preserving the original state of the Host header by passing true for the second ($preserveHost) argument. When this argument is set to true, the returned request will not update the Host header of the returned message – unless the message contains no Host header.

      This table illustrates what getHeaderLine('Host') will return for a request returned by withUri() with the $preserveHost argument set to true for various initial requests and URIs.

      Request Host header1 Request host component2 URI host component3 Result
      ’’ ’’ ’’ ’’
      ’’ 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 header value prior to operation.
      • 2 Host component of the URI composed in the request prior to the operation.
      • 3 Host component of the URI being injected via withUri().
  3. 流 (Streams)

    HTTP报文由一个起始行(请求行、状态行)、消息报头、主体内容组成。其中,主体内容可以非常的小,也可以 很大。尝试将报文的主体内容表示为一个字符串可能会容易比预期消耗更多的内存,因为主体内容必须被完整地放到 内存中。尝试将请求或者响应报文存储在内存将会妨碍实现库处理大规模报文主体的能力。 (注:简而言之,就是为了避免一次性将报文放进内存处理,才引入了流的概念) StreamInterface这个接口是用来在数据流的读取或者写入中隐藏实现细节的。 为了让字符串可以被准确地表示为报文,php://memoryphp://temp这两个内置的流可以被使用。

    关于这里的php://memoryphp://temp,我查找了相关的资料:

    php://memoryphp://temp是一个类似文件包装器的数据流,允许读写临时数据。 两者的唯一区别是php://memory总是把数据储存在内存中, 而php://temp会在内存量达到预定义的限制后(默认是2MB)存入临时文件中。 临时文件位置的决定和sys_get_temp_dir()的方式一致。

    php://temp的内存限制可通过添加/maxmemory:NN来控制, NN是以字节为单位、保留在内存的最大数据量,超过则使用临时文件。

    参考文献:php:// 语言参考

    下面继续,

    StreamInterface暴露出了几个方法,用来支持流的读取、写入和高效地遍历。 还提供了如下三个方法:isReadable()isWritable()isSeekable()。 这些方法可以被的协作者用来判断其是否具备相应能力。

    每一个的实例具有不同的能力:可以是只读的(read-only),可以是只写的(write-only), 也可以是可读写的(read-write)。它也可以允许任意地随机访问(前向或后向到任意位置搜寻), 或者只连续地进行访问(例如在socketpipe或者callback-based的流中)。

    最后,StreamInterface定义了一个__toString()方法,来简化一次性获取或者发送整体的内容。

    不像请求或者响应接口,StreamInterface没有固定的构造模型。在实际的情况下,PHP stream是被封装的, 不可能来强制其具有一个不变的形式,因为那些与资源交互的代码会潜在地改变流的状态 (包括光标的位置,内容等)。 我们的建议是实现库为服务器端的请求和客户端的响应使用只读的(read-only。 使用者(Consumers)应该知道实际上的实例可以是不定的,而且,同样地,其可以改变报文的状态。 当不能肯定时,可以创建一个新的实例并将其绑定到一个报文来强制其状态。

  4. 请求目标和统一资源标识符(Request Targets and URIs)

    按照RFC 7230要求,请求报文包含一个请求目标,在请求行的第二个部分。 这个请求目标可以是下列中的一种形式:

    • origin-form(原始形式),由路径和查询字符串(如果存在)组成。通常被称为相对URL。 典型的通过TCP传输的报文中的请求目标都是原始形式的。 协议名(scheme)和认证数据(authority data)通常都是通过CGI变量呈现的。

    • absolute-form(绝对形式),包含协议名(scheme)、 认证(“[user-info@]host[:port]”,方括号为可选项)、路径(path)、查询字符串(query string)和 片段(fragment)。这是RFC 3986中指定URI的唯一形式。这种形式常被用来构造HTTP代理请求。

        通过查看wiki,一般的`URI`具有如下的形式:	
      		
        scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]
      

      参考:Uniform_Resource_Identifier

    • authority-form(认证形式),只包含认证(authority)。一般只被用在CONNECT请求中, 来搭建HTTP客户端和代理服务器之间的连接。

    • asterisk-form(星*形式),单独地包含字符*,且与OPTIONS方法一同来确定web服务器的一般性能力。

    除了这些请求目标(request-targets)外,还有一个常用的effective URL,它是从请求目标分离出来的。 这个effective URL不能在HTTP报文中传递, 但是可以被用来确定构成请求的协议类型(http/https)、端口和主机名称。

    这个effective URLUriInterface中被表示。UriInterface按照RFC 3986中的标准 构造HTTPHTTPS类型的URIs。这个接口提供了与不同URI部分交互的方法,可以避免重复解析URI__toString()方法可以转化构造出的URI为一个字符串。

    当通过getRequestTarget()方法获取请求目标时,默认情况下这个方法会使用URI对象,且提取出所有必要的 组件来构造origin-form(原始形式)。目前,origin-form是最常用的请求目标形式。

    如果终端用户希望使用其他的三种形式,或者用户想要明确地重写(override)请求目标, 可以使用withRequestTarget()方法。使用这个方法并不会影响URI,因为它是从getUri()方法返回的。

    例如,用户可能想要发送一个asterisk-form(星*形式)的请求到服务器:

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

    这个例子最终产生的HTTP请求如下所示:

     OPTIONS * HTTP/1.1
    

    但是,HTTP客户端仍然可以使用effective URLgetUri()中得到),来确定协议、主机名和TCP端口。

    一个HTTP客户端必须忽略Uri::getPath()Uri::getQuery()的值。而应该取而代之使用 getRequestTarget()返回的值,默认情况下getRequestTarget()会返回上述两个方法连接的值。

    客户端如果选择不去实现任何一种请求目标的形式或者多于4种的形式,仍然必须使用getRequestTarget()。 这些客户端必须拒绝不支持的请求目标,且必须不能fall backgetUri()返回的值。

    RequestInterface接口提供了获取请求目标(request-target)和 根据提供的请求目标创建一个新的实例的方法。默认情况下,一个实例如果没有指定请求目标, getRequestTarget()方法将返回其包含的原始形式(origin-form)的URI(如果没有URI,则返回/)。 withRequestTarget($requestTarget)方法根据提供的请求目标创建一个新的实例, 因此允许开发者创建包含其他三种请求目标形式的请求报文(absolute-form, authority-form, 和 asterisk-form)。 在使用时,包含的URI实例仍然能够被使用,特别在客户端,它可以被用来创建与服务器的连接。

  5. 服务器端请求(Server-side Requests)

    RequestInterface接口提供了HTTP请求报文的一般性表示。 但是,服务器端的请求由于环境原因需要额外的处理。 服务器端的处理需要考虑Common Gateway Interface (CGI),以及更具体地, PHP的抽象和通过服务器APIs(SAPI)的CGI拓展。PHP已经提供了超全局变量来简化这些输入,如下所示:

    • $_COOKIE,反序列化并提供了对HTTP cookies的简化访问。

    • $_GET,反序列化并提供对查询字符串参数的简化访问。

    • $_POST,反序列化并提供对通过HTTP POST提交的urlencoded参数的简化访问。 一般地说,它可以被认为是报文主体的解析结果。

    • $_FILES,which provides serialized metadata around file uploads.

    • $_SERVER,提供对CGI/SAPI环境变量的访问,一般包括请求的方法、请求的协议、请求的URIheaders

    ServerRequestInterface继承自RequestInterface,它抽象了上述的各种超全局变量。

  6. 上传文件(RequestInterface)

    ServerRequestInterface指定了一个方法来获取上传文件的标准化树形结构, 每一个叶节点是UploadedFileInterface的一个实例。

    $_FILES这个超全局变量在处理文件输入的数组时有一些众所周知的问题:如果你提交的是一个文件数组, 举例来说,就是输入的文件名“files”,提交的为files[0]files[1],那么PHP的超全局变量$_FILES为:

     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

    • 进行单元测试时。

    • 当在non-SAPI环境下操作时,例如ReactPHP

    在这些情况时,数据需要不同地进行确定,例如:

    • 一个进程可以通过解析报文主体(message body)来发现上传的文件。在这种情况下, 实现库可能并不是选择将上传的文件写到文件系统,而是封装它们到一个流stream里, 以减少内存、I/O和存储的高消耗。

    • 在进行单元测试时,开发者需要能够存根(stub)并且/或者模拟(mock)这个上传文件的元数据, 以便进行验证、核实。

    getUploadedFiles()为用户提供了标准化的结构。实现库应该:

    • 聚集上传文件的所有信息,并且使用这些信息来填充一个 Psr\Http\Message\UploadedFileInterface的实例。

    • 重建上传的树结构(以我们期待的形式,而不是$_FILE中的结构),给定树上的每一个叶节点使用合适的 Psr\Http\Message\UploadedFileInterface实例。

    这个树结构的引用应该模仿提交的每一个文件的命名结构。

    举个最简单的例子,下面是一个单独命名形式的提交:

     <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][]" />
    

    在这种情况下,实现库必须聚集所有对应下标的信息到一个文件结构中。 原因是$_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 methodwithUploadedFiles()也在接口中提出,允许标准化委托到另一个进程。

    用户可以类似如下使用:

     $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"
    

    这个标准也意识到了实现库可能会在non-SAPI环境下进行处理。同样地, UploadedFileInterface提供了保证操作可以在不同环境下工作的方法,特别是:

    • moveTo($targetPath)被提供作为一个安全和推荐的替代品,来取代直接调用move_uploaded_file()方法 来处理上传的临时文件。实现库将基于环境来使用对应的操作。

    • getStream()将返回一个StreamInterface实例。在non-SAPI环境中,有可能会解析单独的上传文件到 php://temp流中,来取代直接写到文件。在这种情况下,并没有上传文件存在($_FILE没被填充?)。 因此,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);
    

包 Package

这个规范中的相关接口和类都在`psr/http-message`这个包中。

接口 Interfaces

1 Psr\Http\Message\MessageInterface

<?php
namespace Psr\Http\Message;

/**
 * HTTP messages consist of requests from a client to a server and responses
 * from a server to a client. This interface defines the methods common to
 * each.
 *
 * Messages are considered immutable; all methods that might change state MUST
 * be implemented such that they retain the internal state of the current
 * message and return an instance that contains the changed state.
 *
 * @see http://www.ietf.org/rfc/rfc7230.txt
 * @see http://www.ietf.org/rfc/rfc7231.txt
 */
interface MessageInterface
{
	/**
	 * Retrieves the HTTP protocol version as a string.
	 *
	 * The string MUST contain only the HTTP version number (e.g., "1.1", "1.0").
	 *
	 * @return string HTTP protocol version.
	 */
	public function getProtocolVersion();

	/**
	 * Return an instance with the specified HTTP protocol version.
	 *
	 * The version string MUST contain only the HTTP version number (e.g.,
	 * "1.1", "1.0").
	 *
	 * This method MUST be implemented in such a way as to retain the
	 * immutability of the message, and MUST return an instance that has the
	 * new protocol version.
	 *
	 * @param string $version HTTP protocol version
	 * @return static
	 */
	public function withProtocolVersion($version);

	/**
	 * Retrieves all message header values.
	 *
	 * The keys represent the header name as it will be sent over the wire, and
	 * each value is an array of strings associated with the header.
	 *
	 *     // Represent the headers as a string
	 *     foreach ($message->getHeaders() as $name => $values) {
	 *         echo $name . ': ' . implode(', ', $values);
	 *     }
	 *
	 *     // Emit headers iteratively:
	 *     foreach ($message->getHeaders() as $name => $values) {
	 *         foreach ($values as $value) {
	 *             header(sprintf('%s: %s', $name, $value), false);
	 *         }
	 *     }
	 *
	 * While header names are not case-sensitive, getHeaders() will preserve the
	 * exact case in which headers were originally specified.
	 *
	 * @return string[][] Returns an associative array of the message's headers.
	 *     Each key MUST be a header name, and each value MUST be an array of
	 *     strings for that header.
	 */
	public function getHeaders();

	/**
	 * Checks if a header exists by the given case-insensitive name.
	 *
	 * @param string $name Case-insensitive header field name.
	 * @return bool Returns true if any header names match the given header
	 *     name using a case-insensitive string comparison. Returns false if
	 *     no matching header name is found in the message.
	 */
	public function hasHeader($name);

	/**
	 * Retrieves a message header value by the given case-insensitive name.
	 *
	 * This method returns an array of all the header values of the given
	 * case-insensitive header name.
	 *
	 * If the header does not appear in the message, this method MUST return an
	 * empty array.
	 *
	 * @param string $name Case-insensitive header field name.
	 * @return string[] An array of string values as provided for the given
	 *    header. If the header does not appear in the message, this method MUST
	 *    return an empty array.
	 */
	public function getHeader($name);

	/**
	 * Retrieves a comma-separated string of the values for a single header.
	 *
	 * This method returns all of the header values of the given
	 * case-insensitive header name as a string concatenated together using
	 * a comma.
	 *
	 * NOTE: Not all header values may be appropriately represented using
	 * comma concatenation. For such headers, use getHeader() instead
	 * and supply your own delimiter when concatenating.
	 *
	 * If the header does not appear in the message, this method MUST return
	 * an empty string.
	 *
	 * @param string $name Case-insensitive header field name.
	 * @return string A string of values as provided for the given header
	 *    concatenated together using a comma. If the header does not appear in
	 *    the message, this method MUST return an empty string.
	 */
	public function getHeaderLine($name);

	/**
	 * Return an instance with the provided value replacing the specified header.
	 *
	 * While header names are case-insensitive, the casing of the header will
	 * be preserved by this function, and returned from getHeaders().
	 *
	 * This method MUST be implemented in such a way as to retain the
	 * immutability of the message, and MUST return an instance that has the
	 * new and/or updated header and value.
	 *
	 * @param string $name Case-insensitive header field name.
	 * @param string|string[] $value Header value(s).
	 * @return static
	 * @throws \InvalidArgumentException for invalid header names or values.
	 */
	public function withHeader($name, $value);

	/**
	 * Return an instance with the specified header appended with the given value.
	 *
	 * Existing values for the specified header will be maintained. The new
	 * value(s) will be appended to the existing list. If the header did not
	 * exist previously, it will be added.
	 *
	 * This method MUST be implemented in such a way as to retain the
	 * immutability of the message, and MUST return an instance that has the
	 * new header and/or value.
	 *
	 * @param string $name Case-insensitive header field name to add.
	 * @param string|string[] $value Header value(s).
	 * @return static
	 * @throws \InvalidArgumentException for invalid header names.
	 * @throws \InvalidArgumentException for invalid header values.
	 */
	public function withAddedHeader($name, $value);

	/**
	 * Return an instance without the specified header.
	 *
	 * Header resolution MUST be done without case-sensitivity.
	 *
	 * This method MUST be implemented in such a way as to retain the
	 * immutability of the message, and MUST return an instance that removes
	 * the named header.
	 *
	 * @param string $name Case-insensitive header field name to remove.
	 * @return static
	 */
	public function withoutHeader($name);

	/**
	 * Gets the body of the message.
	 *
	 * @return StreamInterface Returns the body as a stream.
	 */
	public function getBody();

	/**
	 * Return an instance with the specified message body.
	 *
	 * The body MUST be a StreamInterface object.
	 *
	 * This method MUST be implemented in such a way as to retain the
	 * immutability of the message, and MUST return a new instance that has the
	 * new body stream.
	 *
	 * @param StreamInterface $body Body.
	 * @return static
	 * @throws \InvalidArgumentException When the body is not valid.
	 */
	public function withBody(StreamInterface $body);
}

2 Psr\Http\Message\RequestInterface

<?php
namespace Psr\Http\Message;

/**
 * Representation of an outgoing, client-side request.
 *
 * Per the HTTP specification, this interface includes properties for
 * each of the following:
 *
 * - Protocol version
 * - HTTP method
 * - URI
 * - Headers
 * - Message body
 *
 * During construction, implementations MUST attempt to set the Host header from
 * a provided URI if no Host header is provided.
 *
 * Requests are considered immutable; all methods that might change state MUST
 * be implemented such that they retain the internal state of the current
 * message and return an instance that contains the changed state.
 */
interface RequestInterface extends MessageInterface
{
	/**
	 * Retrieves the message's request target.
	 *
	 * Retrieves the message's request-target either as it will appear (for
	 * clients), as it appeared at request (for servers), or as it was
	 * specified for the instance (see withRequestTarget()).
	 *
	 * In most cases, this will be the origin-form of the composed URI,
	 * unless a value was provided to the concrete implementation (see
	 * withRequestTarget() below).
	 *
	 * If no URI is available, and no request-target has been specifically
	 * provided, this method MUST return the string "/".
	 *
	 * @return string
	 */
	public function getRequestTarget();

	/**
	 * Return an instance with the specific request-target.
	 *
	 * If the request needs a non-origin-form request-target — e.g., for
	 * specifying an absolute-form, authority-form, or asterisk-form —
	 * this method may be used to create an instance with the specified
	 * request-target, verbatim.
	 *
	 * This method MUST be implemented in such a way as to retain the
	 * immutability of the message, and MUST return an instance that has the
	 * changed request target.
	 *
	 * @see http://tools.ietf.org/html/rfc7230#section-5.3 (for the various
	 *     request-target forms allowed in request messages)
	 * @param mixed $requestTarget
	 * @return static
	 */
	public function withRequestTarget($requestTarget);

	/**
	 * Retrieves the HTTP method of the request.
	 *
	 * @return string Returns the request method.
	 */
	public function getMethod();

	/**
	 * Return an instance with the provided HTTP method.
	 *
	 * While HTTP method names are typically all uppercase characters, HTTP
	 * method names are case-sensitive and thus implementations SHOULD NOT
	 * modify the given string.
	 *
	 * This method MUST be implemented in such a way as to retain the
	 * immutability of the message, and MUST return an instance that has the
	 * changed request method.
	 *
	 * @param string $method Case-sensitive method.
	 * @return static
	 * @throws \InvalidArgumentException for invalid HTTP methods.
	 */
	public function withMethod($method);

	/**
	 * Retrieves the URI instance.
	 *
	 * This method MUST return a UriInterface instance.
	 *
	 * @see http://tools.ietf.org/html/rfc3986#section-4.3
	 * @return UriInterface Returns a UriInterface instance
	 *     representing the URI of the request.
	 */
	public function getUri();

	/**
	 * Returns an instance with the provided URI.
	 *
	 * This method MUST update the Host header of the returned request by
	 * default if the URI contains a host component. If the URI does not
	 * contain a host component, any pre-existing Host header MUST be carried
	 * over to the returned request.
	 *
	 * You can opt-in to preserving the original state of the Host header by
	 * setting `$preserveHost` to `true`. When `$preserveHost` is set to
	 * `true`, this method interacts with the Host header in the following ways:
	 *
	 * - If the Host header is missing or empty, and the new URI contains
	 *   a host component, this method MUST update the Host header in the returned
	 *   request.
	 * - If the Host header is missing or empty, and the new URI does not contain a
	 *   host component, this method MUST NOT update the Host header in the returned
	 *   request.
	 * - If a Host header is present and non-empty, this method MUST NOT update
	 *   the Host header in the returned request.
	 *
	 * This method MUST be implemented in such a way as to retain the
	 * immutability of the message, and MUST return an instance that has the
	 * new UriInterface instance.
	 *
	 * @see http://tools.ietf.org/html/rfc3986#section-4.3
	 * @param UriInterface $uri New request URI to use.
	 * @param bool $preserveHost Preserve the original state of the Host header.
	 * @return static
	 */
	public function withUri(UriInterface $uri, $preserveHost = false);
}
2.1 Psr\Http\Message\ServerRequestInterface
<?php
namespace Psr\Http\Message;

/**
 * Representation of an incoming, server-side HTTP request.
 *
 * Per the HTTP specification, this interface includes properties for
 * each of the following:
 *
 * - Protocol version
 * - HTTP method
 * - URI
 * - Headers
 * - Message body
 *
 * Additionally, it encapsulates all data as it has arrived at the
 * application from the CGI and/or PHP environment, including:
 *
 * - The values represented in $_SERVER.
 * - Any cookies provided (generally via $_COOKIE)
 * - Query string arguments (generally via $_GET, or as parsed via parse_str())
 * - Upload files, if any (as represented by $_FILES)
 * - Deserialized body parameters (generally from $_POST)
 *
 * $_SERVER values MUST be treated as immutable, as they represent application
 * state at the time of request; as such, no methods are provided to allow
 * modification of those values. The other values provide such methods, as they
 * can be restored from $_SERVER or the request body, and may need treatment
 * during the application (e.g., body parameters may be deserialized based on
 * content type).
 *
 * Additionally, this interface recognizes the utility of introspecting a
 * request to derive and match additional parameters (e.g., via URI path
 * matching, decrypting cookie values, deserializing non-form-encoded body
 * content, matching authorization headers to users, etc). These parameters
 * are stored in an "attributes" property.
 *
 * Requests are considered immutable; all methods that might change state MUST
 * be implemented such that they retain the internal state of the current
 * message and return an instance that contains the changed state.
 */
interface ServerRequestInterface extends RequestInterface
{
	/**
	 * Retrieve server parameters.
	 *
	 * Retrieves data related to the incoming request environment,
	 * typically derived from PHP's $_SERVER superglobal. The data IS NOT
	 * REQUIRED to originate from $_SERVER.
	 *
	 * @return array
	 */
	public function getServerParams();

	/**
	 * Retrieve cookies.
	 *
	 * Retrieves cookies sent by the client to the server.
	 *
	 * The data MUST be compatible with the structure of the $_COOKIE
	 * superglobal.
	 *
	 * @return array
	 */
	public function getCookieParams();

	/**
	 * Return an instance with the specified cookies.
	 *
	 * The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST
	 * be compatible with the structure of $_COOKIE. Typically, this data will
	 * be injected at instantiation.
	 *
	 * This method MUST NOT update the related Cookie header of the request
	 * instance, nor related values in the server params.
	 *
	 * This method MUST be implemented in such a way as to retain the
	 * immutability of the message, and MUST return an instance that has the
	 * updated cookie values.
	 *
	 * @param array $cookies Array of key/value pairs representing cookies.
	 * @return static
	 */
	public function withCookieParams(array $cookies);

	/**
	 * Retrieve query string arguments.
	 *
	 * Retrieves the deserialized query string arguments, if any.
	 *
	 * Note: the query params might not be in sync with the URI or server
	 * params. If you need to ensure you are only getting the original
	 * values, you may need to parse the query string from `getUri()->getQuery()`
	 * or from the `QUERY_STRING` server param.
	 *
	 * @return array
	 */
	public function getQueryParams();

	/**
	 * Return an instance with the specified query string arguments.
	 *
	 * These values SHOULD remain immutable over the course of the incoming
	 * request. They MAY be injected during instantiation, such as from PHP's
	 * $_GET superglobal, or MAY be derived from some other value such as the
	 * URI. In cases where the arguments are parsed from the URI, the data
	 * MUST be compatible with what PHP's parse_str() would return for
	 * purposes of how duplicate query parameters are handled, and how nested
	 * sets are handled.
	 *
	 * Setting query string arguments MUST NOT change the URI stored by the
	 * request, nor the values in the server params.
	 *
	 * This method MUST be implemented in such a way as to retain the
	 * immutability of the message, and MUST return an instance that has the
	 * updated query string arguments.
	 *
	 * @param array $query Array of query string arguments, typically from
	 *     $_GET.
	 * @return static
	 */
	public function withQueryParams(array $query);

	/**
	 * Retrieve normalized file upload data.
	 *
	 * This method returns upload metadata in a normalized tree, with each leaf
	 * an instance of Psr\Http\Message\UploadedFileInterface.
	 *
	 * These values MAY be prepared from $_FILES or the message body during
	 * instantiation, or MAY be injected via withUploadedFiles().
	 *
	 * @return array An array tree of UploadedFileInterface instances; an empty
	 *     array MUST be returned if no data is present.
	 */
	public function getUploadedFiles();

	/**
	 * Create a new instance with the specified uploaded files.
	 *
	 * This method MUST be implemented in such a way as to retain the
	 * immutability of the message, and MUST return an instance that has the
	 * updated body parameters.
	 *
	 * @param array $uploadedFiles An array tree of UploadedFileInterface instances.
	 * @return static
	 * @throws \InvalidArgumentException if an invalid structure is provided.
	 */
	public function withUploadedFiles(array $uploadedFiles);

	/**
	 * Retrieve any parameters provided in the request body.
	 *
	 * If the request Content-Type is either application/x-www-form-urlencoded
	 * or multipart/form-data, and the request method is POST, this method MUST
	 * return the contents of $_POST.
	 *
	 * Otherwise, this method may return any results of deserializing
	 * the request body content; as parsing returns structured content, the
	 * potential types MUST be arrays or objects only. A null value indicates
	 * the absence of body content.
	 *
	 * @return null|array|object The deserialized body parameters, if any.
	 *     These will typically be an array or object.
	 */
	public function getParsedBody();

	/**
	 * Return an instance with the specified body parameters.
	 *
	 * These MAY be injected during instantiation.
	 *
	 * If the request Content-Type is either application/x-www-form-urlencoded
	 * or multipart/form-data, and the request method is POST, use this method
	 * ONLY to inject the contents of $_POST.
	 *
	 * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of
	 * deserializing the request body content. Deserialization/parsing returns
	 * structured data, and, as such, this method ONLY accepts arrays or objects,
	 * or a null value if nothing was available to parse.
	 *
	 * As an example, if content negotiation determines that the request data
	 * is a JSON payload, this method could be used to create a request
	 * instance with the deserialized parameters.
	 *
	 * This method MUST be implemented in such a way as to retain the
	 * immutability of the message, and MUST return an instance that has the
	 * updated body parameters.
	 *
	 * @param null|array|object $data The deserialized body data. This will
	 *     typically be in an array or object.
	 * @return static
	 * @throws \InvalidArgumentException if an unsupported argument type is
	 *     provided.
	 */
	public function withParsedBody($data);

	/**
	 * Retrieve attributes derived from the request.
	 *
	 * The request "attributes" may be used to allow injection of any
	 * parameters derived from the request: e.g., the results of path
	 * match operations; the results of decrypting cookies; the results of
	 * deserializing non-form-encoded message bodies; etc. Attributes
	 * will be application and request specific, and CAN be mutable.
	 *
	 * @return mixed[] Attributes derived from the request.
	 */
	public function getAttributes();

	/**
	 * Retrieve a single derived request attribute.
	 *
	 * Retrieves a single derived request attribute as described in
	 * getAttributes(). If the attribute has not been previously set, returns
	 * the default value as provided.
	 *
	 * This method obviates the need for a hasAttribute() method, as it allows
	 * specifying a default value to return if the attribute is not found.
	 *
	 * @see getAttributes()
	 * @param string $name The attribute name.
	 * @param mixed $default Default value to return if the attribute does not exist.
	 * @return mixed
	 */
	public function getAttribute($name, $default = null);

	/**
	 * Return an instance with the specified derived request attribute.
	 *
	 * This method allows setting a single derived request attribute as
	 * described in getAttributes().
	 *
	 * This method MUST be implemented in such a way as to retain the
	 * immutability of the message, and MUST return an instance that has the
	 * updated attribute.
	 *
	 * @see getAttributes()
	 * @param string $name The attribute name.
	 * @param mixed $value The value of the attribute.
	 * @return static
	 */
	public function withAttribute($name, $value);

	/**
	 * Return an instance that removes the specified derived request attribute.
	 *
	 * This method allows removing a single derived request attribute as
	 * described in getAttributes().
	 *
	 * This method MUST be implemented in such a way as to retain the
	 * immutability of the message, and MUST return an instance that removes
	 * the attribute.
	 *
	 * @see getAttributes()
	 * @param string $name The attribute name.
	 * @return static
	 */
	public function withoutAttribute($name);
}

3 Psr\Http\Message\ResponseInterface

<?php
namespace Psr\Http\Message;

/**
 * Representation of an outgoing, server-side response.
 *
 * Per the HTTP specification, this interface includes properties for
 * each of the following:
 *
 * - Protocol version
 * - Status code and reason phrase
 * - Headers
 * - Message body
 *
 * Responses are considered immutable; all methods that might change state MUST
 * be implemented such that they retain the internal state of the current
 * message and return an instance that contains the changed state.
 */
interface ResponseInterface extends MessageInterface
{
	/**
	 * Gets the response status code.
	 *
	 * The status code is a 3-digit integer result code of the server's attempt
	 * to understand and satisfy the request.
	 *
	 * @return int Status code.
	 */
	public function getStatusCode();

	/**
	 * Return an instance with the specified status code and, optionally, reason phrase.
	 *
	 * If no reason phrase is specified, implementations MAY choose to default
	 * to the RFC 7231 or IANA recommended reason phrase for the response's
	 * status code.
	 *
	 * This method MUST be implemented in such a way as to retain the
	 * immutability of the message, and MUST return an instance that has the
	 * updated status and reason phrase.
	 *
	 * @see http://tools.ietf.org/html/rfc7231#section-6
	 * @see http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
	 * @param int $code The 3-digit integer result code to set.
	 * @param string $reasonPhrase The reason phrase to use with the
	 *     provided status code; if none is provided, implementations MAY
	 *     use the defaults as suggested in the HTTP specification.
	 * @return static
	 * @throws \InvalidArgumentException For invalid status code arguments.
	 */
	public function withStatus($code, $reasonPhrase = '');

	/**
	 * Gets the response reason phrase associated with the status code.
	 *
	 * Because a reason phrase is not a required element in a response
	 * status line, the reason phrase value MAY be empty. Implementations MAY
	 * choose to return the default RFC 7231 recommended reason phrase (or those
	 * listed in the IANA HTTP Status Code Registry) for the response's
	 * status code.
	 *
	 * @see http://tools.ietf.org/html/rfc7231#section-6
	 * @see http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
	 * @return string Reason phrase; must return an empty string if none present.
	 */
	public function getReasonPhrase();
}

4 Psr\Http\Message\StreamInterface

<?php
namespace Psr\Http\Message;

/**
 * Describes a data stream.
 *
 * Typically, an instance will wrap a PHP stream; this interface provides
 * a wrapper around the most common operations, including serialization of
 * the entire stream to a string.
 */
interface StreamInterface
{
	/**
	 * Reads all data from the stream into a string, from the beginning to end.
	 *
	 * This method MUST attempt to seek to the beginning of the stream before
	 * reading data and read the stream until the end is reached.
	 *
	 * Warning: This could attempt to load a large amount of data into memory.
	 *
	 * This method MUST NOT raise an exception in order to conform with PHP's
	 * string casting operations.
	 *
	 * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
	 * @return string
	 */
	public function __toString();

	/**
	 * Closes the stream and any underlying resources.
	 *
	 * @return void
	 */
	public function close();

	/**
	 * Separates any underlying resources from the stream.
	 *
	 * After the stream has been detached, the stream is in an unusable state.
	 *
	 * @return resource|null Underlying PHP stream, if any
	 */
	public function detach();

	/**
	 * Get the size of the stream if known.
	 *
	 * @return int|null Returns the size in bytes if known, or null if unknown.
	 */
	public function getSize();

	/**
	 * Returns the current position of the file read/write pointer
	 *
	 * @return int Position of the file pointer
	 * @throws \RuntimeException on error.
	 */
	public function tell();

	/**
	 * Returns true if the stream is at the end of the stream.
	 *
	 * @return bool
	 */
	public function eof();

	/**
	 * Returns whether or not the stream is seekable.
	 *
	 * @return bool
	 */
	public function isSeekable();

	/**
	 * Seek to a position in the stream.
	 *
	 * @see http://www.php.net/manual/en/function.fseek.php
	 * @param int $offset Stream offset
	 * @param int $whence Specifies how the cursor position will be calculated
	 *     based on the seek offset. Valid values are identical to the built-in
	 *     PHP $whence values for `fseek()`.  SEEK_SET: Set position equal to
	 *     offset bytes SEEK_CUR: Set position to current location plus offset
	 *     SEEK_END: Set position to end-of-stream plus offset.
	 * @throws \RuntimeException on failure.
	 */
	public function seek($offset, $whence = SEEK_SET);

	/**
	 * Seek to the beginning of the stream.
	 *
	 * If the stream is not seekable, this method will raise an exception;
	 * otherwise, it will perform a seek(0).
	 *
	 * @see seek()
	 * @see http://www.php.net/manual/en/function.fseek.php
	 * @throws \RuntimeException on failure.
	 */
	public function rewind();

	/**
	 * Returns whether or not the stream is writable.
	 *
	 * @return bool
	 */
	public function isWritable();

	/**
	 * Write data to the stream.
	 *
	 * @param string $string The string that is to be written.
	 * @return int Returns the number of bytes written to the stream.
	 * @throws \RuntimeException on failure.
	 */
	public function write($string);

	/**
	 * Returns whether or not the stream is readable.
	 *
	 * @return bool
	 */
	public function isReadable();

	/**
	 * Read data from the stream.
	 *
	 * @param int $length Read up to $length bytes from the object and return
	 *     them. Fewer than $length bytes may be returned if underlying stream
	 *     call returns fewer bytes.
	 * @return string Returns the data read from the stream, or an empty string
	 *     if no bytes are available.
	 * @throws \RuntimeException if an error occurs.
	 */
	public function read($length);

	/**
	 * Returns the remaining contents in a string
	 *
	 * @return string
	 * @throws \RuntimeException if unable to read.
	 * @throws \RuntimeException if error occurs while reading.
	 */
	public function getContents();

	/**
	 * Get stream metadata as an associative array or retrieve a specific key.
	 *
	 * The keys returned are identical to the keys returned from PHP's
	 * stream_get_meta_data() function.
	 *
	 * @see http://php.net/manual/en/function.stream-get-meta-data.php
	 * @param string $key Specific metadata to retrieve.
	 * @return array|mixed|null Returns an associative array if no key is
	 *     provided. Returns a specific key value if a key is provided and the
	 *     value is found, or null if the key is not found.
	 */
	public function getMetadata($key = null);
}

5 Psr\Http\Message\UriInterface

<?php
namespace Psr\Http\Message;

/**
 * Value object representing a URI.
 *
 * This interface is meant to represent URIs according to RFC 3986 and to
 * provide methods for most common operations. Additional functionality for
 * working with URIs can be provided on top of the interface or externally.
 * Its primary use is for HTTP requests, but may also be used in other
 * contexts.
 *
 * Instances of this interface are considered immutable; all methods that
 * might change state MUST be implemented such that they retain the internal
 * state of the current instance and return an instance that contains the
 * changed state.
 *
 * Typically the Host header will also be present in the request message.
 * For server-side requests, the scheme will typically be discoverable in the
 * server parameters.
 *
 * @see http://tools.ietf.org/html/rfc3986 (the URI specification)
 */
interface UriInterface
{
	/**
	 * Retrieve the scheme component of the URI.
	 *
	 * If no scheme is present, this method MUST return an empty string.
	 *
	 * The value returned MUST be normalized to lowercase, per RFC 3986
	 * Section 3.1.
	 *
	 * The trailing ":" character is not part of the scheme and MUST NOT be
	 * added.
	 *
	 * @see https://tools.ietf.org/html/rfc3986#section-3.1
	 * @return string The URI scheme.
	 */
	public function getScheme();

	/**
	 * Retrieve the authority component of the URI.
	 *
	 * If no authority information is present, this method MUST return an empty
	 * string.
	 *
	 * The authority syntax of the URI is:
	 *
	 * <pre>
	 * [user-info@]host[:port]
	 * </pre>
	 *
	 * If the port component is not set or is the standard port for the current
	 * scheme, it SHOULD NOT be included.
	 *
	 * @see https://tools.ietf.org/html/rfc3986#section-3.2
	 * @return string The URI authority, in "[user-info@]host[:port]" format.
	 */
	public function getAuthority();

	/**
	 * Retrieve the user information component of the URI.
	 *
	 * If no user information is present, this method MUST return an empty
	 * string.
	 *
	 * If a user is present in the URI, this will return that value;
	 * additionally, if the password is also present, it will be appended to the
	 * user value, with a colon (":") separating the values.
	 *
	 * The trailing "@" character is not part of the user information and MUST
	 * NOT be added.
	 *
	 * @return string The URI user information, in "username[:password]" format.
	 */
	public function getUserInfo();

	/**
	 * Retrieve the host component of the URI.
	 *
	 * If no host is present, this method MUST return an empty string.
	 *
	 * The value returned MUST be normalized to lowercase, per RFC 3986
	 * Section 3.2.2.
	 *
	 * @see http://tools.ietf.org/html/rfc3986#section-3.2.2
	 * @return string The URI host.
	 */
	public function getHost();

	/**
	 * Retrieve the port component of the URI.
	 *
	 * If a port is present, and it is non-standard for the current scheme,
	 * this method MUST return it as an integer. If the port is the standard port
	 * used with the current scheme, this method SHOULD return null.
	 *
	 * If no port is present, and no scheme is present, this method MUST return
	 * a null value.
	 *
	 * If no port is present, but a scheme is present, this method MAY return
	 * the standard port for that scheme, but SHOULD return null.
	 *
	 * @return null|int The URI port.
	 */
	public function getPort();

	/**
	 * Retrieve the path component of the URI.
	 *
	 * The path can either be empty or absolute (starting with a slash) or
	 * rootless (not starting with a slash). Implementations MUST support all
	 * three syntaxes.
	 *
	 * Normally, the empty path "" and absolute path "/" are considered equal as
	 * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically
	 * do this normalization because in contexts with a trimmed base path, e.g.
	 * the front controller, this difference becomes significant. It's the task
	 * of the user to handle both "" and "/".
	 *
	 * The value returned MUST be percent-encoded, but MUST NOT double-encode
	 * any characters. To determine what characters to encode, please refer to
	 * RFC 3986, Sections 2 and 3.3.
	 *
	 * As an example, if the value should include a slash ("/") not intended as
	 * delimiter between path segments, that value MUST be passed in encoded
	 * form (e.g., "%2F") to the instance.
	 *
	 * @see https://tools.ietf.org/html/rfc3986#section-2
	 * @see https://tools.ietf.org/html/rfc3986#section-3.3
	 * @return string The URI path.
	 */
	public function getPath();

	/**
	 * Retrieve the query string of the URI.
	 *
	 * If no query string is present, this method MUST return an empty string.
	 *
	 * The leading "?" character is not part of the query and MUST NOT be
	 * added.
	 *
	 * The value returned MUST be percent-encoded, but MUST NOT double-encode
	 * any characters. To determine what characters to encode, please refer to
	 * RFC 3986, Sections 2 and 3.4.
	 *
	 * As an example, if a value in a key/value pair of the query string should
	 * include an ampersand ("&") not intended as a delimiter between values,
	 * that value MUST be passed in encoded form (e.g., "%26") to the instance.
	 *
	 * @see https://tools.ietf.org/html/rfc3986#section-2
	 * @see https://tools.ietf.org/html/rfc3986#section-3.4
	 * @return string The URI query string.
	 */
	public function getQuery();

	/**
	 * Retrieve the fragment component of the URI.
	 *
	 * If no fragment is present, this method MUST return an empty string.
	 *
	 * The leading "#" character is not part of the fragment and MUST NOT be
	 * added.
	 *
	 * The value returned MUST be percent-encoded, but MUST NOT double-encode
	 * any characters. To determine what characters to encode, please refer to
	 * RFC 3986, Sections 2 and 3.5.
	 *
	 * @see https://tools.ietf.org/html/rfc3986#section-2
	 * @see https://tools.ietf.org/html/rfc3986#section-3.5
	 * @return string The URI fragment.
	 */
	public function getFragment();

	/**
	 * Return an instance with the specified scheme.
	 *
	 * This method MUST retain the state of the current instance, and return
	 * an instance that contains the specified scheme.
	 *
	 * Implementations MUST support the schemes "http" and "https" case
	 * insensitively, and MAY accommodate other schemes if required.
	 *
	 * An empty scheme is equivalent to removing the scheme.
	 *
	 * @param string $scheme The scheme to use with the new instance.
	 * @return static A new instance with the specified scheme.
	 * @throws \InvalidArgumentException for invalid schemes.
	 * @throws \InvalidArgumentException for unsupported schemes.
	 */
	public function withScheme($scheme);

	/**
	 * Return an instance with the specified user information.
	 *
	 * This method MUST retain the state of the current instance, and return
	 * an instance that contains the specified user information.
	 *
	 * Password is optional, but the user information MUST include the
	 * user; an empty string for the user is equivalent to removing user
	 * information.
	 *
	 * @param string $user The user name to use for authority.
	 * @param null|string $password The password associated with $user.
	 * @return static A new instance with the specified user information.
	 */
	public function withUserInfo($user, $password = null);

	/**
	 * Return an instance with the specified host.
	 *
	 * This method MUST retain the state of the current instance, and return
	 * an instance that contains the specified host.
	 *
	 * An empty host value is equivalent to removing the host.
	 *
	 * @param string $host The hostname to use with the new instance.
	 * @return static A new instance with the specified host.
	 * @throws \InvalidArgumentException for invalid hostnames.
	 */
	public function withHost($host);

	/**
	 * Return an instance with the specified port.
	 *
	 * This method MUST retain the state of the current instance, and return
	 * an instance that contains the specified port.
	 *
	 * Implementations MUST raise an exception for ports outside the
	 * established TCP and UDP port ranges.
	 *
	 * A null value provided for the port is equivalent to removing the port
	 * information.
	 *
	 * @param null|int $port The port to use with the new instance; a null value
	 *     removes the port information.
	 * @return static A new instance with the specified port.
	 * @throws \InvalidArgumentException for invalid ports.
	 */
	public function withPort($port);

	/**
	 * Return an instance with the specified path.
	 *
	 * This method MUST retain the state of the current instance, and return
	 * an instance that contains the specified path.
	 *
	 * The path can either be empty or absolute (starting with a slash) or
	 * rootless (not starting with a slash). Implementations MUST support all
	 * three syntaxes.
	 *
	 * If an HTTP path is intended to be host-relative rather than path-relative
	 * then it must begin with a slash ("/"). HTTP paths not starting with a slash
	 * are assumed to be relative to some base path known to the application or
	 * consumer.
	 *
	 * Users can provide both encoded and decoded path characters.
	 * Implementations ensure the correct encoding as outlined in getPath().
	 *
	 * @param string $path The path to use with the new instance.
	 * @return static A new instance with the specified path.
	 * @throws \InvalidArgumentException for invalid paths.
	 */
	public function withPath($path);

	/**
	 * Return an instance with the specified query string.
	 *
	 * This method MUST retain the state of the current instance, and return
	 * an instance that contains the specified query string.
	 *
	 * Users can provide both encoded and decoded query characters.
	 * Implementations ensure the correct encoding as outlined in getQuery().
	 *
	 * An empty query string value is equivalent to removing the query string.
	 *
	 * @param string $query The query string to use with the new instance.
	 * @return static A new instance with the specified query string.
	 * @throws \InvalidArgumentException for invalid query strings.
	 */
	public function withQuery($query);

	/**
	 * Return an instance with the specified URI fragment.
	 *
	 * This method MUST retain the state of the current instance, and return
	 * an instance that contains the specified URI fragment.
	 *
	 * Users can provide both encoded and decoded fragment characters.
	 * Implementations ensure the correct encoding as outlined in getFragment().
	 *
	 * An empty fragment value is equivalent to removing the fragment.
	 *
	 * @param string $fragment The fragment to use with the new instance.
	 * @return static A new instance with the specified fragment.
	 */
	public function withFragment($fragment);

	/**
	 * Return the string representation as a URI reference.
	 *
	 * Depending on which components of the URI are present, the resulting
	 * string is either a full URI or relative reference according to RFC 3986,
	 * Section 4.1. The method concatenates the various components of the URI,
	 * using the appropriate delimiters:
	 *
	 * - If a scheme is present, it MUST be suffixed by ":".
	 * - If an authority is present, it MUST be prefixed by "//".
	 * - The path can be concatenated without delimiters. But there are two
	 *   cases where the path has to be adjusted to make the URI reference
	 *   valid as PHP does not allow to throw an exception in __toString():
	 *     - If the path is rootless and an authority is present, the path MUST
	 *       be prefixed by "/".
	 *     - If the path is starting with more than one "/" and no authority is
	 *       present, the starting slashes MUST be reduced to one.
	 * - If a query is present, it MUST be prefixed by "?".
	 * - If a fragment is present, it MUST be prefixed by "#".
	 *
	 * @see http://tools.ietf.org/html/rfc3986#section-4.1
	 * @return string
	 */
	public function __toString();
}

6 Psr\Http\Message\UploadedFileInterface

<?php
namespace Psr\Http\Message;

/**
 * Value object representing a file uploaded through an HTTP request.
 *
 * Instances of this interface are considered immutable; all methods that
 * might change state MUST be implemented such that they retain the internal
 * state of the current instance and return an instance that contains the
 * changed state.
 */
interface UploadedFileInterface
{
	/**
	 * Retrieve a stream representing the uploaded file.
	 *
	 * This method MUST return a StreamInterface instance, representing the
	 * uploaded file. The purpose of this method is to allow utilizing native PHP
	 * stream functionality to manipulate the file upload, such as
	 * stream_copy_to_stream() (though the result will need to be decorated in a
	 * native PHP stream wrapper to work with such functions).
	 *
	 * If the moveTo() method has been called previously, this method MUST raise
	 * an exception.
	 *
	 * @return StreamInterface Stream representation of the uploaded file.
	 * @throws \RuntimeException in cases when no stream is available.
	 * @throws \RuntimeException in cases when no stream can be created.
	 */
	public function getStream();

	/**
	 * Move the uploaded file to a new location.
	 *
	 * Use this method as an alternative to move_uploaded_file(). This method is
	 * guaranteed to work in both SAPI and non-SAPI environments.
	 * Implementations must determine which environment they are in, and use the
	 * appropriate method (move_uploaded_file(), rename(), or a stream
	 * operation) to perform the operation.
	 *
	 * $targetPath may be an absolute path, or a relative path. If it is a
	 * relative path, resolution should be the same as used by PHP's rename()
	 * function.
	 *
	 * The original file or stream MUST be removed on completion.
	 *
	 * If this method is called more than once, any subsequent calls MUST raise
	 * an exception.
	 *
	 * When used in an SAPI environment where $_FILES is populated, when writing
	 * files via moveTo(), is_uploaded_file() and move_uploaded_file() SHOULD be
	 * used to ensure permissions and upload status are verified correctly.
	 *
	 * If you wish to move to a stream, use getStream(), as SAPI operations
	 * cannot guarantee writing to stream destinations.
	 *
	 * @see http://php.net/is_uploaded_file
	 * @see http://php.net/move_uploaded_file
	 * @param string $targetPath Path to which to move the uploaded file.
	 * @throws \InvalidArgumentException if the $targetPath specified is invalid.
	 * @throws \RuntimeException on any error during the move operation.
	 * @throws \RuntimeException on the second or subsequent call to the method.
	 */
	public function moveTo($targetPath);

	/**
	 * Retrieve the file size.
	 *
	 * Implementations SHOULD return the value stored in the "size" key of
	 * the file in the $_FILES array if available, as PHP calculates this based
	 * on the actual size transmitted.
	 *
	 * @return int|null The file size in bytes or null if unknown.
	 */
	public function getSize();

	/**
	 * Retrieve the error associated with the uploaded file.
	 *
	 * The return value MUST be one of PHP's UPLOAD_ERR_XXX constants.
	 *
	 * If the file was uploaded successfully, this method MUST return
	 * UPLOAD_ERR_OK.
	 *
	 * Implementations SHOULD return the value stored in the "error" key of
	 * the file in the $_FILES array.
	 *
	 * @see http://php.net/manual/en/features.file-upload.errors.php
	 * @return int One of PHP's UPLOAD_ERR_XXX constants.
	 */
	public function getError();

	/**
	 * Retrieve the filename sent by the client.
	 *
	 * Do not trust the value returned by this method. A client could send
	 * a malicious filename with the intention to corrupt or hack your
	 * application.
	 *
	 * Implementations SHOULD return the value stored in the "name" key of
	 * the file in the $_FILES array.
	 *
	 * @return string|null The filename sent by the client or null if none
	 *     was provided.
	 */
	public function getClientFilename();

	/**
	 * Retrieve the media type sent by the client.
	 *
	 * Do not trust the value returned by this method. A client could send
	 * a malicious media type with the intention to corrupt or hack your
	 * application.
	 *
	 * Implementations SHOULD return the value stored in the "type" key of
	 * the file in the $_FILES array.
	 *
	 * @return string|null The media type sent by the client or null if none
	 *     was provided.
	 */
	public function getClientMediaType();
}

相关英文原文请查看:PSR-7 HTTP message interfaces

超媒体链接逐渐地正在成为网络的重要一部分( in both HTML contexts and various API format contexts)。 但是,并没有一个单独定义的一般性超媒体格式,也没有表示它们间链接的一般方式。

本条规范旨在为PHP开发者提供一个简单的、一般性的方式去单独地表示一个超媒体链接, 使用了序列化的格式。 That in turn allows a system to serialize a response with hypermedia links into one or more wire formats independently of the process of deciding what those links should be.

详述

  1. 基本链接 Basic links

    一个超媒体链接包含(最小化形式): 一个表示引用目标资源的URI,一个定义目前资源与源之间的关系。

    还存在其他各种的其他属性,根据格式来使用。因为额外的属性并不是well-standardized或者通用的 (universal),所以本规范并不追求规范化它们。

    为了这个目标,本规范应用了如下定义:

    • 实现对象(Implementing Object):一个实现了本规范定义接口的对象。

    • 序列化器(Serializer):一个库或者其它系统,它们使用一个或多个链接对象, 并提供其序列化为一些规定形式的表示。

  2. 属性 Attributes

    所有的链接可以包含0个或多个除了URI和关联关系外的额外属性。这里没有规定这些值的格式, 且它们的合法性依赖于上下文以及常常取决于特别的序列化格式。常见支持的属性包括: ‘hreflang’, ‘title’, 和 ‘type’

    序列化器(Serializer)在序列化格式需要的情况下可以省略一个链接对象的属性。 但是,序列化器应该尽可能编码所有提供的属性,以允许用户拓展,除非受到序列化格式定义的阻碍。

    一些属性(常见的hreflang)可能会在上下文中出现多于一次。因此, 一个属性值可以是一个数组形式的值,而不是单独一个值。序列化器可以编码这个数组为任意合适 的序列化形式(例如一个空格分割的list,逗号分隔的list等)。 如果一个属性在上下文环境中不被允许有多个值,序列化器必须使用提供的第一个值, 忽略掉随后的其他所有值。

    如果一个属性的值是boolean类型的true,序列化器可以使用适合且支持的简短格式。例如, HTML准许boolean类型的属性没有值(no value)。这个规定只适用于属性boolean类型的true, 并不适用于其他“为真”的值,例如整型的1。

    如果一个属性的值是boolean类型的false,序列化器应该完全省略这个属性, 除非这样做改变了结果的语义。这个规定只适用于属性为boolean类型的false, 并不适用于其他“为假”的值,例如整型的0。

  3. 关系 Relationships

    链接的关系被定义为一个字符串,要么是一个简单的关键词(在公开定义的关系的情况下), 或者是一个绝对的URI(在私有关系的情况下)。

    In case a simple keyword is used, it SHOULD match one from the IANA registry at:

    http://www.iana.org/assignments/link-relations/link-relations.xhtml

    Optionally the microformats.org registry MAY be used, but this may not be valid in every context:

    http://microformats.org/wiki/existing-rel-values

    A relationship that is not defined in one of the above registries or a similar public registry is considered “private”, that is, specific to a particular application or use case. Such relationships MUST use an absolute URI.

  4. 链接模板 Link Templates

    RFC 6570 defines a format for URI templates, that is, a pattern for a URI that is expected to be filled in with values provided by a client tool. Some hypermedia formats support templated links while others do not, and may have a special way to denote that a link is a template. A Serializer for a format that does not support URI Templates MUST ignore any templated Links it encounters.

  5. Evolvable providers

    In some cases, a Link Provider may need the ability to have additional links added to it. In others, a link provider is necessarily read-only, with links derived at runtime from some other data source. For that reason, modifiable providers are a secondary interface that may optionally be implemented.

    Additionally, some Link Provider objects, such as PSR-7 Response objects, are by design immutable. That means methods to add links to them in-place would be incompatible. Therefore, the EvolvableLinkProviderInterface’s single method requires that a new object be returned, identical to the original but with an additional link object included.

  6. Evolvable link objects

    Link objects are in most cases value objects. As such, allowing them to evolve in the same fashion as PSR-7 value objects is a useful option. For that reason, an additional EvolvableLinkInterface is included that provides methods to produce new object instances with a single change. The same model is used by PSR-7 and, thanks to PHP’s copy-on-write behavior, is still CPU and memory efficient.

    There is no evolvable method for templated, however, as the templated value of a link is based exclusively on the href value. It MUST NOT be set independently, but derived from whether or not the href value is an RFC 6570 link template.

Package

The interfaces and classes described are provided as part of the psr/link package.

Interfaces

Psr\Link\LinkInterface

<?php

namespace Psr\Link;

/**
 * A readable link object.
 */
interface LinkInterface
{
    /**
     * Returns the target of the link.
     *
     * The target link must be one of:
     * - An absolute URI, as defined by RFC 5988.
     * - A relative URI, as defined by RFC 5988. The base of the relative link
     *     is assumed to be known based on context by the client.
     * - A URI template as defined by RFC 6570.
     *
     * If a URI template is returned, isTemplated() MUST return True.
     *
     * @return string
     */
    public function getHref();

    /**
     * Returns whether or not this is a templated link.
     *
     * @return bool
     *   True if this link object is templated, False otherwise.
     */
    public function isTemplated();

    /**
     * Returns the relationship type(s) of the link.
     *
     * This method returns 0 or more relationship types for a link, expressed
     * as an array of strings.
     *
     * @return string[]
     */
    public function getRels();

    /**
     * Returns a list of attributes that describe the target URI.
     *
     * @return array
     *   A key-value list of attributes, where the key is a string and the value
     *  is either a PHP primitive or an array of PHP strings. If no values are
     *  found an empty array MUST be returned.
     */
    public function getAttributes();
}

Psr\Link\EvolvableLinkInterface

<?php

namespace Psr\Link;

/**
 * An evolvable link value object.
 */
interface EvolvableLinkInterface extends LinkInterface
{
    /**
     * Returns an instance with the specified href.
     *
     * @param string $href
     *   The href value to include.  It must be one of:
     *     - An absolute URI, as defined by RFC 5988.
     *     - A relative URI, as defined by RFC 5988. The base of the relative link
     *       is assumed to be known based on context by the client.
     *     - A URI template as defined by RFC 6570.
     *     - An object implementing __toString() that produces one of the above
     *       values.
     *
     * An implementing library SHOULD evaluate a passed object to a string
     * immediately rather than waiting for it to be returned later.
     *
     * @return static
     */
    public function withHref($href);

    /**
     * Returns an instance with the specified relationship included.
     *
     * If the specified rel is already present, this method MUST return
     * normally without errors, but without adding the rel a second time.
     *
     * @param string $rel
     *   The relationship value to add.
     * @return static
     */
    public function withRel($rel);

    /**
     * Returns an instance with the specified relationship excluded.
     *
     * If the specified rel is already not present, this method MUST return
     * normally without errors.
     *
     * @param string $rel
     *   The relationship value to exclude.
     * @return static
     */
    public function withoutRel($rel);

    /**
     * Returns an instance with the specified attribute added.
     *
     * If the specified attribute is already present, it will be overwritten
     * with the new value.
     *
     * @param string $attribute
     *   The attribute to include.
     * @param string $value
     *   The value of the attribute to set.
     * @return static
     */
    public function withAttribute($attribute, $value);


    /**
     * Returns an instance with the specified attribute excluded.
     *
     * If the specified attribute is not present, this method MUST return
     * normally without errors.
     *
     * @param string $attribute
     *   The attribute to remove.
     * @return static
     */
    public function withoutAttribute($attribute);
}

Psr\Link\LinkProviderInterface

<?php

namespace Psr\Link;

/**
 * A link provider object.
 */
interface LinkProviderInterface
{
    /**
     * Returns an iterable of LinkInterface objects.
     *
     * The iterable may be an array or any PHP \Traversable object. If no links
     * are available, an empty array or \Traversable MUST be returned.
     *
     * @return LinkInterface[]|\Traversable
     */
    public function getLinks();

    /**
     * Returns an iterable of LinkInterface objects that have a specific relationship.
     *
     * The iterable may be an array or any PHP \Traversable object. If no links
     * with that relationship are available, an empty array or \Traversable MUST be returned.
     *
     * @return LinkInterface[]|\Traversable
     */
    public function getLinksByRel($rel);
}

Psr\Link\EvolvableLinkProviderInterface

<?php

namespace Psr\Link;

/**
 * An evolvable link provider value object.
 */
interface EvolvableLinkProviderInterface extends LinkProviderInterface
{
    /**
     * Returns an instance with the specified link included.
     *
     * If the specified link is already present, this method MUST return normally
     * without errors. The link is present if $link is === identical to a link
     * object already in the collection.
     *
     * @param LinkInterface $link
     *   A link object that should be included in this collection.
     * @return static
     */
    public function withLink(LinkInterface $link);

    /**
     * Returns an instance with the specifed link removed.
     *
     * If the specified link is not present, this method MUST return normally
     * without errors. The link is present if $link is === identical to a link
     * object already in the collection.
     *
     * @param LinkInterface $link
     *   The link to remove.
     * @return static
     */
    public function withoutLink(LinkInterface $link);
}

相关英文原文请查看:PSR-13 Link definition interfaces

参考文献

PHP Standards Recommendations

php:// 语言参考

Uniform_Resource_Identifier


评论