处理 URI

CodeIgniter 提供了面向对象的方式处理应用中的 URI。使用该类可轻松确保 URI 结构始终正确, 无论 URI 多复杂都能应对,同时也支持将相对 URI 安全地解析到已有 URI 中。

创建 URI 实例

创建 URI 实例就像创建一个新的类实例一样简单。

创建新实例时,可以在构造函数中传入完整或部分 URL,将解析为对应的各部分:

$uri = new \CodeIgniter\HTTP\URI('http://www.example.com/some/path');

或者,也可以使用 service() 函数返回实例:

$uri = service('uri', 'http://www.example.com/some/path');

v4.4.0 起,如果不传入 URL,将返回当前 URI:

$uri = service('uri'); // returns the current SiteURI instance.

备注

上述代码返回 SiteURI 实例,该类继承自 URI 类。 URI 类用于通用 URI,而 SiteURI 类用于站点 URI。

当前 URI

当需要一个表示当前请求 URL 的对象时,可使用 current_url() 函数,该函数位于 URL 辅助函数 中:

$uri = current_url(true);

必须传入 true 作为第一个参数,否则将返回当前 URL 的字符串表示。

该 URI 基于当前请求对象和 Config\App 中的配置(baseURLindexPageforceGlobalSecureRequests)确定的路径(相对于 baseURL)。

假设在继承 CodeIgniter\Controller 的控制器中,也可以这样获取当前 SiteURI 实例:

$uri = $this->request->getUri();

URI 字符串

很多时候,只需获取 URI 的字符串表示。将 URI 转为字符串即可:

<?php

$uri = current_url(true);
echo (string) $uri;  // http://example.com/index.php

如果已知 URI 的各部分,只想确保格式正确,可以使用 URI 类的静态方法 createURIString() 生成字符串:

<?php

use CodeIgniter\HTTP\URI;

$uriString = URI::createURIString($scheme, $authority, $path, $query, $fragment);

// Creates: http://exmample.com/some/path?foo=bar#first-heading
echo URI::createURIString('http', 'example.com', 'some/path', 'foo=bar', 'first-heading');

重要

URI 转为字符串时,会根据 Config\App 中定义的配置自动调整项目 URL。 如果需要精确且未经调整的版本,请改用 URI::createURIString()

URI 各部分

获取 URI 实例后,可以设置或获取 URI 的各个部分。本节将介绍这些部分的含义及操作方法。

Scheme

Scheme 通常是 'http' 或 'https',但也支持其他 Scheme,如 'file'、'mailto' 等。

<?php

$uri = new \CodeIgniter\HTTP\URI('http://www.example.com/some/path');

echo $uri->getScheme(); // 'http'
$uri->setScheme('https');

Authority

许多 URI 包含多个统称为 'Authority' 的元素,包括用户信息、主机名和端口号。 可以使用 getAuthority() 方法将其作为单个字符串获取,也可以操作各独立部分。

<?php

$uri = new \CodeIgniter\HTTP\URI('ftp://user:password@example.com:21/some/path');

echo $uri->getAuthority();  // user@example.com:21

默认情况下不会显示密码部分。如需显示密码,可使用 showPassword() 方法。 该 URI 实例将持续显示密码,直到关闭该功能,因此使用完毕后务必立即关闭:

<?php

echo $uri->getAuthority();  // user@example.com:21
echo $uri->showPassword()->getAuthority();   // user:password@example.com:21

// Turn password display off again.
$uri->showPassword(false);

如果不想显示端口,传入 true 作为唯一参数:

<?php

echo $uri->getAuthority(true);  // user@example.com

备注

如果当前端口是该 Scheme 的默认端口,则永远不会显示。

UserInfo

UserInfo 部分就是 FTP URI 中可能看到的用户名和密码。虽然可以作为 Authority 的一部分获取, 也可以单独获取:

<?php

echo $uri->getUserInfo();   // user

默认不显示密码,但可通过 showPassword() 方法覆盖此行为:

<?php

echo $uri->showPassword()->getUserInfo();   // user:password
$uri->showPassword(false);

Host

URI 的 Host 部分通常是域名。使用 getHost()setHost() 方法可以轻松设置和获取:

<?php

$uri = new \CodeIgniter\HTTP\URI('http://www.example.com/some/path');

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

Port

Port 是 0 到 65535 之间的整数。每个 Scheme 都有对应的默认值。

<?php

$uri = new \CodeIgniter\HTTP\URI('ftp://user:password@example.com:21/some/path');

echo $uri->getPort();   // 21
echo $uri->setPort(2201)->getPort(); // 2201

使用 setPort() 方法时,会检查端口是否在有效范围内,然后赋值。

Path

Path 是站点内部的所有段。正如预期的那样,可以使用 getPath()setPath() 方法进行操作:

<?php

$uri = new \CodeIgniter\HTTP\URI('http://www.example.com/some/path');

echo $uri->getPath();                            // '/some/path'
echo $uri->setPath('/another/path')->getPath();  // '/another/path'

备注

设置 Path 时,会进行清理以编码危险字符,并移除点号段(dot segment)以确保安全。

备注

v4.4.0 起,SiteURI::getRoutePath() 方法返回相对于 baseURL 的 URI 路径, 而 SiteURI::getPath() 方法始终返回带前导 / 的完整 URI 路径。

Query

可以通过类提供的简单字符串表示法来操作 Query 数据。

获取/设置 Query

当前 Query 值只能以字符串形式设置。

<?php

$uri = new \CodeIgniter\HTTP\URI('http://www.example.com?foo=bar');

echo $uri->getQuery();  // 'foo=bar'
$uri->setQuery('foo=bar&bar=baz');

setQuery() 方法会覆盖现有的查询变量。

备注

Query 值不能包含 Fragment。如果包含,将抛出 InvalidArgumentException。

从数组设置 Query

可以使用数组设置 Query 值:

<?php

$uri->setQueryArray(['foo' => 'bar', 'bar' => 'baz']);

setQueryArray() 方法会覆盖现有的 Query 变量。

添加 Query 值

使用 addQuery() 方法可以向 Query 变量集合添加值,而不会破坏现有 Query 变量。 第一个参数是变量名,第二个参数是值:

<?php

$uri->addQuery('foo', 'bar');

过滤 Query 值

可以向 getQuery() 方法传入包含 onlyexcept 键的选项数组来过滤返回的 Query 值:

<?php

$uri = new \CodeIgniter\HTTP\URI('http://www.example.com?foo=bar&bar=baz&baz=foz');

// Returns 'foo=bar'
echo $uri->getQuery(['only' => ['foo']]);

// Returns 'foo=bar&baz=foz'
echo $uri->getQuery(['except' => ['bar']]);

这仅改变本次调用返回的值。

修改 Query 值

如需更永久地修改 URI 的 Query 值,可以使用 stripQuery()keepQuery() 方法 来更改对象实际的 Query 变量集合:

<?php

$uri = new \CodeIgniter\HTTP\URI('http://www.example.com?foo=bar&bar=baz&baz=foz');

// Leaves just the 'baz' variable
$uri->stripQuery('foo', 'bar');

// Leaves just the 'foo' variable
$uri->keepQuery('foo');

备注

默认情况下,setQuery()setQueryArray() 方法使用原生 parse_str() 函数准备数据。 如需使用更宽松的规则(允许键名包含点),可提前调用专用方法 useRawQueryString()

Fragment

Fragment 是 URL 末尾由井号(#)开头的部分。在 HTML URL 中,用于链接到页面内的锚点。 而在媒体 URI 中,则可能有多种其他用途。

<?php

$uri = new \CodeIgniter\HTTP\URI('http://www.example.com/some/path#first-heading');

echo $uri->getFragment();   // 'first-heading'
echo $uri->setFragment('second-heading')->getFragment();    // 'second-heading'

URI 段

路径中每两个斜杠之间的部分就是一个段。

备注

对于站点 URI,URI 段仅指相对于 baseURL 的 URI 路径部分。 如果 baseURL 包含子文件夹,其值将与当前 URI 路径不同。

URI 类提供了一种简单的方法来确定各段的值。段从 1 开始计数,最左侧的段为 1。

<?php

// URI = http://example.com/users/15/profile

// Prints '15'
if ($uri->getSegment(1) === 'users') {
    echo $uri->getSegment(2);
}

也可以使用 getSegment() 方法的第二个参数为特定段设置不同的默认值。默认值为空字符串。

<?php

// URI = http://example.com/users/15/profile

// will print 'profile'
echo $uri->getSegment(3, 'foo');
// will print 'bar'
echo $uri->getSegment(4, 'bar');
// will throw an exception
echo $uri->getSegment(5, 'baz');
// will print 'baz'
echo $uri->setSilent()->getSegment(5, 'baz');
// will print '' (empty string)
echo $uri->setSilent()->getSegment(5);

备注

可以获取最后一个段的下一个段。尝试获取最后一个段的下两个或更后面的段时, 默认会抛出异常。可通过 setSilent() 方法阻止抛出异常。

可以获取段的总数:

<?php

$total = $uri->getTotalSegments(); // 3

最后,还可以获取包含所有段的数组:

<?php

$segments = $uri->getSegments();

/*
 * Produces:
 * [
 *     0 => 'users',
 *     1 => '15',
 *     2 => 'profile',
 * ]
 */

禁用抛出异常

默认情况下,该类的某些方法可能会抛出异常。如需禁用,可设置特殊标志阻止抛出异常。

<?php

// Disable throwing exceptions
$uri->setSilent();

// Enable throwing exceptions (default)
$uri->setSilent(false);