值为 null 的参数不会出现在结果字符串中。
<?php
$arr = array('test' => null, 'test2' => 1);
echo http_build_query($arr);
?>
将生成
test2=1
(PHP 5, PHP 7, PHP 8)
http_build_query — 生成 URL 编码的查询字符串
$data
,$numeric_prefix
= "",$arg_separator
= null
,$encoding_type
= PHP_QUERY_RFC1738
从提供的关联(或索引)数组生成 URL 编码的查询字符串。
data
可以是包含属性的数组或对象。
如果 data
是一个数组,它可以是一个简单的单维结构,或者是一个数组的数组(依次可能包含其他数组)。
如果 data
是一个对象,则只有公共属性会被纳入结果。
numeric_prefix
如果在基础数组中使用了数字索引并且提供了此参数,则它将仅附加到基础数组中元素的数字索引之前。
这旨在允许在稍后由 PHP 或其他 CGI 应用程序解码数据时使用合法的变量名。
arg_separator
参数分隔符。如果未设置或为 null
,则使用 arg_separator.output 分隔参数。
encoding_type
默认为 PHP_QUERY_RFC1738
。
如果 encoding_type
为 PHP_QUERY_RFC1738
,则根据 » RFC 1738 和 application/x-www-form-urlencoded
媒体类型执行编码,这意味着空格被编码为加号 (+
) 符号。
如果 encoding_type
为 PHP_QUERY_RFC3986
,则根据 » RFC 3986 执行编码,空格将被百分比编码 (%20
)。
返回一个 URL 编码的字符串。
版本 | 描述 |
---|---|
8.0.0 |
arg_separator 现在可以为 null。 |
示例 #1 http_build_query() 的简单用法
<?php
$data = array(
'foo' => 'bar',
'baz' => 'boom',
'cow' => 'milk',
'null' => null,
'php' => 'hypertext processor'
);
echo http_build_query($data) . "\n";
echo http_build_query($data, '', '&');
?>
以上示例将输出
foo=bar&baz=boom&cow=milk&php=hypertext+processor foo=bar&baz=boom&cow=milk&php=hypertext+processor
示例 #2 带有数字索引元素的 http_build_query()。
<?php
$data = array('foo', 'bar', 'baz', null, 'boom', 'cow' => 'milk', 'php' => 'hypertext processor');
echo http_build_query($data) . "\n";
echo http_build_query($data, 'myvar_');
?>
以上示例将输出
0=foo&1=bar&2=baz&4=boom&cow=milk&php=hypertext+processor myvar_0=foo&myvar_1=bar&myvar_2=baz&myvar_4=boom&cow=milk&php=hypertext+processor
示例 #3 使用复杂数组的 http_build_query()
<?php
$data = array(
'user' => array(
'name' => 'Bob Smith',
'age' => 47,
'sex' => 'M',
'dob' => '5/12/1956'
),
'pastimes' => array('golf', 'opera', 'poker', 'rap'),
'children' => array(
'bobby' => array('age'=>12, 'sex'=>'M'),
'sally' => array('age'=>8, 'sex'=>'F')
),
'CEO'
);
echo http_build_query($data, 'flags_');
?>
以上示例将输出:(为易读性而换行)
user%5Bname%5D=Bob+Smith&user%5Bage%5D=47&user%5Bsex%5D=M& user%5Bdob%5D=5%2F12%2F1956&pastimes%5B0%5D=golf&pastimes%5B1%5D=opera& pastimes%5B2%5D=poker&pastimes%5B3%5D=rap&children%5Bbobby%5D%5Bage%5D=12& children%5Bbobby%5D%5Bsex%5D=M&children%5Bsally%5D%5Bage%5D=8& children%5Bsally%5D%5Bsex%5D=F&flags_0=CEO
注意:
只有基础数组“CEO”中的数字索引元素接收了前缀。在 pastimes 下找到的其他数字索引不需要字符串前缀即可成为合法的变量名。
示例 #4 使用 http_build_query() 和对象
<?php
类 parentClass {
public $pub = 'publicParent';
protected $prot = 'protectedParent';
private $priv = 'privateParent';
public $pub_bar = null;
protected $prot_bar = null;
private $priv_bar = null;
public function __construct(){
$this->pub_bar = new childClass();
$this->prot_bar = new childClass();
$this->priv_bar = new childClass();
}
}
class childClass {
public $pub = 'publicChild';
protected $prot = 'protectedChild';
private $priv = 'privateChild';
}
$parent = new parentClass();
echo http_build_query($parent);
?>
以上示例将输出
pub=publicParent&pub_bar%5Bpub%5D=publicChild
值为 null 的参数不会出现在结果字符串中。
<?php
$arr = array('test' => null, 'test2' => 1);
echo http_build_query($arr);
?>
将生成
test2=1
将 null 传递给 $arg_separator 与传递空字符串相同,这可能不是您想要的。
如果您需要更改 enc_type,请使用以下代码
http_build_query($query, null, '&', PHP_QUERY_RFC3986);
或者可能是这个
http_build_query($query, null, ini_get('arg_separator.output'), PHP_QUERY_RFC3986);
但不要使用这个
// 错误代码!
http_build_query($query, null, null, PHP_QUERY_RFC3986);
此函数会生成如下内容
files[0]=1&files[1]=2&...
要生成如下内容
files[]=1&files[]=2&...
请执行以下操作
$query = http_build_query($query);
$query = preg_replace('/%5B[0-9]+%5D/simU', '%5B%5D', $query);
如果您发送布尔值,它将转换为整数
$a = [teste1= true,teste2=false];
echo http_build_query($a)
// 结果将为 teste1=1&teste2=0
在 <?php http_build_query() ?> 中发生的数字到字符串的转换受区域设置的影响,这可能并不明显。
<?php
$params = ["v" => 5.63];
setlocale(LC_ALL, 'us_En');
http_build_query($params) // v=5.63
setlocale(LC_ALL, 'ru_RU');
http_build_query($params) // v=5,63 注意逗号
?>
如前所述,在 php5.3 中,某些服务器上的分隔符为 &。通常,如果发布到另一台 php5.3 服务器,这不会有问题。
但是,如果您发布到 Tomcat Java 服务器或其他服务器,则 & 可能无法正确处理。
要解决此问题,请指定
http_build_query($array, '', '&');
而不是
http_build_query($array); // 对某些服务器返回 &
如果您需要反向功能,并且(像我一样)无法使用 pecl_http,您可能希望使用类似以下内容。
<?php function http_parse_query($Query) {
// 模仿 $_GET 的行为,另请参阅 RFC 1738 和 3986。
$Delimiter = ini_get('arg_separator.input');
$Params = array();
foreach (explode($Delimiter, $Query) as $NameValue) {
preg_match(
'/^(?P<name>[^=\[]*)(?P<indices_present>\[(?P<indices>[^\]]*(\]\[[^\]]*)*)\]?)?(?P<value_present>=(?P<value>.*))?$/',
$NameValue,
$NameValueParts
);
if (!empty($NameValueParts)) {
$Param =& $Params[$NameValueParts['name']];
if (!empty($NameValueParts['indices_present'])) {
$Indices = explode('][', $NameValueParts['indices']);
foreach ($Indices as $Index) {
if (!is_array($Param)) {
$Param = array();
}
if ($Index === '') {
$Param[] = array();
end($Param);
$Param =& $Param[key($Param)];
} else {
if (ctype_digit($Index)) { $Index = (int) $Index; }
if (!array_key_exists($Index, $Param)) {
$Param[$Index] = array();
}
$Param =& $Param[$Index];
}
}
}
if (!empty($NameValueParts['value_present'])) {
$Param = urldecode($NameValueParts['value']);
} else {
$Param = '';
}
}
}
return $Params;
}?>
是否值得注意的是,如果 query_data 是一个关联数组,并且一个值本身是一个空数组,或者一个仅包含空数组的数组(或包含仅包含空数组的数组等),则相应的键将不会出现在结果查询字符串中?
例如
$post_data = array('name'=>'miller', 'address'=>array('address_lines'=>array()), 'age'=>23);
echo http_build_query($post_data);
将打印
name=miller&age=23
请注意示例 1 - 它正是 *不* 应该如何实现。
& 作为分隔符是 URL 编码。
& 是 HTML 编码。
如果将 URL 嵌入到网页中,则应对其进行 HTML 编码。这比简单地将 & 替换为 & 复杂得多。按照此示例中的建议操作会导致安全漏洞。
当使用 http_build_query 函数从数组创建 URL 查询以用于 curl_setopt($ch, CURLOPT_POSTFIELDS, $post_url) 等内容时,请注意 URL 编码。
在我的情况下,我只是想将接收到的 $_POST 数据传递到 CURL 的 POST 数据,这需要它以 URL 格式表示。如果像空格 [ ] 这样的内容进入 http_build_query,它会输出为 +。如果您随后再次发送此数据进行 POST,则不会获得预期的结果。这对于 GET 很有用,但对于 POST 则不行。
相反,如果您只是想传递数据,可以创建自己的简单函数
<?php
$post_url = '';
foreach ($_POST AS $key=>$value)
$post_url .= $key.'='.$value.'&';
$post_url = rtrim($post_url, '&');
?>
然后你可以使用它在 CURL 中传递 POST 数据。
<?php
$ch = curl_init($some_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_url);
curl_exec($ch);
?>
请注意,在处理 POST 数据的最终页面上,您应该正确地过滤/转义它。
如前所述,此函数忽略了值为 null 的键。这可能会破坏某些将键视为布尔值(因此没有值)的代码,或者期望无论值如何都填充数组的其他代码。
对此的解决方法是用空字符串替换 null 值
$data=array(
'a'=>'apple',
'b'=>2,
'c'=>null,
'd'=>'…',
);
// 补偿 http_build_query 忽略 null 值的事实
foreach($data as &$datum) if($datum===null) $datum='';
如果它应该是一个真正的查询字符串,那么丢失原始的 null 值并不是真正的损失。如果 null 很重要,则可以使用虚拟值代替。
Mark
正确实现对不带索引的参数数组的编码(valdikks 修复的代码 - 对内部数组不起作用)
<code>
function cr_post($a,$b='',$c=0)
{
if (!is_array($a)) return false;
foreach ((array)$a as $k=>$v)
{
if ($c)
{
if( is_numeric($k) )
$k=$b."[]";
else
$k=$b."[$k]";
}
else
{ if (is_int($k))
$k=$b.$k;
}
if (is_array($v)||is_object($v))
{
$r[]=cr_post($v,$k,1);
continue;
}
$r[]=urlencode($k)."=".urlencode($v);
}
return implode("&",$r);
}
</code>
当使用 http_build_query($args) 其中 $args 是一个数组时;请注意,数组的大小有限制。请参阅 php.ini 中的 max_input_vars 以增加此大小。
我注意到即使在禁用 magic quotes 后,http_build_query() 也会自动为字符串添加斜杠。
所以,我必须为每个字符串变量添加“stripslashes”。
在我的 PHP 5.3 安装中,http_build_query() 似乎使用 & 作为默认分隔符。当与 stream_context_create() 结合用于 POST 请求时,这很有趣,并且在接收端获取 $_POST['amp;fieldName']。
警告:不同的数组可能会返回相同的结果
<CODE>
$a1 = array('x[y]' => array('a'=>1));
$a2 = array('x' => array('y' => array('a'=>1)));
$q1 = http_build_query($a1);
$q2 = http_build_query($a2);
var_dump($a1);
echo '<BR>';
var_dump($a2);
echo '<BR>';
echo $q1;
echo '<BR>';
echo $q2;
echo '<BR>';
</CODE>
结果
array(1) { ["x[y]"]=> array(1) { ["a"]=> int(1) } }
array(1) { ["x"]=> array(1) { ["y"]=> array(1) { ["a"]=> int(1) } } }
x%5By%5D%5Ba%5D=1
x%5By%5D%5Ba%5D=1
文档中没有提到,但在对对象调用 http_build_query 时,公共 null 字段会被忽略。
<?php
class A {
public int $publicNotNull;
public ?int $publicNull;
private string $privateNotNull;
public function __construct()
{
$this->publicNotNull = 2;
$this->privateNotNull = "Test";
}
}
$a = new A();
echo http_build_query($a); // publicNotNull=2
?>
虽然 http_build_query 也可以用于将大多数类编码为查询字符串,但带有 <![CDATA[]]> 值的 SimpleXML 元素会被识别为空数组,因此不会自然地包含在内。
<?php
$xml = simplexml_load_string( '<wrapper><key><![CDATA[value]]></key><key2>value2</key2></wrapper>' );
var_dump( $xml, http_build_query( $xml ) );
/* 输出:
object(SimpleXMLElement)#1 (2) {
["key"]=>
object(SimpleXMLElement)#2 (0) {
}
["key2"]=>
string(6) "value2"
}
string(11) "key2=value2"
*/
?>