编程 PHP 8.6 的 Uri 类型:把 parse_url 的推断换成枚举判断

2026-10-31 20:04:02

PHP 8.6 的 Uri 类型:把 parse_url 的推断换成枚举判断

PHP 8.6 新增了 Uri\Rfc3986 相关类型。它替代的核心场景很具体:判断一个字符串是不是绝对 URL、host 是不是 IP,过去靠 parse_url() 的数组加一点猜测,现在直接读类型。

从 parse_url 到 Uri::parse

PHP 8.5 的写法:

$parts = parse_url($input); // an array, or false
$isAbsolute = isset($parts['scheme']); // a guess about intent
$isIp = (bool) filter_var($parts['host'], FILTER_VALIDATE_IP); // another guess

PHP 8.6:

$uri = Uri\Rfc3986\Uri::parse($input);
$isAbsolute = Uri\Rfc3986\UriType::Uri === $uri->getUriType();
$isIp = in_array($uri->getHostType(), [Uri\Rfc3986\UriHostType::IpV4, Uri\Rfc3986\UriHostType::IpV6], true);

实测下来的分类是:

  • https://example.com/a → Uri
  • /a/b → AbsolutePathReference
  • ../a/b → RelativePathReference
  • //example.com/a → NetworkPathReference

host 一侧则分成 RegisteredName、IpV4、IpV6、IpVFuture。这些都是枚举,所以对引用类型做 match 时,检查是穷尽的,不必再写一长串字符串比较。

类都在命名空间下:Uri\Rfc3986\Uri、Uri\Rfc3986\UriHostType、Uri\Rfc3986\UriType。

三个会直接踩到的行为差异

一、parse() 失败返回 null,不是 false。 判空不能省:

Uri\Rfc3986\Uri::parse('https://a b.com/x'); // null

对着 null 调 getUriType(),报错是 Call to a member function getUriType() on null。沿用 if ($parts === false) 的习惯会在这里断掉。

二、空字符串和相对路径都能解析成功。 parse() 拒绝的是畸形输入,不是相对引用。所以「解析成功」不等于「是绝对 URL」——判断绝对性仍然要看 getUriType() 的结果。

三、拼接 URL 时,畸形路径会抛异常而不是产出坏 URL。 过去手工拼串,路径里混进空格,上线后就是一个 404;现在 setPath('/a b') 直接抛 Uri\InvalidUriException,消息为:

The specified path is malformed

UriBuilder

PHP 8.5 的手工拼接:

$url = 'https://'.$host.$path.'?'.http_build_query($query); // hope every part is already safe

PHP 8.6:

$url = (new Uri\Rfc3986\UriBuilder())
    ->setScheme('https')
    ->setHost($host)
    ->setPath($path)
    ->setQuery('page=2')
    ->build()
    ->toString();

各部件分别校验,错误在构造阶段暴露,而不是在生产环境的访问日志里。

百分号编码按去向区分上下文

编码函数放在 WHATWG 一侧,所以不是全局函数,而是带命名空间:

Uri\WhatWg\url_percent_encode('a b/c?d', Uri\WhatWg\UrlPercentEncodingMode::Path);
// 'a%20b/c%3Fd'

Uri\WhatWg\url_percent_encode('a b/c?d', Uri\WhatWg\UrlPercentEncodingMode::Query);
// 'a%20b/c?d'

同一个值,两个调用给出两个正确结果,取决于它要去的上下文。以前是在 urlencode() 和 rawurlencode() 之间记哪个该用在哪,选错就多编或漏编一个字符。

迁移与命名注意

可以先在「是不是绝对 URL / 是不是 IP」这类判断上把 parse_url() 换掉,改动面最小,收益也最直接。

另外,RC2 已按 PHP 命名规范把枚举 case 从 IPv4 改成 IpV4(GH-23851),这些枚举本身是 8.6 新增的,旧版本上没有对应写法。

信源:https://wolinski.com/blog/php-8-6-new-features.html

复制全文 生成海报 PHP 8.6 URL 处理

推荐文章

程序员茄子在线接单