值為 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
class 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;
}
如果 (is_array($v) || is_object($v))
{
$r[]=cr_post($v,$k,1);
繼續;
}
$r[]=urlencode($k)."=".urlencode($v);
}
返回 implode("&",$r);
}
</code>
當使用 http_build_query($args) 且 $args 是一個陣列時;請注意陣列的大小有限制。請查看 php.ini 中的 max_input_vars 以增加此大小。
我注意到即使關閉了魔術引號,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
類別 A {
公開 int $publicNotNull;
公開 ?int $publicNull;
私有 string $privateNotNull;
公開 函式 __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"
*/
?>