
文档开发工具【免费下载链接】fig-standardsStandards either proposed or approved by the Framework Interop Group项目地址https://gitcode.com/gh_mirrors/fi/fig-standards点击查看免费下载Hypermedia links超媒体链接已成为现代 Web 的核心组成无论是以 HTML 形式还是以 HAL、JSON-LD、Atom 等各类 API 格式呈现。本指南以 PHP-FIG 已接受的 PSR-13 Meta 文档 为主体结合其规范正文中的完整接口定义系统讲解 PSR-13 的设计动机、四个核心接口、属性与关系语义以及 1.1/2.0 版本的类型演进帮助读者掌握如何用一套与序列化格式无关的通用接口表示、生成和消费超媒体链接。1. 背景为什么需要统一的超媒体链接表示超媒体链接在 Web 中的重要性正在持续上升它既出现在 HTML 场景中也广泛存在于各种 API 格式如 HAL、JSON-LD、Atom之中。然而整个行业并不存在一种统一的超媒体格式也不存在跨格式表示链接的通用方式。这意味着一个系统如果要把响应中的链接输出到多种线上格式wire format就必须针对每种格式分别实现一套链接语义。PSR-13 的目标正是为 PHP 开发者提供一种简单、通用、与序列化格式无关的超媒体链接表示方式。这样一来一个系统可以先独立地决定这个响应应该包含哪些链接再交由序列化层Serializer把这些链接对象输出为一种或多种线上格式两步解耦、互不干扰。正如 Meta 文档 Summary 所述This specification aims to provide PHP developers with a simple, common way of representing a hypermedia link independently of the serialization format that is used.从仓库的 PSR 索引 可以看到PSR-13Hypermedia Links由 Larry Garfield 维护当前状态为Accepted已接受其配套实现由psr/link包提供规范正文见 accepted/PSR-13-links.md。2. 范围界定做什么与不做什么Meta 文档第 2 节明确了 PSR-13 的边界目标Goals在不同格式之间抽取并标准化超媒体链接的表示方式。换句话说规范回答的是链接在 PHP 对象层面长什么样而不是链接在某一种格式里怎么写。非目标Non-Goals规范不试图标准化或偏袒任何一种特定的超媒体序列化格式。HAL、JSON-LD、Atom、HTML 之间的差异依然存在PSR-13 只负责让它们共享同一套对象模型。这一边界决定了 PSR-13 的接口非常克制它只定义链接是什么、如何读取、如何演化把如何渲染完全交给各个序列化器。3. 核心设计决策三个关键问题Meta 文档第 3 节记录了该规范形成过程中的三个关键设计决策理解它们等于理解了整个接口骨架的由来。3.1 为什么没有就地修改方法mutator methods一个重要的设计考量是PSR-13 的关键目标对象之一是PSR-7 Response 对象而 PSR-7 的设计要求 Response 对象不可变immutable。其他值对象value object实现也大概率需要不可变接口。另一方面某些 Link Provider 对象可能根本不是值对象而是某个领域对象——它能够基于数据库查询结果或其他底层表示即时生成链接。对于这类对象可写的 Provider 定义反而完全不兼容。因此PSR-13 把**访问方法accessor与可演化方法evolvable**拆分成两套独立接口允许实现者根据自己的用例只实现只读版本或可演化版本。这正是下文LinkInterface与EvolvableLinkInterface双接口并存的根本原因。3.2 为什么 rel关系在一个 Link 对象上是多值的不同的超媒体标准对相同关系的多个链接处理方式截然不同有的用一个链接带多个 rel有的用一个 rel 条目下面挂多个链接。PSR-13 选择每个 Link 对象唯一、但允许携带多个 rel作为最大兼容公约数most-compatible-denominator单个LinkInterface对象可以在某个超媒体格式中被序列化为一个或多个链接条目反过来多个 Link 对象共享同一个 URI、各带一个 rel也是合法的格式可以按需序列化。这种双向容忍让 PSR-13 能够适配尽可能多的既有格式。3.3 为什么需要LinkProviderInterface在很多场景下一组链接会依附于某个其他对象比如代表 HAL、JSON-LD、Atom 等各种 REST 格式的值对象而使用方往往只关心其中的链接或链接的子集。Meta 文档举了两个典型用例从某个对象中提取next/previous链接追加到 PSR-7 Response 的Link头中把大量链接表示为preload关系让兼容 HTTP/2 的 Web 服务器提前把被引用的资源推送给客户端为后续请求做准备。上述场景都与对象的载荷payload或编码无关。通过提供统一的链接访问接口PSR-13 让链接的通用处理成为可能无论产生链接的是值对象还是领域对象。4. 链接的构成URI、关系与属性在进入接口代码之前先明确 PSR-13 规范正文accepted/PSR-13-links.md 第 1 节对链接构成的定义一个超媒体链接至少包含URI被引用目标资源的地址与关系目标资源与源资源的关联方式链接还可以有零个或多个额外属性但属性缺乏统一注册表合法性依赖具体上下文和序列化格式因此规范不试图标准化它们。常见的属性包括hreflang、title、type。规范还定义了两种实现角色Implementing Object实现了本规范任一接口的对象Serializer接收一个或多个 Link 对象并输出某种格式序列化表示的库或系统。4.1 属性Attributes序列化规则序列化器在格式要求时可以省略属性但应当尽可能编码所有提供的属性以支持用户扩展某些属性如hreflang在上下文中可能多次出现因此属性值可以是数组序列化器可按格式特点编码空格分隔、逗号分隔等若某格式不允许多值序列化器必须取第一个值并忽略其余属性值为布尔true时序列化器可以在格式支持的情况下使用简写如 HTML 的无值属性该规则只适用于布尔true不适用于 PHP 中其他truthy值如整数1属性值为布尔false时序列化器应当完全省略该属性除非省略会改变语义该规则同样只适用于布尔false不适用于其他falsey值如整数0。4.2 关系Relationships链接关系以字符串表示分两类公开关系使用简单关键字keyword应当匹配 IANA Link Relations Registry 中的条目可选地也可以使用 microformats.org 的关系列表但后者并非在所有上下文都有效私有关系凡未在公共注册表中定义的关系视为应用或用例私有的必须使用绝对 URI 表示。4.3 链接模板Link TemplatesRFC 6570 定义了 URI 模板格式——一种期望由客户端工具填充值的 URI 模式。部分超媒体格式支持模板化链接部分不支持并有特殊的标记方式。规范要求不支持 URI 模板的序列化器必须忽略所遇到的模板化链接。5. 四个核心接口从只读到可演化PSR-13 规范正文第 3 节完整给出了四个接口的 PHP 定义这也是 Meta 文档中只读/可演化拆分设计决策的具体落地。以下代码可直接用于理解或作为实现的参考。5.1Psr\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(); }要点getHref()允许返回绝对 URIRFC 5988、相对 URI基准由客户端按上下文推定或 RFC 6570 URI 模板若返回模板isTemplated()必须返回truegetRels()返回零个或多个关系字符串数组对应上文多值 rel决策getAttributes()返回键为字符串、值为 PHP 基本类型或字符串数组的键值列表无属性时必须返回空数组。5.2Psr\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-7 值对象的返回新实例模式所有with*方法都返回static即与原始对象相同但只做了一处修改的新对象。得益于 PHP 的写时复制copy-on-write行为这种演化方式依然具备优秀的 CPU 与内存效率。注意一个细节withHref()接受实现__toString()的对象并建议实现库立即将其求值为字符串而不是延迟到返回之后。没有针对模板化值templated的演化方法因为链接的模板化状态完全取决于 href 值本身——它不能被独立设置只能由href 是否为 RFC 6570 URI 模板推导得出。5.3Psr\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); }getLinks()返回数组或任意\Traversable对象无链接时必须返回空集合getLinksByRel($rel)按关系过滤。这正是 Meta 文档 3.3 节所述从各种值对象/领域对象中统一抽取链接的落地接口。5.4Psr\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 specified 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); }注意与普通 Provider 的差异可演化 Provider 的withLink()/withoutLink()同样返回static新实例。原因在规范正文第 1.5 节写得很清楚——像 PSR-7 Response 这类对象按设计不可变就地添加链接的方法与之根本不兼容因此唯一的方法必须是返回一个与原来相同但多了一个 Link 的新对象。链接是否已存在、是否被移除都以全等比较为准。6. 演化模型为什么不可变 with*是合理选择Meta 文档与规范正文共同勾勒出一套完整的设计哲学链接对象在大多数情况下是值对象允许它们像 PSR-7 值对象一样演化是一种有用的能力因此引入EvolvableLinkInterface链接提供者分两类有的需要能往里追加链接有的天生只读链接运行时从其他数据源推导因此可修改的 Provider 是可选实现的次级接口模板化状态不可独立设置只能由 href 推导因此接口中不存在withTemplated()之类的方法。这套只读接口 可演化接口的双层结构与 PSR-7 一脉相承PSR-7 的规范背景可参见 accepted/PSR-7-http-message-meta.md。事实上这种事件/对象可演化的思路还被后续标准继承——PSR-14 Event Dispatcher Meta 文档 就明确要求 Event 可演化不可变但提供with*()方法如同 PSR-7 与 PSR-13。7. 版本演进与类型系统ErrataMeta 文档第 7 节记录了规范发布后的两处重要修订对实现者至关重要。7.1 类型补充Type additionspsr/link包的演进刻意采用了渐进式升级策略1.1 版本加入标量参数类型scalar parameter types2.0 版本加入返回类型并将array|\Traversable的引用替换为iterable该结构利用 PHP 7.2 的协变covariance支持实现渐进升级但要获得完整的类型兼容则需要PHP 8.0。实现者implementers的约束如下实现者可以自行在其包中加入返回类型前提是返回类型与 2.0 包一致且实现声明最低 PHP 版本为 8.0.0 或更高实现者可以在新的大版本中加入参数类型可与返回类型同时加入也可随后续版本加入前提是参数类型与 1.1 包一致、最低 PHP 版本为 8.0.0 或更高并且依赖声明为psr/link: ^1.1 || ^2.0以排除无类型的 1.0 版本鼓励但不强制实现者尽早向 2.0 版本过渡。7.2 属性类型处理Attribute type handling原始规范存在一处不一致规范正文第 1.2 节说明传给EvolvableLinkInterface::withAttribute()的属性值可以是多种类型其中部分允许特殊处理如布尔值或数组但该方法的 docblock 却错误地把$value参数限定为字符串。后续版本已修正接口允许$value为以下类型string|\Stringable|int|float|bool|array配套规则实现者应当将Stringable对象视同string参数处理实现者可以为特定序列化格式按类型感知的方式序列化int、float或bool其他对象类型与资源resource仍被禁止以相同$name多次调用withAttribute()必须覆盖先前的值规范第 1.2 节已有此要求要为某属性提供多个值请传入包含所需值的array第 1.2 节其余的所有准则与要求保持不变。8. 实战场景串联从领域对象到 HTTP 响应把以上内容串起来一个典型的 PSR-13 使用流如下领域层产出链接某个代表 HAL/JSON-LD/Atom 的值对象实现LinkProviderInterface基于底层数据即时返回链接集合getLinks()、getLinksByRel(next)通用处理中间件不关心该对象的载荷与编码只通过统一接口抽取链接——这正是 Meta 文档 3.3 节强调的通用处理落线上格式序列化器读取LinkInterface的getHref()、getRels()、getAttributes()、isTemplated()按目标格式输出HTMLlink、HTTPLink头、HAL_links、JSON-LD 等HTTP/2 预加载需要推送的资源以preload关系表达兼容 HTTP/2 的服务器据此提前流式传输被引用资源为后续请求做准备可演化路径若目标对象如 PSR-7 Response不可变则通过EvolvableLinkProviderInterface::withLink()返回新实例把额外链接追加到 Response 的Link头中。9. 参与背景与进一步阅读根据 Meta 文档第 4 节PSR-13 的 Editor 为 Larry GarfieldSponsors 为 Matthew Weier OPhinneycoordinator与 Marc AlexanderContributor 为 Evert Pot。该标准已列入 PSR 索引 的 Accepted 状态。若需继续深入仓库内可参考的资料包括完整接口定义与规范细节accepted/PSR-13-links.md设计决策与修订记录本文主体accepted/PSR-13-links-meta.md与 PSR-13 交互紧密的 PSR-7 背景accepted/PSR-7-http-message-meta.md继承其可演化值对象思路的 PSR-14accepted/PSR-14-event-dispatcher-meta.md。小结PSR-13 用四个接口、约十个方法为 PHP 世界确立了一套与序列化格式解耦的超媒体链接对象模型。它不定义任何格式只定义链接是什么、怎么读、怎么演化它不依赖可变对象而是借助 PSR-7 式的不可变with*()模式保持高效与兼容。理解它的三个核心设计决策——只读/可演化拆分、多值 rel、Provider 抽象——也就理解了整个标准的骨架。赞分享文档开发工具【免费下载链接】fig-standardsStandards either proposed or approved by the Framework Interop Group项目地址https://gitcode.com/gh_mirrors/fi/fig-standards点击查看免费下载相关推荐PSR-13 超媒体链接接口Hypermedia Links实战指南用 psr/link 统一表示与序列化超媒体链接PSR 13 超媒体链接接口Hypermedia Links实战指南用 psr/link 统一表示与序列化超媒体链接 PSR 13Hypermedia文档开发工具React-Tween-State缓动函数完全解析从easeInQuad到easeInOutBounce的30种动画曲线React Tween State缓动函数完全解析从easeInQuad到easeInOutBounce的30种动画曲线 React Tween State前端PSR-13权威指南构建PHP标准化HTTP链接的完整实践方案PSR 13权威指南构建PHP标准化HTTP链接的完整实践方案 引言终结PHP链接管理的碎片化困境 你是否正在为不同PHP框架间的链接处理兼容性问题而头疼后端上一篇Hyperresearch lint规则完整清单scaffold-prompt、locus-coverage等结构性校验下一篇终极文件搜索神器ripgrep-all与fzf集成打造极速检索工作流创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考