为什么在使用http_build_query
函数构建查询字符串时,它会在值外部对方括号[]
进行urlencodes以及如何摆脱它?
$query = array("var" => array("foo" => "value", "bar" => "encodedBracket["));
$queryString = http_build_query($query, "", "&");
var_dump($queryString);
var_dump("urldecoded: " . urldecode($queryString));
输出:
var%5Bfoo%5D=value&var%5Bbar%5D=encodedBracket%5B
urldecoded: var[foo]=value&var[bar]=encodedBracket[
该函数在输出的第一行中正确地对[
中的encodedBracket[
进行了编码,但在var[foo]=
和var[bar]=
中对方括号进行编码的原因是什么?正如您所看到的,urldecoding字符串也解码了值中的保留字符,encodedBracket%5B
应该保持原样,查询字符串是正确的,而不是encodedBracket[
。
根据section 2.2 Reserved Characters of Uniform Resource Identifier (URI): Generic Syntax
URI包括由分隔的组件和子组件 “保留”集中的字符。这些字符被调用 “保留”因为它们可能(或可能不)被定义为分隔符 通用语法,每种特定于方案的语法,或者由 URI的解除引用算法的特定于实现的语法。如果 URI组件的数据将与保留字符冲突 作为分隔符的目的,那么冲突的数据必须是 在URI形成之前进行百分比编码。
reserved = gen-delims / sub-delims
gen-delims =“:”/“/”/“?” /“#”/“[”/“]”/“@”
sub-delims =“!” /“$”/“&” /“'”/“(”/“)”/“*”/“+”/“,”/ / “;” /“=”
所以不应该http_build_query
真正产生更可读的输出,只有[]
字符的urlencoded只在需要的地方?如何让它产生这样的输出?
答案 0 :(得分:0)
我发现以下内容"修复" here:
[...]可行的修复'我一直在使用is来后处理http_build_query()输出 以下 - 解决方案'这让我的皮肤爬了一点:
function http_build_query_unborker($s) { return preg_replace_callback('#%5[bd](?=[^&]*=)#i', function($match) { return urldecode($match[0]); }, $s); }
所以现在它会变成:
$query = array("var" => array("foo" => "value", "bar" => "encodedBracket["));
$queryString = http_build_query_unborker(http_build_query($query, "", "&"));
var_dump($queryString);
var_dump("urldecoded: " . urldecode($queryString)); // var[foo]=value&var[bar]=encodedBracket%5B
答案 1 :(得分:0)
你这里有很多问题。用RFC的术语表达,并用这些相同的术语阅读你自己的问题。我从下到上提出你的问题:
如何让它产生这样的输出?
使用其他编码器 Net_URL2 (pear / packagist)例如:
$vars = array("var" => array("foo" => "value", "bar" => "encodedBracket["));
$url = new Net_URL2('');
$url->setQueryVariables($vars);
$query = $url->getQuery();
var_dump($query); // string(41) "var[foo]=value&var[bar]=encodedBracket%5B"
所以不应该
http_build_query really
使用[] urlencoded这样的字符产生更可读的输出吗?
不,不应该。即使不需要对查询部分内的方括号进行编码,也建议使用。应该做的就是建议。
除此之外,http_build_query()
功能与创建"更易读的输出" 无关。它只是关于创建HTTP URI的查询。对于这样的查询部分,方括号应该是百分比编码的。这些是未特别允许查询的保留字符。
在
var[foo]=
和var[bar]=
编码方括号的原因是什么?
编码方括号的原因与编码encodedBracket[
中的方括号的原因相同。在你的问题中你在这些部分之间做的区别纯粹是单独的语法,在URI中这些部分被视为相等。 URI中没有查询部分的子部分。因此,对括号var[
或括号encodedBracket[
进行区分与查询部分的URI编码完全无关。
正如你所说encodedBracket[
到encodedBracket%5B
的百分比编码是正确的,因为它属于URI的同一部分(查询部分),逻辑规定你必须接受该编码在var[
到var%5B
中的括号在URI编码方面同样正确。相同的URI部分,相同的编码。查询部分唯一的结尾分隔符是" #
"。
另外,你的推理表明这部分存在误解:
正如您所看到的,urldecoding字符串也解码了值中的保留字符,
encodedBracket%5B
应该保持原样,查询字符串是正确的,而不是encodedBracket[
。
如果你urldecode,所有百分比编码序列都将被解码 - 无论百分比编码是否代表保留字符。就正确而言,它与您所说的相反:%5B
必须被解码为[
,无论它是在字符串的开头,中间还是末尾
为什么在使用
http_build_query
函数构建查询字符串时,urlencodes方括号[]外部值以及如何摆脱它?
回答第二部分更容易,在答案开头看,已经回答了。
关于为什么这可能不会立即可见,特别是因为您可能已经发现PHP本身在查询中接受百分比编码和逐字方括号(甚至混合)而没有任何问题。
为什么会出现这种差异?它是否真的像你在问题中描述的那样简单?这只是一种美容差异吗?
首先,在查询部分不应包含来自gen-delims字符未加密码的括号中,不在URI的查询查询部分中编码方括号会违反RFC3986 。根据ABNF,非百分比编码的方括号不能成为查询的一部分:
query = *( pchar / "/" / "?" ) pchar = unreserved / pct-encoded / sub-delims / ":" / "@" unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" pct-encoded = "%" HEXDIG HEXDI sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
因此不建议删除这些(至少为了符合标准的编码目的),因为它会改变URI:
用保留字符替换保留字符的URI 相应的百分比编码的八位字节不相等。
这已经是一个很好的暗示,对于你要求的URI,它与PHP通过内置函数创建的URI具有不同的含义。
进一步说:
生成URI的应用程序应对数据八位字节进行百分比编码 除非这些字符对应于保留集中的字符 URI方案特别允许表示其中的数据 零件。
对于gen-delims 中的所有字符而言,情况并非如此,但是对于ABNF而言:
"/" / "?" / ":" / "@"
因此看起来http_build_query()
路由到百分比编码方括号,因为那些是保留字符,不特定于该部分的URI方案(查询) 。基本上没有任何问题,它遵循RFC3986的建议。并且它不会为查询的那些部分建议不同的含义。
但是你清楚地说,从技术上讲,这些括号在查询中不是分隔符。是的,这是真的:
查询组件由第一个问题指示 标记("?")字符并以数字符号("#")字符结束 或者在URI的末尾。
因此,与之前确定的保留字符而不是特别允许的情况相比:
"#" / "[" / "]"
(已经是一个非常小的清单)应该清楚" #
"必须保持保留,否则URI会被破坏(在查询结束时是一个真正的分隔分隔符),但是在表示不相等的URI而不保留数据并且保留所有URI分隔符时,不能特别允许使用方括号:
如果在URI组件中找到保留字符,则 对于该角色没有分隔角色,那么它必须是 解释为表示与其对应的数据八位字节 字符在US-ASCII中的编码。
因此,如果您仍然可以关注我,可能需要实际执行您要求的内容:创建一个URI,其中方括号表示分隔符(例如,表示数组定义的一小部分)但没有这是数据。尽管根据RFC 3986保留了该字符的数据。
因此,在技术上可以创建一个带有方括号的URI,而不是在查询中编码的百分比。从技术上讲,即使是内部值,就像它在值之外的语法差异一样,这只是值内部的另一个语法差异。
当您在浏览器中输入这些内容时,这也是浏览器保留方括号状态的原因。是否编码百分比 - 浏览器按原样将URI的那部分传递给服务器,以便服务器上的基础进程可以从可能由此表达的语法差异中受益。
因此,请为底层平台正确选择URL编码。只是因为它可能,它并不意味着它以稳定的方式运作。 http_build_query()
的方式是遵循RFC 3986的最稳定(安全)方式。然而,它应该在RFC中,所以如果您理解这一点,那么您可以有正当理由不进行百分比编码方括号。
您在问题中提到的一个原因是可读性。当您在一张纸上写下URL时,这一点尤为重要。我不太确定方括号是否是一个如此好的可区分字符,如果不是百分比编码甚至有助于可读性。但我没试过。 PHP会接受这两种方式。但是你不需要以编程方式做到这一点。因此,在您的方案中可能无法实现可读性。
答案 2 :(得分:0)
这是我编写的快速函数,用于生成更好的查询字符串。它不仅不编码方括号,而且如果它匹配索引,也将省略数组键。
请注意,它不支持对象或http_build_query
的附加选项。 $prefix
参数用于递归,在初始调用时应省略。
function http_clean_query(array $query_data, string $prefix=null): string {
$parts = [];
$i = 0;
foreach ($query_data as $key=>$value) {
if ($prefix === null) {
$key = rawurlencode($key);
} else if ($key === $i) {
$key = $prefix.'[]';
$i++;
} else {
$key = $prefix.'['.rawurlencode($key).']';
}
if (is_array($value)) {
if (!empty($value)) $parts[] = http_clean_query($value, $key);
} else {
$parts[] = $key.'='.rawurlencode($value);
}
}
return implode('&', $parts);
}
答案 3 :(得分:0)
我知道这有点过时了,但我认为它今天仍然适用。
TL;DR:http_build_query() 工作正常
更长的解释:是的,> #from my layout.html
>
> <!doctype html> <html lang="en">
> <head>
> <meta charset="utf-8">
> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
> <link href="/static/favicon.ico" rel="icon" type="image/x-icon"/>
> <title>{% block title %}Title{% endblock %}</title>
> {% block firebase1 %}{% endblock %}
> {% block firebase2 %}{% endblock %}
> </head>
对 http_build_query
进行编码,看起来很糟糕……但这是正确的行为:[]
是 保留字符,根据 { {3}}。而且...不,它们不是为传递数组保留的!
[]
所保留的内容在 rfc3986#section-2.3 中定义:
由 Internet 协议文字地址、版本标识的主机
6 [RFC3513] 或更高版本,通过包含 IP 文字来区分
方括号内(“[”和“]”)。 这是唯一的地方
URI 语法中允许使用方括号字符。在
对未来尚未定义的 IP 文字地址格式的预期,
一个实现可以使用一个可选的版本标志来指示这样一个
明确格式化,而不是依赖启发式确定。
[]
所以基本上它是为诸如 IP-literal = "[" ( IPv6address / IPvFuture ) "]"
IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
这里还有一个关于同一主题的问题:rfc3986#section-3.2.2