编程 PHP 8.5 的 URI 扩展:用 Uri\Rfc3986\Uri 和 Uri\WhatWg\Url 替掉 parse_url

2026-10-08 21:02:59

PHP 8.5 的 URI 扩展:用 Uri\Rfc3986\Uri 和 Uri\WhatWg\Url 替掉 parse_url

PHP 8.5 新增内置 URI 扩展,按 RFC 3986 和 WHATWG URL 两套标准解析、规范化、修改 URL。底层分别由 uriparser(RFC 3986)和 Lexbor(WHATWG URL)驱动。

parse_url 的问题

parse_url() 自 PHP 4 就存在,它不遵循任何标准,官方文档明确警告不要用于不可信或畸形 URL。

一个能复现的例子:输入 example.com/example/:8080/foo。

  • 按 RFC 3986,这是一个只含相对路径的合法 URL。
  • 按 WHATWG,在没有 base 的情况下它是非法输入。
  • 而 parse_url() 给出 host=example.com、port=8080、path=/example/:8080/foo——把 8080 同时放进了两个组件里。

新扩展里对应两个类,两者不可互换:

  • Uri\Rfc3986\Uri:遵循 RFC 3986,通用 URI、严格校验、可选规范化、在安全的地方做百分号解码;可以没有 scheme。提供 raw 视图与规范化解码视图两种读法。
  • Uri\WhatWg\Url:遵循 WHATWG URL,对齐浏览器行为,支持 IDNA/Unicode 主机、解析期变换、软错误与硬错误;必须有 scheme。多数组件保留百分号编码,解析时自动规范化。

二者都支持解析、解析相对引用(resolve)、读写组件、比较、序列化。

解析与失败

use Uri\Rfc3986\Uri;

$rfc = Uri::parse("https://example.com/path?x=1"); // Uri 或 null
$bad = Uri::parse("invalid uri"); // null(严格失败)

$errors = [];
$whatwg = Uri\WhatWg\Url::parse(" invalid url", null, $errors); // null 且带软/硬错误

RFC 3986 侧是严格失败:解析不出来就是 null。WHATWG 侧走浏览器的软错误模型,失败时仍会通过 $errors 带回具体问题。

WHATWG 类在组件层面保留编码:

new Url("HTTPS://%61pple:p%61ss@ex%61mple.com:433/foob%61r?%61bc=%61bc#%61bc");
// getScheme()   => https
// getUsername() => %61pple
// getPath()     => /foob%61r
// getQuery()    => %61bc=%61bc

IDNA/Unicode 主机只在 WHATWG 侧提供:

new Url("https://🐘.com");
// getAsciiHost()   => xn--go8h.com
// getUnicodeHost() => 🐘.com

不可变与 with-er

两个类都是不可变的,修改组件通过 withScheme()、withPort() 这类 with-er 返回新实例:

use Uri\Rfc3986\Uri;

$url = new Uri('HTTPS://thephp.foundation:443/sp%6Fnsor/');
$url = $url->withPort(null); // 去掉默认端口

// 取值器默认规范化;Raw 变体返回输入原样
echo $url->toRawString(); // HTTPS://thephp.foundation/sp%6Fnsor/

重定向与校验

$uri = Uri::parse($incoming);
if ($uri === null) {
    http_response_code(400);
    exit;
}

$secure = $uri->withScheme("https");
if (!$secure->equals($uri, UriComparisonMode::ExcludeFragment)) {
    header("Location: " . $secure->toString(), true, 301);
    exit;
}

取舍

规范化视图适合做路由和缓存键——同一个资源的不同写法会收敛到一个形式。raw 视图保留精确字节,适合签名校验或转发代理:任何重新编码都可能让签名失效,所以这类场景必须走 raw。

WHATWG 那套对齐浏览器行为,代价是它的结果不是 RFC 3986 的合法子集,Unicode 域名和自动百分号编码都由它接管;反过来,它的软错误模型意味着你得主动检查 $errors 才知道输入哪里不规范。RFC 3986 那套更严格,但浏览器不按它走。两边都留着,是因为要处理的 URL 来源本来就分这两类。

顺带修掉的一个老问题:FILTER_VALIDATE_URL 与客户端遵循 RFC 3986 之间的不一致,会引发上面那种解析混淆。

链接

  • PHP 8.5 发布说明:https://www.php.net/releases/8.5/zh.php
  • URI 扩展手册:https://www.php.net/manual/zh/book.uri.php
  • The PHP Foundation 公告:https://thephp.foundation/blog/2025/10/10/php-85-uri-extension/
  • Amit Merchant 的介绍:https://www.amitmerchant.com/the-new-standards-compliant-uri-url-api-in-php-85/
复制全文 生成海报 PHP URI parse_url RFC3986 WHATWG

推荐文章

程序员茄子在线接单