PHP Conference Japan 2024

parse_url

(PHP 4, PHP 5, PHP 7, PHP 8)

parse_url解析 URL 并返回其组成部分

描述

parse_url(字符串 $url, 整数 $component = -1): 整数|字符串|数组||false

此函数解析 URL 并返回一个关联数组,其中包含 URL 中存在的各种组成部分。数组元素的值 *未* 进行 URL 解码。

此函数 *并非* 用于验证给定的 URL,它只将其分解为下面列出的部分。部分和无效的 URL 也被接受,parse_url() 尽力正确解析它们。

警告

对于相对 URL 或无效 URL,此函数可能无法给出正确的结果,结果甚至可能与 HTTP 客户端的常用行为不符。如果需要解析来自不受信任输入的 URL,则需要额外的验证,例如使用 filter_var()FILTER_VALIDATE_URL 过滤器。

参数

url

要解析的 URL。

component

指定 PHP_URL_SCHEMEPHP_URL_HOSTPHP_URL_PORTPHP_URL_USERPHP_URL_PASSPHP_URL_PATHPHP_URL_QUERYPHP_URL_FRAGMENT 之一,以检索仅作为 字符串 的特定 URL 组成部分(除非给出 PHP_URL_PORT,在这种情况下,返回值将为 整数)。

返回值

对于严重格式错误的 URL,parse_url() 可能会返回 false

如果省略 component 参数,则返回一个关联 数组。数组中至少存在一个元素。此数组中的潜在键为

  • scheme - 例如 http
  • host
  • port
  • user
  • pass
  • path
  • query - 问号 ? 之后
  • fragment - 井号 # 之后

如果指定了 component 参数,parse_url() 将返回一个 字符串(或者在 PHP_URL_PORT 的情况下为 整数),而不是 数组。如果给定 URL 中不存在请求的组件,则将返回 null。从 PHP 8.0.0 开始,parse_url() 区分不存在和为空的查询和片段。

http://example.com/foo → query = null, fragment = null
http://example.com/foo? → query = "",   fragment = null
http://example.com/foo# → query = null, fragment = ""
http://example.com/foo?# → query = "",   fragment = ""

以前所有情况都会导致 query 和 fragment 为 null

请注意,组件中的控制字符(参见 ctype_cntrl())将被下划线 (_) 替换。

变更日志

版本 描述
8.0.0 parse_url() 现在将区分不存在和为空的查询和片段。

示例

示例 #1 parse_url() 示例

<?php
$url
= 'http://username:password@hostname:9090/path?arg=value#anchor';

var_dump(parse_url($url));
var_dump(parse_url($url, PHP_URL_SCHEME));
var_dump(parse_url($url, PHP_URL_USER));
var_dump(parse_url($url, PHP_URL_PASS));
var_dump(parse_url($url, PHP_URL_HOST));
var_dump(parse_url($url, PHP_URL_PORT));
var_dump(parse_url($url, PHP_URL_PATH));
var_dump(parse_url($url, PHP_URL_QUERY));
var_dump(parse_url($url, PHP_URL_FRAGMENT));
?>

以上示例将输出

array(8) {
  ["scheme"]=>
  string(4) "http"
  ["host"]=>
  string(8) "hostname"
  ["port"]=>
  int(9090)
  ["user"]=>
  string(8) "username"
  ["pass"]=>
  string(8) "password"
  ["path"]=>
  string(5) "/path"
  ["query"]=>
  string(9) "arg=value"
  ["fragment"]=>
  string(6) "anchor"
}
string(4) "http"
string(8) "username"
string(8) "password"
string(8) "hostname"
int(9090)
string(5) "/path"
string(9) "arg=value"
string(6) "anchor"

示例 #2 缺少 scheme 的 parse_url() 示例

<?php
$url
= '//www.example.com/path?googleguy=googley';

// 在 5.4.7 之前,这将显示路径为 "//www.example.com/path"
var_dump(parse_url($url));
?>

以上示例将输出

array(3) {
  ["host"]=>
  string(15) "www.example.com"
  ["path"]=>
  string(5) "/path"
  ["query"]=>
  string(17) "googleguy=googley"
}

注释

注意:

此函数专门用于解析 URL,而不是 URI。但是,为了符合 PHP 的向后兼容性要求,它对 file:// 方案(允许使用三个斜杠 (file:///...))进行了例外处理。对于任何其他方案,这都是无效的。

参见

添加笔记

用户贡献笔记 38条笔记

thomas at gielfeldt dot com
13年前
如果您还没有找到从解析后的URL简单转换回字符串的方法,这里有一个示例

<?php

$url
= 'http://usr:[email protected]:81/mypath/myfile.html?a=b&b[]=2&b[]=3#myfragment';
if (
$url === unparse_url(parse_url($url))) {
print
"YES, they match!\n";
}

function
unparse_url($parsed_url) {
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
$port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
$user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
$pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
$pass = ($user || $pass) ? "$pass@" : '';
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
$query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
return
"$scheme$user$pass$host$port$path$query$fragment";
}

?>
lauris () lauris ! lv
10年前
这是一个基于“laszlo dot janszky at gmail dot com”作品的兼容UTF-8的parse_url()替换函数。原函数错误地处理了带有用户:密码的URL。也使其兼容PHP 5.5(去除了现在已弃用的正则表达式/e修饰符)。

<?php

/**
* 支持UTF-8的parse_url()替换函数。
*
* @return array
*/
function mb_parse_url($url)
{
$enc_url = preg_replace_callback(
'%[^:/@?&=#]+%usD',
function (
$matches)
{
return
urlencode($matches[0]);
},
$url
);

$parts = parse_url($enc_url);

if(
$parts === false)
{
throw new
\InvalidArgumentException('Malformed URL: ' . $url);
}

foreach(
$parts as $name => $value)
{
$parts[$name] = urldecode($value);
}

return
$parts;
}

?>
jerome at chaman dot ca
9年前
值得提醒的是,#fragment的值永远不会发送到服务器。锚点处理完全是客户端的。
nospam at spellingcow dot com
16年前
相对URL查询字符串中的URL将导致问题

失败
/page.php?foo=bar&url=http://www.example.com

解析结果
http://www.foo.com/page.php?foo=bar&url=http://www.example.com
adrian-php at sixfingeredman dot net
17年前
这是一个根据RFC 2396第5.2节实现解析相对URL的函数。毫无疑问,还有更高效的实现,但为了清晰起见,这个函数尽量遵循标准。它依赖于一个名为“unparse_url”的函数来实现第7节,留给读者作为练习(或者您可以替换前面发布的“glue_url”函数)。

<?php
/**
* 解析相对于基路径的URL。这恰好也适用于POSIX
* 文件名。这基于RFC 2396第5.2节。
*/
function resolve_url($base, $url) {
if (!
strlen($base)) return $url;
// 步骤2
if (!strlen($url)) return $base;
// 步骤3
if (preg_match('!^[a-z]+:!i', $url)) return $url;
$base = parse_url($base);
if (
$url{0} == "#") {
// 步骤2(片段)
$base['fragment'] = substr($url, 1);
return
unparse_url($base);
}
unset(
$base['fragment']);
unset(
$base['query']);
if (
substr($url, 0, 2) == "//") {
// 步骤4
return unparse_url(array(
'scheme'=>$base['scheme'],
'path'=>$url,
));
} else if (
$url{0} == "/") {
// 步骤5
$base['path'] = $url;
} else {
// 步骤6
$path = explode('/', $base['path']);
$url_path = explode('/', $url);
// 步骤6a:删除基路径中的文件
array_pop($path);
// 步骤6b、6c、6e:追加url,同时从
// 目录部分删除“.”和“..”
$end = array_pop($url_path);
foreach (
$url_path as $segment) {
if (
$segment == '.') {
// 跳过
} else if ($segment == '..' && $path && $path[sizeof($path)-1] != '..') {
array_pop($path);
} else {
$path[] = $segment;
}
}
// 步骤6d、6f:删除文件部分的“.”和“..”
if ($end == '.') {
$path[] = '';
} else if (
$end == '..' && $path && $path[sizeof($path)-1] != '..') {
$path[sizeof($path)-1] = '';
} else {
$path[] = $end;
}
// 步骤6h
$base['path'] = join('/', $path);

}
// 步骤7
return unparse_url($base);
}
?>
james at roundeights dot com
14年前
我当时正在编写单元测试,需要使此函数抛出错误并返回FALSE以便测试特定的执行路径。如果其他人需要强制失败,则以下输入将有效

<?php
parse_url
("http:///example.com");
parse_url("http://:80");
parse_url("http://user@:80");
?>
to1ne at hotmail dot com
16年前
基于“jbr at ya-right dot com”的想法,我一直在开发一个新的URL解析函数

<?php
function parseUrl($url) {
$r = "^(?:(?P<scheme>\w+)://)?";
$r .= "(?:(?P<login>\w+):(?P<pass>\w+)@)?";
$r .= "(?P<host>(?:(?P<subdomain>[\w\.]+)\.)?" . "(?P<domain>\w+\.(?P<extension>\w+)))";
$r .= "(?::(?P<port>\d+))?";
$r .= "(?P<path>[\w/]*/(?P<file>\w+(?:\.\w+)?)?)?";
$r .= "(?:\?(?P<arg>[\w=&]+))?";
$r .= "(?:#(?P<anchor>\w+))?";
$r = "!$r!"; // 分隔符

preg_match ( $r, $url, $out );

return
$out;
}
print_r ( parseUrl ( 'me:[email protected]:29000/pear/validate.html?happy=me&sad=you#url' ) );
?>

这将返回
数组
(
[0] => me:[email protected]:29000/pear/validate.html?happy=me&sad=you#url
[scheme] =>
[1] =>
[login] => me
[2] => me
[pass] => you
[3] => you
[host] => sub.site.org
[4] => sub.site.org
[subdomain] => sub
[5] => sub
[domain] => site.org
[6] => site.org
[extension] => org
[7] => org
[port] => 29000
[8] => 29000
[path] => /pear/validate.html
[9] => /pear/validate.html
[file] => validate.html
[10] => validate.html
[arg] => happy=me&sad=you
[11] => happy=me&sad=you
[anchor] => url
[12] => url
)

因此,命名和编号数组键都是可能的。

它相当先进,但我认为无论如何它都能工作……如果不行,请告诉我……
pjpawel
2年前
不幸的是,parse_url()无法正确解析没有scheme或“//”的url。例如,“www.xyz.com”被视为路径而不是主机

代码
<?php
var_dump
(parse_url('www.xyz.com'));
?>
输出
array(1) {
["path"]=>
string(10) "www.xyz.com"
}

要获得更好的输出,请将url更改为
'//www.xyz.com' 或 'http://www.xyz.com'
therselman at gmail
12年前
支持UTF-8的parse_url()替代函数。

我意识到,即使URL中不允许使用UTF-8字符,我也必须处理大量此类字符,而parse_url()会出错。

主要基于“mallluhuct at gmail dot com”的工作,我添加了与parse_url()兼容的“命名值”,这使得数组值更容易处理(而不仅仅是数字)。我还实现了端口、用户名/密码的检测和反向引用,以便更好地检测类似这样的URL://en.wikipedia.com
……虽然从技术上讲这是一个无效的URL,但在像维基百科这样的网站的锚标记的href中广泛使用,在浏览器中它是有效的(在抓取页面时必须支持的URL类型之一)。这将被准确地检测为主机名,而不是像其他所有示例一样是“path”。

我将提交我的完整函数(而不仅仅是正则表达式),它几乎是parse_url()的“直接”替代品。它返回一个已清理的数组(或false),其值与parse_url()兼容。我可以告诉preg_match()不要存储未使用的额外值,但这会使正则表达式变得更复杂,更难以阅读、理解和扩展。检测UTF-8字符的关键是在preg_match()中使用“u”参数。

<?php
function parse_utf8_url($url)
{
static
$keys = array('scheme'=>0,'user'=>0,'pass'=>0,'host'=>0,'port'=>0,'path'=>0,'query'=>0,'fragment'=>0);
if (
is_string($url) && preg_match(
'~^((?P<scheme>[^:/?#]+):(//))?((\\3|//)?(?:(?P<user>[^:]+):(?P<pass>[^@]+)@)?(?P<host>[^/?:#]*))(:(?P<port>\\d+))?' .
'(?P<path>[^?#]*)(\\?(?P<query>[^#]*))?(#(?P<fragment>.*))?~u', $url, $matches))
{
foreach (
$matches as $key => $value)
if (!isset(
$keys[$key]) || empty($value))
unset(
$matches[$key]);
return
$matches;
}
return
false;
}
?>

使用此函数提取后,应将UTF-8 URL“规范化”。
theking2(at)king.ma
10个月前
URL 中使用双斜杠('//')将被视为无法解析的字符串,并将返回 NULL。

<?php
$result
= parse_url('http://api.example.com//resource');

// $result = null

?>

在 PHP 8.1.27 上测试
mys5droid at gmail dot com
8年前
我为我的一个项目编写了一个函数,用于将相对 URL 转换为绝对 URL。考虑到我找不到其他地方的类似功能,我想将其发布到这里。

以下函数接受两个参数,第一个参数是要从相对路径转换为绝对路径的 URL,第二个参数是绝对 URL 的示例。

目前它不解析 URL 中的 '../',只是因为我不需要它。大多数 Web 服务器会为你解析它。如果你想让它解析路径中的 '../',只需要进行一些小的修改。

<?php

function relativeToAbsolute($inurl, $absolute) {
// 获取所有部分,避免多次获取 :)
$absolute_parts = parse_url($absolute);
// 测试 URL 是否已经是绝对路径(包含主机,或以 '/' 开头)
if ( (strpos($inurl, $absolute_parts['host']) == false) ) {
// 定义 $tmpurlprefix 以防止以下错误
$tmpurlprefix = "";
// 构建 URL 前缀 (SCHEME)
if (!(empty($absolute_parts['scheme']))) {
// 将 scheme 添加到 tmpurlprefix
$tmpurlprefix .= $absolute_parts['scheme'] . "://";
}
// 构建 URL 前缀 (USER, PASS)
if ((!(empty($absolute_parts['user']))) and (!(empty($absolute_parts['pass'])))) {
// 将 user:port 添加到 tmpurlprefix
$tmpurlprefix .= $absolute_parts['user'] . ":" . $absolute_parts['pass'] . "@";
}
// 构建 URL 前缀 (HOST, PORT)
if (!(empty($absolute_parts['host']))) {
// 将 host 添加到 tmpurlprefix
$tmpurlprefix .= $absolute_parts['host'];
// 检查端口,如果存在则添加
if (!(empty($absolute_parts['port']))) {
// 将 port 添加到 tmpurlprefix
$tmpurlprefix .= ":" . $absolute_parts['port'];
}
}
// 构建 URL 前缀 (PATH),仅当图像路径不包含 './' 时才添加
if ( (!(empty($absolute_parts['path']))) and (substr($inurl, 0, 1) != '/') ) {
// 获取路径部分
$path_parts = pathinfo($absolute_parts['path']);
// 将 path 添加到 tmpurlprefix
$tmpurlprefix .= $path_parts['dirname'];
$tmpurlprefix .= "/";
}
else {
$tmpurlprefix .= "/";
}
// 删除 '/'
if (substr($inurl, 0, 1) == '/') { $inurl = substr($inurl, 1); }
// 删除 './'
if (substr($inurl, 0, 2) == './') { $inurl = substr($inurl, 2); }
return
$tmpurlprefix . $inurl;
}
else {
// 路径已经是绝对路径。返回它 :)
return $inurl;
}
}

// 定义一个示例绝对 URL
$absolute = "http://" . "user:[email protected]:8080/path/to/index.html"; // 只是为了避免 php.net 垃圾邮件过滤器,我不确定 example.com 怎么会是垃圾邮件……

/* 示例 1 */
echo relativeToAbsolute($absolute, $absolute) . "\n";
/* 示例 2 */
echo relativeToAbsolute("img.gif", $absolute) . "\n";
/* 示例 3 */
echo relativeToAbsolute("/img.gif", $absolute) . "\n";
/* 示例 4 */
echo relativeToAbsolute("./img.gif", $absolute) . "\n";
/* 示例 5 */
echo relativeToAbsolute("../img.gif", $absolute) . "\n";
/* 示例 6 */
echo relativeToAbsolute("images/img.gif", $absolute) . "\n";
/* 示例 7 */
echo relativeToAbsolute("/images/img.gif", $absolute) . "\n";
/* 示例 8 */
echo relativeToAbsolute("./images/img.gif", $absolute) . "\n";
/* 示例 9 */
echo relativeToAbsolute("../images/img.gif", $absolute) . "\n";

?>

输出结果
http :// user:[email protected]:8080/path/to/index.html
http :// user:[email protected]:8080/path/to/img.gif
http :// user:[email protected]:8080/img.gif
http :// user:[email protected]:8080/path/to/img.gif
http :// user:[email protected]:8080/path/to/../img.gif
http :// user:[email protected]:8080/path/to/images/img.gif
http :// user:[email protected]:8080/images/img.gif
http :// user:[email protected]:8080/path/to/images/img.gif
http :// user:[email protected]:8080/path/to/../images/img.gif

如果以上代码不符合你的风格,或者你认为它“凌乱”,或者你认为有更好的方法,请见谅。我已经尽可能地减少了空格。

欢迎改进 :)
Michael
5年前
此函数有一个怪癖,如果开头有空格,它会将主机名作为“路径”返回。

<?php

$url
= ' https://foobar.com:80/mypath/myfile.php';

print_r(parse_url($url));
/*
数组
(
[path] => https://foobar.com:80/mypath/myfile.php
)
*/

print_r(trim(parse_url($url)));
/*
数组
(
[scheme] => https
[host] => foobar.com
[port] => 80
[path] => /mypath/myfile.php
)
*/

?>
utilmind
11年前
如果未指定协议,parse_url 无法工作。这似乎是标准行为,即使 YouTube 在生成嵌入代码时(例如 "//youtube.com/etc")也不会提供协议名称。

因此,为了避免错误,您必须始终检查提供的 URL 是否包含协议,如果没有(以两个斜杠开头),则添加 "http:" 前缀。
demerit
7年前
PHP 7(我在从 5.3 升级到 7.1 时注意到)中出现了一个变化,如果密码部分包含井号 (#),则在 7.1 中解析会失败,而在 5.3 中则会成功。
boctulus @ gmail co!m
5年前
parse_url() 无法解析一些明显的错误,所以我创建了一个补充函数

function url_check(string $url){
$sym = null;

$len = strlen($url);
for ($i=0; $i<$len; $i++){
if ($url[$i] == '?'){
if ($sym == '?' || $sym == '&')
return false;

$sym = '?';
}elseif ($url[$i] == '&'){
if ($sym === null)
return false;

$sym = '&';
}
}
return true;
}
}
ivijan dot stefan at gmail dot com
10年前
这是一个使用 parse_url() 获取 YouTube 链接的好方法。
我在许多作品中都使用了这个函数

<?php
function youtube($url, $width=560, $height=315, $fullscreen=true)
{
parse_str( parse_url( $url, PHP_URL_QUERY ), $my_array_of_vars );
$youtube= '<iframe allowtransparency="true" scrolling="no" width="'.$width.'" height="'.$height.'" src="//www.youtube.com/embed/'.$my_array_of_vars['v'].'" frameborder="0"'.($fullscreen?' allowfullscreen':NULL).'></iframe>';
return
$youtube;
}

// 在我的页面上显示 YouTube 视频
$url='http://www.youtube.com/watch?v=yvTd6XxgCBE';
youtube($url, 560, 315, true);
?>

parse_url() 分配一个唯一的 YouTube 代码并将其放入 iframe 链接,然后显示在您的页面上。您可以自己选择视频的大小。

享受。
ap dot public1 at gmail dot com
15年前
一个简单的静态库,允许轻松操作 URL 参数

<?php
/**
* 提供一种简单的方法来操作 URL 参数
* @author Alexander Podgorny
*/

class Url {
/**
* 将 URL 分解成如下所示的各个部分组成的数组:
* [scheme]://[user]:[pass]@[host]/[path]?[query]#[fragment]
* 此外,它还添加了 'query_params' 键,其中包含 URL 解码后的键值对数组
*
* @param String $sUrl URL
* @return Array 解析后的 URL 各部分
*/
public static function explode($sUrl) {
$aUrl = parse_url($sUrl);
$aUrl['query_params'] = array();
$aPairs = explode('&', $aUrl['query']);
DU::show($aPairs);
foreach(
$aPairs as $sPair) {
if (
trim($sPair) == '') { continue; }
list(
$sKey, $sValue) = explode('=', $sPair);
$aUrl['query_params'][$sKey] = urldecode($sValue);
}
return
$aUrl;
}
/**
* 根据 URL 各部分的数组(由 explode 函数返回)编译 URL
* 如果存在 'query_params',则忽略 'query'
*
* @param Array $aUrl URL 各部分的数组
*/
public static function implode($aUrl) {
//[scheme]://[user]:[pass]@[host]/[path]?[query]#[fragment]

$sQuery = '';

// 编译查询字符串
if (isset($aUrl['query_params']) && is_array($aUrl['query_params'])) {
$aPairs = array();
foreach (
$aUrl['query_params'] as $sKey=>$sValue) {
$aPairs[] = $sKey.'='.urlencode($sValue);
}
$sQuery = implode('&', $aPairs);
} else {
$sQuery = $aUrl['query'];
}

// 编译 URL
$sUrl =
$aUrl['scheme'] . '://' . (
isset(
$aUrl['user']) && $aUrl['user'] != '' && isset($aUrl['pass'])
?
$aUrl['user'] . ':' . $aUrl['pass'] . '@'
: ''
) .
$aUrl['host'] . (
isset(
$aUrl['path']) && $aUrl['path'] != ''
? $aUrl['path']
:
''
) . (
$sQuery != ''
? '?' . $sQuery
: ''
) . (
isset(
$aUrl['fragment']) && $aUrl['fragment'] != ''
? '#' . $aUrl['fragment']
:
''
);
return
$sUrl;
}
/**
* 解析 URL 并返回 URL 参数的键值对数组
*
* @param String $sUrl
* @return Array
*/
public static function getParams($sUrl) {
$aUrl = self::explode($sUrl);
return
$aUrl['query_params'];
}
/**
* 删除现有的 URL 参数,并将其设置为 $aParams 中指定的参数
*
* @param String $sUrl URL
* @param Array $aParams 要设置的 URL 参数的键值对数组
* @return String 新编译的 URL
*/
public static function setParams($sUrl, $aParams) {
$aUrl = self::explode($sUrl);
$aUrl['query'] = '';
$aUrl['query_params'] = $aParams;
return
self::implode($aUrl);
}
/**
* 更新现有 URL 参数的值,并添加(如果未设置)$aParams 中指定的参数
*
* @param String $sUrl URL
* @param Array $aParams 要设置的 URL 参数的键值对数组
* @return String 新编译的 URL
*/
public static function updateParams($sUrl, $aParams) {
$aUrl = self::explode($sUrl);
$aUrl['query'] = '';
$aUrl['query_params'] = array_merge($aUrl['query_params'], $aParams);
return
self::implode($aUrl);
}
}

?>
[email protected]
12年前
创建了另一个与 UTF-8 兼容的 parse_url 函数。
<?php
function mb_parse_url($url) {
$encodedUrl = preg_replace('%[^:/?#&=\.]+%usDe', 'urlencode(\'$0\')', $url);
$components = parse_url($encodedUrl);
foreach (
$components as &$component)
$component = urldecode($component);
return
$components;
}
?>
[email protected]
15年前
您好,由于某种奇怪的原因,当输入 URL 中没有提供 scheme 时,parse_url 会将主机 (例如 example.com) 返回为路径。因此,我编写了一个快速函数来获取真实的主机

<?php
function getHost($Address) {
$parseUrl = parse_url(trim($Address));
return
trim($parseUrl[host] ? $parseUrl[host] : array_shift(explode('/', $parseUrl[path], 2)));
}

getHost("example.com"); // 返回 example.com
getHost("http://example.com"); // 返回 example.com
getHost("www.example.com"); // 返回 www.example.com
getHost("http://example.com/xyz"); // 返回 example.com
?>

您可以尝试任何方法!它会返回主机(包括子域名,如果存在)。

希望对您有所帮助。
[email protected]
17年前
回复 adrian:

非常感谢您的函数。您的相对协议函数存在一个小问题。在将 URL 设置为路径时,您需要删除 //。以下是新的函数。

function resolve_url($base, $url) {
如果 (!strlen($base)) 返回 $url;
// 步骤 2
如果 (!strlen($url)) 返回 $base;
// 步骤 3
如果 (preg_match('!^[a-z]+:!i', $url)) 返回 $url;
$base = parse_url($base);
如果 ($url{0} == "#") {
// 步骤 2(片段)
$base['fragment'] = substr($url, 1);
返回 unparse_url($base);
}
unset($base['fragment']);
unset($base['query']);
如果 (substr($url, 0, 2) == "//") {
// 步骤 4
返回 unparse_url(array(
'scheme'=>$base['scheme'],
'path'=>substr($url,2),
));
} 否则如果 ($url{0} == "/") {
// 步骤 5
$base['path'] = $url;
} 否则 {
// 步骤 6
$path = explode('/', $base['path']);
$url_path = explode('/', $url);
// 步骤 6a:删除 base 中的文件名
array_pop($path);
// 步骤 6b、6c、6e:追加 url,同时从目录部分删除 "." 和 ".."
// 部分
$end = array_pop($url_path);
foreach ($url_path as $segment) {
如果 ($segment == '.') {
// 跳过
} 否则如果 ($segment == '..' && $path && $path[sizeof($path)-1] != '..') {
array_pop($path);
} 否则 {
$path[] = $segment;
}
}
// 步骤 6d、6f:删除文件部分的 "." 和 ".."
如果 ($end == '.') {
$path[] = '';
} 否则如果 ($end == '..' && $path && $path[sizeof($path)-1] != '..') {
$path[sizeof($path)-1] = '';
} 否则 {
$path[] = $end;
}
// 步骤 6h
$base['path'] = join('/', $path);

}
// 步骤 7
返回 unparse_url($base);
}
vbrazas15 at gmail dot com
3 年前
我对 “thomas at gielfeldt dot com” 著名的 `unparse_url` 函数做了一些改进

```php
/**
* @param array $parsedUrl -- 库函数 `parse_url()` 的结果
*
* @return string
*/
function unparseUrl(array $parsedUrl): string
{
// PHP_URL_SCHEME
$scheme = empty($parsedUrl[PHP_URL_SCHEME]) ? '' : (rtrim($parsedUrl['scheme'], ':/') . '://');

$user = empty($parsedUrl[PHP_URL_USER]) ? '' : rtrim($parsedUrl['user'], '@:');
$pass = empty($parsedUrl[PHP_URL_PASS]) ? '' : (':' . trim($parsedUrl['pass'], '@:'));

$pass = !$user ? '' : ($pass . '@');

$host = empty($parsedUrl[PHP_URL_HOST]) ? '' : rtrim($parsedUrl['host'], '/');
$port = empty($parsedUrl[PHP_URL_PORT]) ? '' : (':' . (int)ltrim($parsedUrl['port'], ':'));
$path = empty($parsedUrl[PHP_URL_PATH]) ? '' : ('/' . ltrim($parsedUrl['path'], '/'));

$host = ($host && !$port && !$path) ? $parsedUrl['host'] : $host;
$path = ($path && !$host && !$port) ? $parsedUrl['path'] : $path;

$query = empty($parsedUrl[PHP_URL_QUERY]) ? '' : ('?' . ltrim($parsedUrl['query'], '?'));
$fragment = empty($parsedUrl[PHP_URL_FRAGMENT]) ? '' : ('#' . ltrim($parsedUrl['fragment'], '#'));

返回 "$scheme$user$pass$host$port$path$query$fragment";
}
```
匿名用户
6 年前
此函数将尝试解析相对 URL,但依赖它可能会产生意外行为,从而导致一些难以追踪的错误。(以下结果来自 PHP 5.5.19)

尝试解析如下 URL
http://example.com/entities/GOA:98/?search=8989157d1f22
正确地产生了
<?php
array (
'scheme' => 'http',
'host' => 'example.com',
'path' => '/entities/GOA:98/',
'query' => 'search=8989157d1f22',
);
?>

然而,尝试解析相对 URL
entities/GOA:98/?search=8989157d1f22
<?php
array (
'host' => 'entities',
'port' => 98,
'path' => '/GOA:98/',
'query' => 'search=8989157d1f22',
)
?>
如果我把 :98 改成 :A98,parse_url 会正确地解析 URL 为
<?php
array (
'path' => 'entities/GOA:A98/',
'query' => 'search=8989157d1f22',
)
?>
底线是,除非你测试了预期的输入并且知道 parse_url 会很好地处理它们,否则避免对相对 url 使用 parse_url。

https://forums.hawacastle.com/
Rob
9年前
我一直在开发一个通用的类,它可以使 URI 解析/构建更容易一些。

Composer 包在这里:https://packagist.org.cn/packages/enrise/urihelper

代码库在这里:https://github.com/Enrise/UriHelper

用法示例

<?php
$uri
= new \Enrise\Uri('http://usr:[email protected]:81/mypath/myfile.html?a=b&b[]=2&b[]=3#myfragment');
echo
$uri->getScheme(); // http
echo $uri->getUser(); // usr
echo $uri->getPass(); // pss
echo $uri->getHost(); // example.com
echo $uri->getPort(); // 81
echo $uri->getPath(); // /mypath/myfile.html
echo $uri->getQuery(); // a=b&b[]=2&b[]=3
echo $uri->getFragment(); // myfragment
echo $uri->isSchemeless(); // false
echo $uri->isRelative(); // false

$uri->setScheme('scheme:child:scheme.VALIDscheme123:');
$uri->setPort(null);

echo
$uri->getUri(); //scheme:child:scheme.VALIDscheme123:usr:[email protected]/mypath/myfile.html?a=b&b[]=2&b[]=3#myfragment
?>
zappascripts at gmail com
7年前
这是一个我编写的简单类,它使用了 parse_url。
我需要一种方法让页面保留 get 参数,同时也能编辑或添加它们。
我还有一些页面需要相同的 GET 参数,所以我添加了一种更改路径的方法。

<?php
class Paths{

private
$url;
public function
__construct($url){
$this->url = parse_url($url);
}

public function
returnUrl(){
$return = $this->url['path'].'?'.$this->url['query'];
$return = (substr($return,-1) == "&")? substr($return,0,-1) : $return;
$this->resetQuery();
return
$return;
}

public function
changePath($path){
$this->url['path'] = $path;
}

public function
editQuery($get,$value){
$parts = explode("&",$this->url['query']);
$return = "";
foreach(
$parts as $p){
$paramData = explode("=",$p);
if(
$paramData[0] == $get){
$paramData[1] = $value;
}
$return .= implode("=",$paramData).'&';

}

$this->url['query'] = $return;
}

public function
addQuery($get,$value){
$part = $get."=".$value;
$and = ($this->url['query'] == "?") ? "" : "&";
$this->url['query'] .= $and.$part;
}

public function
checkQuery($get){
$parts = explode("&",$this->url['query']);

foreach(
$parts as $p){
$paramData = explode("=",$p);
if(
$paramData[0] == $get)
return
true;
}
return
false;

}

public function
buildQuery($get,$value){
if(
$this->checkQuery($get))
$this->editQuery($get,$value);
else
$this->addQuery($get,$value);

}

public function
resetQuery(){
$this->url = parse_url($_SERVER['REQUEST_URI']);
}




}
?>

用法

Test.php?foo=1

<?php
$path
= new Paths($_SERVER['REQUEST_URI']);
$path->changePath("/baz.php");
$path->buildQuery("foo",2);
$path->buildQuery("bar",3);
echo
$path->returnUrl();
?>

返回:/baz.php?foo=2&bar=3

希望这对某些人有用!
need_sunny at yahoo dot com
14年前
感谢 xellisx 提供他的 parse_query 函数。我在一个项目中使用了它,效果很好。但是它有一个错误。我修复了这个错误并对其进行了一些改进。这是我的版本:

<?php
// 原作者:xellisx
function parse_query($var)
{
/**
* 使用此函数从 parse_url() 的输出中解析查询数组元素。
*/
$var = parse_url($var, PHP_URL_QUERY);
$var = html_entity_decode($var);
$var = explode('&', $var);
$arr = array();

foreach(
$var as $val)
{
$x = explode('=', $val);
$arr[$x[0]] = $x[1];
}
unset(
$val, $x, $var);
return
$arr;
}
?>

第一行原本是 parse_query($val),我改成了 $var。在修复之前,它返回的是一个空数组。

我添加了 parse_url 行。因此,现在该函数只关注查询部分,而不是整个 URL。如果执行如下操作,这将非常有用:
<?php
$my_GET
= parse_query($_SERVER['REQUEST_URI']);
?>
vdklah at hotmail dot com
16年前
一些确定 URL 端口的示例。
当未指定端口时,它从方案中推导出端口。

<?php
function getUrlPort( $urlInfo )
{
if( isset(
$urlInfo['port']) ) {
$port = $urlInfo['port'];
} else {
// 未指定端口;获取默认端口
if (isset($urlInfo['scheme']) ) {
switch(
$urlInfo['scheme'] ) {
case
'http':
$port = 80; // http 默认端口
break;
case
'https':
$port = 443; // https 默认端口
break;
case
'ftp':
$port = 21; // ftp 默认端口
break;
case
'ftps':
$port = 990; // ftps 默认端口
break;
default:
$port = 0; // 错误;不支持的协议
break;
}
} else {
$port = 0; // 错误;未知协议
}
}
return
$port;
}

$url = "http://nl3.php.net/manual/en/function.parse-url.php";
$urlInfo = parse_url( $url );
$urlPort = getUrlPort( $urlInfo );
if(
$urlPort !== 0 ) {
print
'找到URL端口:'.$urlPort;
} else {
print
'错误:无法在URL中找到端口:'.$url;
}
?>
simbappo
4年前
从传递的或当前URL中取消设置查询变量

function unsetqueryvar($var, $url=null) {
if (null == $url) $url = $_SERVER['REQUEST_URI'];
//转换为列表
$url = parse_url($url);
$rq = [];
parse_str($url['query'], $rq);
unset($rq[$var]);
return $url['scheme'].$url['host'].$url['path'].'?'.http_build_query($rq).$url['fragment'];
}
jesse at example dot com
14年前
@ solenoid:你的代码非常有帮助,但在当前URL没有查询字符串时会失败(它会在查询前附加“&”而不是“?”)。下面是一个修正版本,它捕获了这个边缘情况并进行了修正。

<?php
function modify_url($mod)
{
$url = "http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$query = explode("&", $_SERVER['QUERY_STRING']);
if (!
$_SERVER['QUERY_STRING']) {$queryStart = "?";} else {$queryStart = "&";}
// 修改/删除数据
foreach($query as $q)
{
list(
$key, $value) = explode("=", $q);
if(
array_key_exists($key, $mod))
{
if(
$mod[$key])
{
$url = preg_replace('/'.$key.'='.$value.'/', $key.'='.$mod[$key], $url);
}
else
{
$url = preg_replace('/&?'.$key.'='.$value.'/', '', $url);
}
}
}
// 添加新数据
foreach($mod as $key => $value)
{
if(
$value && !preg_match('/'.$key.'=/', $url))
{
$url .= $queryStart.$key.'='.$value;
}
}
return
$url;
}
?>
solenoid at example dot com
14年前
这是一段修改、替换或删除URL查询的代码。这通常用于分页情况下,页面参数多于页面本身的情况。

<?php
function modify_url($mod)
{
$url = "http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$query = explode("&", $_SERVER['QUERY_STRING']);
// 修改/删除数据
foreach($query as $q)
{
list(
$key, $value) = explode("=", $q);
if(
array_key_exists($key, $mod))
{
if(
$mod[$key])
{
$url = preg_replace('/'.$key.'='.$value.'/', $key.'='.$mod[$key], $url);
}
else
{
$url = preg_replace('/&?'.$key.'='.$value.'/', '', $url);
}
}
}
// 添加新数据
foreach($mod as $key => $value)
{
if(
$value && !preg_match('/'.$key.'=/', $url))
{
$url .= '&'.$key.'='.$value;
}
}
return
$url;
}

// 页面URL:"http://www.example.com/page.php?p=5&show=list&style=23"

$url = modify_url(array('p' => 4, 'show' => 'column'));

// $url = "http://www.example.com/page.php?p=4&show=column&style=23"
?>
admin at griefer1999 dot uhostfull dot com
9年前
<?php
function url_parse($url){
$sflfdfldf=$url;
if(
strpos($url,"?")>-1){
$a=explode("?",$url,2);
$url=$a[0];
$query=$a[1];
}
if(
strpos($url,"://")>-1){
$scheme=substr($url,0,strpos($url,"//")-1);
$url=substr($url,strpos($url,"//")+2,strlen($url));
}
if(
strpos($url,"/")>-1){
$a=explode("/",$url,2);
$url=$a[0];
$path="/".$a[1];
}
if(
strpos($url,":")>-1){
$a=explode(":",$url,2);
$url=$a[0];
$port=$a[1];
}
$host=$url;
$url=null;
foreach(array(
"url","scheme","host","port","path","query") as $var){
if(!empty($
$var)){
$return[$var]=$$var;
}
}
//return array("url"=>$sflfdfldf,"scheme"=>$scheme,"host"=>$host,"port"=>$port,"path"=>$path,"query"=>$query,"a"=>$url);
return $return;
}
?>

<?php
/* 比较两个输出 */
//我的
print_r(url_parse("http://login.yahoo.com?.src=ym&.intl=gb&.lang=zh-Hans-HK&.done=https://mail.yahoo.com"));
//内部的
print_r(parse_url("http://login.yahoo.com?.src=ym&.intl=gb&.lang=zh-Hans-HK&.done=https://mail.yahoo.com"));
?>
[email protected]
3个月前
请考虑以下提示和案例

1. 处理片段标识符

parse_url() 处理片段标识符 (#section),但片段不会发送到服务器,仅在客户端使用。依赖片段数据时要谨慎,因为它可能在服务器端处理中不可用。

2. URL 编码和解码问题

parse_url() 不会解码路径中 URL 编码的字符。如果涉及特殊字符,请确保正确处理编码和解码。

例如
$url = 'https://www.primeogroup.com/es/servicios-de-configuraci%C3%B3n-instalaci%C3%B3n-y-an%C3%A1lisis-de-google-analytics/';
// /es/servicios-de-configuraci%C3%B3n-instalaci%C3%B3n-y-an%C3%A1lisis-de-google-analytics/
$path = parse_url($url, PHP_URL_PATH);
// /es/servicios-de-configuración-instalación-y-análisis-de-google-analytics/
$decoded_path = urldecode($path);

3. 不寻常的端口号

parse_url() 无法正确处理超出有效范围 (1-65535) 的端口。

parse_url 将返回:bool(false)
[email protected]
9个月前
虽然与以上内容没有直接关系,但我发现此页面正在寻找如何访问 REST 风格的 domain.com?key1=value1&key2=value2 类型参数。阅读页面和评论后,想添加此内容来帮助其他可能在这里寻找相同解决方案的人。

已知:domain.com?key1=value1&key2=value2

echo $_GET['key2']; // 输出:'value2'

在我看来,PHP 比几乎任何其他语言都更容易做到这一点。
xellisx
16年前
我需要从推荐者中解析出查询字符串,所以我创建了这个函数。

<?php
function parse_query($val)
{
/**
* 使用此函数从 parse_url() 的输出中解析出查询数组元素。
*/
$var = html_entity_decode($var);
$var = explode('&', $var);
$arr = array();

foreach(
$var as $val)
{
$x = explode('=', $val);
$arr[$x[0]] = $x[1];
}
unset(
$val, $x, $var);
return
$arr;
}
?>
[email protected]
3 年前
此函数“parse_rebuild_url”将使用“overwrite_parsed_url_array”提供的新的值来解析和重新组装您的 URL。
也可以通过键名覆盖 URL 组件,并合并或覆盖查询参数。
<?php

$test_url
= 'http://usr:[email protected]:81/mypath/myfile.html?a=b&b[]=2&b[]=3&z=9#myfragment';

$new_url_01_overwrite_query_params = parse_rebuild_url( $test_url, array(
'host' => 'new-hostname.tld',
'query' => array(
'test' => 'Hello World',
'a' => array( 'c', 'd' ),
'z' => 8
),
'fragment' => 'new-fragment-value'
), false );

$new_url_02_mergewith_query_params = parse_rebuild_url( $test_url, array(
'query' => array(
'test' => 'Hello World',
'a' => array( 'c', 'd' ),
'z' => 8
),
'fragment' => 'new-fragment-value'
), true );

function
parse_rebuild_url( $url, $overwrite_parsed_url_array, $merge_query_parameters = true ) {

$parsed_url_array = parse_url( $url );
$parsed_url_keys_array = array(
'scheme' => null,
'abempty' => isset( $parsed_url_array['scheme'] ) ? '://' : null,
'user' => null,
'authcolon' => isset( $parsed_url_array['pass'] ) ? ':' : null,
'pass' => null,
'authat' => isset( $parsed_url_array['user'] ) ? '@' : null,
'host' => null,
'portcolon' => isset( $parsed_url_array['port'] ) ? ':' : null,
'port' => null,
'path' => null,
'param' => isset( $parsed_url_array['query'] ) ? '?' : null,
'query' => null,
'hash' => isset( $parsed_url_array['fragment'] ) ? '#' : null,
'fragment' => null
);

if ( isset(
$parsed_url_array['query'] ) && $merge_query_parameters === true ) {
parse_str( $parsed_url_array['query'], $query_array );
$overwrite_parsed_url_array['query'] = array_merge_recursive( $query_array, $overwrite_parsed_url_array['query'] );
}

$query_parameters = http_build_query( $overwrite_parsed_url_array['query'], null, '&', PHP_QUERY_RFC1738 );
$overwrite_parsed_url_array['query'] = urldecode( preg_replace( '/%5B[0-9]+%5D/simU', '%5B%5D', $query_parameters ) );

$fully_parsed_url_array = array_filter( array_merge( $parsed_url_keys_array, $parsed_url_array, $overwrite_parsed_url_array ) );
return
implode( null, $fully_parsed_url_array );

}
JosephDor
1年前
你好!<a href=https://stromectolxf.online/>伊维菌素 24毫克</a> 优秀的网站 https://stromectolrf.top
bramg dot net1 at gmail dot com
6 年前
这是我的404错误页面,这样可以吗?还是需要改进?

<?php
/**
* 404.php
*
* 显示404页面(未找到)的模板
*
* @author BetterStudio
* @package Publisher
* @version 2.0.2
*/

get_header();

// 显示面包屑导航
if ( publisher_show_breadcrumb() ) {
Better_Framework()->breadcrumb()->generate( array(
'before' => '<div class="container bf-breadcrumb-container">',
'after' => '</div>',
'custom_class' => 'bc-top-style'
) );
}

?>
<div class="content-wrap">
<main <?php publisher_attr( 'content', '' ); ?>>

<div class="container layout-1-col layout-no-sidebar">
<div class="row main-section">

<div class="content-column content-404">

<div class="row first-row">

<div class="col-lg-12 text-404-section">
<p class="text-404 heading-typo">404</p>
</div>

<div class="col-lg-12 desc-section">
<h1 class="title-404"><?php publisher_translation_echo( '404_not_found' ); ?></h1>
<p><?php publisher_translation_echo( '404_not_found_message' ); ?></p>
<div class="action-links clearfix">

<script type="text/javascript">
if (document.referrer) {
document.write('<div class="search-action-container"><a href="' + document.referrer + '"><i class="fa fa-angle-double-right"></i> <?php publisher_translation_echo( '404_go_previous_page' ); ?></a></div>');
}
</script>

<div class="search-action-container">
<a href="<?php echo esc_url( home_url( '/' ) ); ?>"><i
class="fa fa-angle-double-right"></i> <?php publisher_translation_echo( '404_go_homepage' ); ?>
</a>
</div>
</div>
</div>

</div><!-- .first-row -->

<div class="row second-row">
<div class="col-lg-12">
<div class="top-line">
<?php get_search_form(); ?>
</div>
</div>
</div><!-- .second-row -->

</div><!-- .content-column -->

</div><!-- .main-section -->
</div> <!-- .layout-1-col -->

</main><!-- main -->
</div><!-- .content-wrap -->

<?php get_footer(); ?>

https://bramg.net
匿名用户
6 年前
您好,由于某种奇怪的原因,当输入 URL 中没有提供 scheme 时,parse_url 会将主机 (例如 example.com) 返回为路径。因此,我编写了一个快速函数来获取真实的主机

<?php
function getHost($Address) {
$parseUrl = parse_url(trim($Address));
return
trim($parseUrl[host] ? $parseUrl[host] : array_shift(explode('/', $parseUrl[path], 2)));
}

getHost("example.com"); // 返回 example.com
getHost("http://example.com"); // 返回 example.com
getHost("www.example.com"); // 返回 www.example.com
getHost("http://example.com/xyz"); // 返回 example.com
?>

您可以尝试任何方法!它会返回主机(包括子域名,如果存在)。

希望对您有所帮助。
https://vb.3dlat.com/
匿名用户
7年前
获取参数(URL 查询字符串)作为关联数组,使用此函数

<?php
/**
* 将URL查询字符串转换为关联数组
*
* @param string query
* @return array params
*/
function convertUrlQuery($query) {
$queryParts = explode('&', $query);

$params = array();
foreach (
$queryParts as $param) {
$item = explode('=', $param);
$params[$item[0]] = $item[1];
}

return
$params;
}
?>
To Top