riimu/kit-urlparser

符合RFC 3986规范的URL解析库,包含PSR-7 Uri组件

v2.2.0 2022-12-13 18:51 UTC

This package is auto-updated.

Last update: 2024-09-14 17:12:43 UTC


README

UrlParser 是一个PHP库,提供符合RFC 3986规范的URL解析器和符合PSR-7的URI组件。该库的目的是提供一个准确实现RFC规范的解析器,与内置函数parse_url()不同,后者在某些细微方面与规范有差异。

该库有两个主要目的。第一个是提供解析URL的信息。为了实现这一点,库实现了PSR-7的标准URI处理接口,并提供了使从URL中检索常用信息更简单的方法。第二个目的是允许使用PSR-7标准接口修改URL,以及一些使某些任务更直接的方法。

虽然这个库主要打算用于解析URL,但解析是基于通用的URI语法的。因此,可以使用这个库来验证和解析任何其他类型的URI与通用语法的兼容性。库不对URL执行任何特定方案的验证。

除了默认的RFC 3986兼容模式之外,该库还提供了一些选项,允许解析包含UTF-8字符的URL,同时将它们转换为适当的百分编码和IDN ascii格式。

API文档可在以下位置找到: http://kit.riimu.net/api/urlparser/

CI Scrutinizer codecov Packagist

要求

  • 最低支持的PHP版本是5.6
  • 该库依赖于以下外部PHP库
  • 该库依赖于以下PHP扩展
    • intl (仅需要IDN支持)

安装

使用Composer安装

安装此库最简单的方法是使用Composer处理依赖项。为了通过Composer安装此库,请按照以下两个步骤操作

  1. 通过在你的项目根目录中运行Composer的命令行安装来获取composer.phar

  2. 一旦运行了安装脚本,你应该在你的项目根目录中有一个composer.phar文件,然后你可以运行以下命令

    php composer.phar require "riimu/kit-urlparser:^2.1"
    

通过Composer安装此库后,您可以通过包含由Composer在安装过程中生成的vendor/autoload.php文件来加载库。

将库作为依赖项添加

如果您已经熟悉如何使用Composer,您还可以通过将以下composer.json文件添加到您的项目并运行composer install命令来将库作为依赖项添加。

{
    "require": {
        "riimu/kit-urlparser": "^2.1"
    }
}

手动安装

如果您不想使用Composer来加载库,您也可以手动下载库。通过下载最新版本并提取src文件夹到您的项目中。然后,您可以通过包含提供的src/autoload.php文件来加载库类。

请注意,使用Composer还会自动下载其他所需的PHP库。如果您手动安装此库,您还需要提供这些其他所需的库。

使用方法

使用此库相对简单。该库提供了一个URL解析类UriParser和一个不可变值对象类Uri,用于表示URL。要解析URL,您可以将URL作为字符串提供给UriParser中的parse()方法,该方法返回一个由解析的URL生成的Uri实例。

例如

<?php

require 'vendor/autoload.php';

$parser = new \Riimu\Kit\UrlParser\UriParser();
$uri = $parser->parse('http://www.example.com');

echo $uri->getHost(); // Outputs 'www.example.com'

或者,您也可以完全不使用UriParser,只需将URL作为构造函数参数提供给Uri

<?php

require 'vendor/autoload.php';
$uri = new \Riimu\Kit\UrlParser\Uri('http://www.example.com');
echo $uri->getHost(); // Outputs 'www.example.com'

使用parse()方法和构造函数之间的主要区别在于,如果提供的URL不是一个有效的URL,则parse()方法将返回一个null,而构造函数将抛出InvalidArgumentException异常。

要从中检索不同类型的URL信息,Uri类提供了各种不同的方法来帮助您。以下是一个概述不同可用方法的简单示例

<?php

require 'vendor/autoload.php';

$parser = new \Riimu\Kit\UrlParser\UriParser();
$uri = $parser->parse('http://jane:pass123@www.example.com:8080/site/index.php?action=login&prev=index#form');

echo $uri->getScheme() . PHP_EOL;         // outputs: http
echo $uri->getUsername() . PHP_EOL;       // outputs: jane
echo $uri->getPassword() . PHP_EOL;       // outputs: pass123
echo $uri->getHost() . PHP_EOL;           // outputs: www.example.com
echo $uri->getTopLevelDomain() . PHP_EOL; // outputs: com
echo $uri->getPort() . PHP_EOL;           // outputs: 8080
echo $uri->getStandardPort() . PHP_EOL;   // outputs: 80
echo $uri->getPath() . PHP_EOL;           // outputs: /site/index.php
echo $uri->getPathExtension() . PHP_EOL;  // outputs: php
echo $uri->getQuery() . PHP_EOL;          // outputs: action=login&prev=index
echo $uri->getFragment() . PHP_EOL;       // outputs: form

print_r($uri->getPathSegments());    // [0 => 'site', 1 => 'index.php']
print_r($uri->getQueryParameters()); // ['action' => 'login', 'prev' => 'index']

Uri组件也提供了各种修改URL的方法,允许您从单独的组件构造新的URL或修改现有的URL。请注意,Uri组件是一个不可变值对象,这意味着每个修改方法都会返回一个新的Uri实例,而不是修改现有的实例。以下是一个从组件构造URL的简单示例

<?php

require 'vendor/autoload.php';

$uri = (new \Riimu\Kit\UrlParser\Uri())
    ->withScheme('http')
    ->withUserInfo('jane', 'pass123')
    ->withHost('www.example.com')
    ->withPort(8080)
    ->withPath('/site/index.php')
    ->withQueryParameters(['action' => 'login', 'prev' => 'index'])
    ->withFragment('form');

// Outputs: http://jane:pass123@www.example.com:8080/site/index.php?action=login&prev=index#form
echo $uri;

如前一个示例所示,Uri组件还提供了一个__toString()方法,该方法提供了URL字符串。

检索信息

以下是Uri组件提供的用于从URL检索信息的方法列表

  • getScheme()从URL返回方案或如果没有方案则为空字符串。

  • getAuthority()返回URL中包含用户信息、主机名和端口的组件,格式为user-info@hostname:port

  • getUserInfo()返回包含用户名和密码的组件,两者之间由冒号分隔。

  • getUsername()返回URL中的解码用户名,如果URL中没有用户名则为空字符串。

  • getPassword()返回URL中的解码密码,如果URL中没有密码则为空字符串。

  • getHost()返回URL中的主机名,如果URL中没有主机则为空字符串。

  • getIpAddress()如果主机是IP地址,则返回主机中的IP地址。如果不是IP地址,此方法将返回null。如果提供了IPv6地址,则返回地址而不包含周围的括号。

  • getTopLevelDomain()返回主机中的顶级域名。如果没有主机或主机是IP地址,则返回空字符串。

  • getPort()返回URL中的端口号,如果URL中没有端口号则为null。如果端口号是当前方案的标准端口号(例如,http的80),则此方法也将返回null

  • getStandardPort()返回当前方案的标准端口号。如果没有方案或方案的标准端口号未知,则返回null

  • getPath()返回URL中的路径,如果URL中没有路径则为空字符串。

  • getPathSegments() 返回一个解码后的路径段数组(即每个正斜杠分割的路径)。空路径段会被丢弃,不包含在返回的数组中。

  • getPathExtension() 从路径返回文件扩展名,或者如果没有路径则返回空字符串。

  • getQuery() 返回 URL 的查询字符串,如果没有查询字符串则返回空字符串。

  • getQueryParameters() 使用 parse_str() 函数解析 URL 的查询字符串,并返回解析值数组。

  • getFragment() 返回 URL 的片段,如果没有片段则返回空字符串。

  • __toString() 返回 URL 的字符串表示形式。

修改 URL

Uri 组件提供了各种方法,可用于修改 URL 和构建新的 URL。请注意,由于 Uri 类是一个不可变值对象,每个方法都返回一个新的 Uri 实例,而不是修改现有的实例。

  • withScheme($scheme) 返回具有给定方案的新的实例。可以使用空方案从 URL 中删除方案。注意,任何提供的方案都会被转换为小写。

  • withUserInfo($user, $password = null) 返回具有给定用户名和密码的新实例。请注意,除非提供了用户名,否则密码会被忽略。可以使用空用户名从 URL 中删除用户名和密码。任何不能单独插入 URL 的字符都会进行百分号编码。

  • withHost($host) 返回具有给定主机的新实例。可以使用空主机从 URL 中删除主机。注意,此方法不接受国际域名。注意,此方法还将主机转换为小写。

  • withPort($port) 返回具有给定端口的新的实例。可以使用 null 从 URL 中删除端口。

  • withPath($path) 返回具有给定路径的新实例。可以使用空路径从 URL 中删除路径。注意,任何不是有效路径字符的字符都会在 URL 中进行百分号编码。但是,现有的百分号编码字符不会进行双重编码。

  • withPathSegments(array $segments) 返回一个具有从路径段数组构造的路径的新实例。段中的所有无效路径字符(包括正斜杠和现有的百分号编码字符)都会进行百分号编码。

  • withQuery($query) 返回具有给定查询字符串的新实例。可以使用空查询字符串从 URL 中删除路径。注意,任何不是有效查询字符串字符的字符都会在 URL 中进行百分号编码。但是,现有的百分号编码字符不会进行双重编码。

  • withQueryParameters(array $parameters) 返回使用 http_build_query() 函数从提供的参数构造查询字符串的新实例。参数中的所有无效查询字符串字符(包括和号、等号和现有的百分号编码字符)都会进行百分号编码。

  • withFragment($fragment) 返回具有给定片段的新实例。可以使用空字符串从 URL 中删除片段。注意,任何不是有效片段字符的字符都会在 URL 中进行百分号编码。但是,现有的百分号编码字符不会进行双重编码。

UTF-8 和国际域名

默认情况下,此库提供了一个符合 RFC 3986 规范的解析器。RFC 规范不允许在域名或其他 URL 部分中使用 UTF-8 字符。这些字符在 URL 中的正确表示是使用 IDN 标准表示域名,并在其他部分中对 UTF-8 字符进行百分号编码。

然而,为了帮助您处理UTF-8编码的字符,Uri组件中的许多方法会自动对无法单独插入URL的任何字符进行百分号编码,包括UTF-8字符。然而,由于涉及到的复杂性,withHost()方法不允许UTF-8编码的字符。

默认情况下,解析器也不解析包含UTF-8编码字符的任何URL,因为这会违反RFC规范。但是,解析器提供了两种额外的解析模式,在可能的情况下允许这些字符。

如果您希望解析可能包含UTF-8字符的用户信息(即用户名或密码)、路径、查询或片段组件的URL,您只需使用UTF-8解析模式。例如

<?php

require 'vendor/autoload.php';

$parser = new \Riimu\Kit\UrlParser\UriParser();
$parser->setMode(\Riimu\Kit\UrlParser\UriParser::MODE_UTF8);

$uri = $parser->parse('http://www.example.com/föö/bär.html');
echo $uri->getPath(); // Outputs: /f%C3%B6%C3%B6/b%C3%A4r.html

然而,域名中的UTF-8字符是一个更为复杂的问题。但是,解析器提供了一种基本的IDNA模式来解析这些域名。例如

<?php

require 'vendor/autoload.php';

$parser = new \Riimu\Kit\UrlParser\UriParser();
$parser->setMode(\Riimu\Kit\UrlParser\UriParser::MODE_IDNA);

$uri = $parser->parse('http://www.fööbär.com');
echo $uri->getHost(); // Outputs: www.xn--fbr-rla2ga.com

请注意,使用此解析模式需要启用PHP扩展intl。也可以通过Uri组件构造函数的第二个参数提供适当的解析模式。

尽管支持解析这些UTF-8字符,但此库不提供任何反向操作的方法,因为此库的目的是处理符合RFC 3986的URI。

URL规范化

由于RFC 3986规范定义了一些URL,尽管有细微的差异,但它们是等效的,因此此库对提供的值进行了一些最小规范化。您可能会在例如解析用户提供的URL时遇到这些实例。您可能会遇到的最显著的规范化如下

  • schemehost组件被视为不区分大小写。因此,这些组件总是被规范化为小写。
  • 如果端口号是当前方案的默认端口号,则不会将其包含在getAuthority()__toString()返回的字符串中。
  • 百分号编码以不区分大小写的方式处理。因此,此库将十六进制字符规范化为大写。
  • 返回字符串中路径开头的正斜杠数量可能会根据URL是否具有authority组件而改变。
  • 由于UriParser与PSR-7规范一起工作,该规范不提供提供编码的用户名或密码的方式,因此解析和生成的URI中userinfo组件中的百分号编码字符可能会有所不同。

致谢

此库版权所有(c)2013-2022 Riikka Kalliomäki。

请参阅LICENSE以获取许可和复制信息。