exteon / uri
一组用于解析、组合和操作 URI 的对象
Requires (Dev)
- phpunit/phpunit: ^8.5
README
提供一组用于解析、组合和操作 URI 的对象
解析 URI 字符串
use Exteon\Uri\Uri; $uri = Uri::fromString('http://user:pass@foo.bar:8080/path/to?query#fragment'); var_dump( $uri->getScheme(), $uri->getUser(), $uri->getPass(), $uri->getHost(), $uri->getPort(), $uri->getPath(), $uri->getQueryString(), $uri->getFragment() );
生成 URI 字符串
use Exteon\Uri\Uri; $uri = new Uri( 'http', 'foo.bar', '8080', 'user', 'pass', 'path/to', 'query', 'fragment' ); var_dump($uri->toString());
注意
未实现神奇的 __toString() 方法,我认为它的语义不一致,因此请使用显式的 toString()。
URI 操作
注意
Uri 对象不是不可变的;在进行操作之前,请确保在适当的地方使用 clone。
设置器
您可以使用 setScheme(), setUser(), setPass(), setHost(), setPort(), setPath(), setQueryString(), setFragment() 设置器来修改 URI。
基础和相对 URL,组合
Uri 对象可以用来将相对 URI 组合到基础 URI。
use Exteon\Uri\Uri; $baseUri = Uri::fromString('scheme://host/root/?query#fragment'); $uri = Uri::fromString('a'); $uri->composeWithBase($baseUri); echo $uri->toString(); // scheme://host/root/a $uri = Uri::fromString('/a'); $uri->composeWithBase($baseUri); echo $uri->toString(); // scheme://host/a $uri = Uri::fromString('://host2/a'); $uri->composeWithBase($baseUri); echo $uri->toString(); // scheme://host2/a $uri = Uri::fromString(''); $uri->composeWithBase($baseUri); echo $uri->toString(); // scheme://host/root/?query#fragment $uri = Uri::fromString('#fragment2'); $uri->composeWithBase($baseUri); echo $uri->toString(); // scheme://host/root/?query#fragment2 $uri = Uri::fromString('?query2'); $uri->composeWithBase($baseUri); echo $uri->toString(); // scheme://host/root/?query2 $uri = Uri::fromString('?query2#fragment2'); $uri->composeWithBase($baseUri); echo $uri->toString(); // scheme://host/root/?query2#fragment2 $baseUri = Uri::fromString('scheme://host/root/a?query#fragment'); $uri = Uri::fromString('b'); $uri->composeWithBase($baseUri); echo $uri->toString(); // scheme://host/root/b
composeWithBase() 的逆操作是 applyRelative(),当在基础 URI 上以相对 URI 作为参数运行时,它是非常对称的。
相对 URL 也可以从绝对 URL 和基础 URL 中导出
use Exteon\Uri\Uri; $baseUri = Uri::fromString('scheme://host/a/b?query#fragment'); $uri = Uri::fromString('scheme://host/a/b?query#fragment'); $uri->makeRelativeToBase($baseUri); echo $uri->toString(); // $uri = Uri::fromString('scheme://host/a/b?query'); $uri->makeRelativeToBase($baseUri); echo $uri->toString(); // # $uri = Uri::fromString('scheme://host/a/b'); $uri->makeRelativeToBase($baseUri); echo $uri->toString(); // ? $uri = Uri::fromString('scheme://host/a/b#fragment'); $uri->makeRelativeToBase($baseUri); echo $uri->toString(); // ?#fragment $uri = Uri::fromString('scheme://host/a/b?query#fragment2'); $uri->makeRelativeToBase($baseUri); echo $uri->toString(); // #fragment2 $uri = Uri::fromString('scheme://host/a/b?query2#fragment'); $uri->makeRelativeToBase($baseUri); echo $uri->toString(); // ?query2#fragment $uri = Uri::fromString('scheme://host/a/c?query#fragment'); $uri->makeRelativeToBase($baseUri); echo $uri->toString(); // c?query#fragment $uri = Uri::fromString('scheme://host/a?query#fragment'); $uri->makeRelativeToBase($baseUri); echo $uri->toString(); // /a?query#fragment $uri = Uri::fromString('scheme://host2/a/b?query#fragment'); $uri->makeRelativeToBase($baseUri); echo $uri->toString(); // ://host2/a/b?query#fragment $baseUri = Uri::fromString('scheme:a/b'); $uri = Uri::fromString('scheme:a'); $uri->makeRelativeToBase($baseUri); echo $uri->toString(); // :a
注意
Uri 将允许解析和生成形式为 ://host/path 的相对空方案 URI,这些 URI 在 RFC 3986 中是不允许的,但在浏览器 URL 中是常见的,用于指定“与浏览页面相同的协议”。
注意
请注意路径中的尾部 /;该库使用浏览器组合算法:一个相对 URL bar 与基础 URL foo 和 foo/ 组合将分别产生 bar 和 foo/bar。此例外是 UnixPathUri。
ascend() 和 descend()
虽然 RFC 3986 中没有出现,但 URI 的路径部分已成为一种非常通用的约定,即通过 / 分隔的分层组织组件的序列。为了满足这一约定,所有 URI 对象都实现了 ascend() 和 descend() 函数。
public function ascend(int $levels = 1): self;
use Exteon\Uri\Uri; $uri = Uri::fromString('a/b?qs'); $uri->ascend(); var_dump($uri->toString()); // a/
注意
使用 ascend() 时,生成的 URI 总是会被视为目录,并且有尾部斜杠。
public function descend(string $path): self;
use Exteon\Uri\Uri; $uri = Uri::fromString('a/b?qs'); $uri->descend('c/d'); var_dump($uri->toString()); // a/c/d
注意
使用 descend() 时,Web 协议,将递归路径应用于 URI 目录,如上面的示例所示。因为 a/b 不包含尾部斜杠,所以目录部分是 a/,然后将 c/d 应用到其中。
上述描述的例外是 UnixPathUri。
PHP URI
使用 Uri,查询字符串没有被赋予任何特殊含义。您可以使用 PHPUri 对象来解析和生成 PHP 查询字符串
use Exteon\Uri\PhpUri; $initial = '?foo=bar'; $uri = PhpUri::fromString($initial); var_dump($uri->getQuery()); // ['foo' => 'bar']
use Exteon\Uri\PhpUri; $uri = new PhpUri(); $uri->setQuery(['foo' => 'bar']); echo $uri->toString(); // ?foo=bar
注意
PHP 的 http_build_query() 在查询字符串聚合时静默丢弃 NULL 值的键。为了保持一致性,您必须显式处理 NULL 值。为此,PHPUri 提供了两个辅助方法:nullValuesToEmptyString() 和 nullValuesUnset()(默认 PHP 行为),您可以在将查询传递给构造函数或 setQuery() 之前使用这些方法。如果传递给 setQuery() 的数组包含 NULL 值,则将抛出 InvalidArgumentException。
Unix 路径 URI
UnixPathUri 模型了一个只能有路径部分(没有方案、主机、查询字符串、片段)的 URI。它们可以像 Web URI 一样组合,但有一个相当重要的不同约定。
对于UnixPathUri,即使URI没有尾部斜杠,getDirectory()和getPath()也表示相同的资源。
示例
use Exteon\Uri\UnixPathUri; $uri = UnixPathUri::fromString('/a/b'); var_dump($uri->getPath()); // /a/b var_dump($uri->getDirectory()); // /a/b/ // Uri::getDirectory() would have been "/a/"
这将对相对URI的应用和派生方式产生影响。例如
use Exteon\Uri\UnixPathUri; $base = UnixPathUri::fromString('/a'); $relative = UnixPathUri::fromString('b'); $relative->composeWithBase($base); var_dump($relative->toString()); // /a/b // With Uri, the result would have been "/b"
以更直观的方式,UnixPathUri URI类型就像Unix shell路径一样,相对URI就像在基本URI上运行cd命令。显然没有实现特殊的路径片段如.和..,你可以使用ascend()方法来模拟进入上级目录。
UnixPathUri可以与其他类型的URI结合(即相对UnixPathUri可以通过Uri::applyRelative()与根WebUri结合),从而得到完整的Web URI。