http_build_query() 函数用于生成 URL 编码的查询字符串。它将数组或对象转换为符合 URL 查询字符串格式的字符串,非常适合用于构建 GET 请求参数或 POST 数据。
http_build_query (
mixed $data ,
string $numeric_prefix = "" ,
string $arg_separator = null ,
int $encoding_type = PHP_QUERY_RFC1738
) : string
参数说明:
| 参数 | 说明 | 必需 | 默认值 |
|---|---|---|---|
$data |
要处理的数据,可以是数组或对象 | 是 | 无 |
$numeric_prefix |
当数组的键名为数字时,使用此前缀作为键名前缀 | 否 | ""(空字符串) |
$arg_separator |
参数分隔符,通常为 "&" | 否 | ini_get("arg_separator.output") |
$encoding_type |
编码类型,可选 PHP_QUERY_RFC1738 或 PHP_QUERY_RFC3986 | 否 | PHP_QUERY_RFC1738 |
函数返回 URL 编码后的查询字符串。
name=John+Doe&age=30&city=New+York
编码类型说明:
PHP_QUERY_RFC1738 - 使用 RFC 1738 标准编码(空格编码为 +)PHP_QUERY_RFC3986 - 使用 RFC 3986 标准编码(空格编码为 %20)下面的示例演示如何使用 http_build_query() 函数将关联数组转换为查询字符串。
<?php
// 基础关联数组
$data = [
'name' => 'John Doe',
'age' => 30,
'city' => 'New York',
'country' => 'USA'
];
// 生成查询字符串
$queryString = http_build_query($data);
echo "<h4>生成的查询字符串:</h4>";
echo "<div class='alert alert-info'>" . htmlspecialchars($queryString) . "</div>";
// 构建完整的URL
$url = 'https://api.example.com/user?' . $queryString;
echo "<h4>完整URL:</h4>";
echo "<div class='alert alert-success'><a href='" . htmlspecialchars($url) . "' target='_blank'>" . htmlspecialchars($url) . "</a></div>";
// 解码查看(用于验证)
echo "<h4>解码后的参数:</h4>";
parse_str($queryString, $decoded);
echo "<pre>" . print_r($decoded, true) . "</pre>";
?>
下面的示例演示如何处理数字键名和嵌套数组结构。
<?php
// 包含数字键名和嵌套数组的数据
$data = [
'user' => 'john_doe',
'items' => ['apple', 'banana', 'orange'], // 数字索引数组
'filters' => [
'category' => 'electronics',
'price' => ['min' => 100, 'max' => 500], // 嵌套关联数组
'brands' => ['Samsung', 'Apple', 'Sony'] // 嵌套索引数组
],
0 => 'first_item', // 纯数字键名
1 => 'second_item'
];
echo "<h4>原始数据:</h4>";
echo "<pre>" . print_r($data, true) . "</pre>";
echo "<hr><h4>1. 默认处理(数字键名直接使用):</h4>";
$query1 = http_build_query($data);
echo "<div class='alert alert-light'>" . htmlspecialchars($query1) . "</div>";
echo "<h4>2. 使用数字前缀:</h4>";
$query2 = http_build_query($data, 'param_');
echo "<div class='alert alert-light'>" . htmlspecialchars($query2) . "</div>";
echo "<h4>3. 自定义分隔符:</h4>";
$query3 = http_build_query($data, '', '&');
echo "<div class='alert alert-light'>" . htmlspecialchars($query3) . "</div>";
echo "<h4>4. RFC3986编码:</h4>";
$query4 = http_build_query($data, '', '&', PHP_QUERY_RFC3986);
echo "<div class='alert alert-light'>" . htmlspecialchars($query4) . "</div>";
// 解码其中一个示例查看结构
echo "<h4>解码后的结构(示例1):</h4>";
parse_str($query1, $decoded);
echo "<pre>" . print_r($decoded, true) . "</pre>";
?>
下面的示例演示如何使用 http_build_query() 构建复杂的 API 请求参数。
<?php
/**
* 构建API请求URL
*/
function buildApiUrl($baseUrl, $endpoint, $params = [], $apiKey = null) {
// 添加API密钥(如果提供)
if ($apiKey !== null) {
$params['api_key'] = $apiKey;
}
// 生成查询字符串
$queryString = http_build_query($params);
// 构建完整URL
$url = rtrim($baseUrl, '/') . '/' . ltrim($endpoint, '/');
if (!empty($queryString)) {
$url .= '?' . $queryString;
}
return $url;
}
/**
* 构建分页参数
*/
function buildPaginationParams($page = 1, $limit = 20, $sortBy = 'id', $sortOrder = 'asc') {
return [
'page' => $page,
'limit' => $limit,
'sort_by' => $sortBy,
'sort_order' => $sortOrder
];
}
/**
* 构建搜索过滤器
*/
function buildSearchFilters($filters = []) {
$result = [];
foreach ($filters as $key => $value) {
if (is_array($value)) {
// 处理数组值(如多个选项)
foreach ($value as $index => $item) {
$result["filters[{$key}][]"] = $item;
}
} else {
$result["filters[{$key}]"] = $value;
}
}
return $result;
}
// 示例:构建用户搜索API请求
$baseUrl = 'https://api.example.com';
$endpoint = '/v1/users/search';
// 分页参数
$pagination = buildPaginationParams(2, 30, 'name', 'desc');
// 搜索过滤器
$filters = buildSearchFilters([
'status' => 'active',
'role' => ['admin', 'editor'], // 多选
'created_after' => '2023-01-01',
'age_range' => ['min' => 18, 'max' => 60]
]);
// 合并所有参数
$allParams = array_merge($pagination, $filters);
// 构建URL
$apiKey = 'your_api_key_here';
$apiUrl = buildApiUrl($baseUrl, $endpoint, $allParams, $apiKey);
echo "<h4>生成的API URL:</h4>";
echo "<div class='alert alert-info'><a href='" . htmlspecialchars($apiUrl) . "' target='_blank'>" . htmlspecialchars($apiUrl) . "</a></div>";
echo "<h4>参数详情:</h4>";
echo "<table class='table table-bordered'>";
echo "<thead><tr><th>参数</th><th>值</th><th>说明</th></tr></thead>";
echo "<tbody>";
$paramDetails = [
'page=2' => '第2页',
'limit=30' => '每页30条记录',
'sort_by=name' => '按名称排序',
'sort_order=desc' => '降序排列',
'filters[status]=active' => '状态为活跃',
'filters[role][]=admin' => '角色包含管理员',
'filters[role][]=editor' => '角色包含编辑',
'filters[created_after]=2023-01-01' => '创建时间在2023年1月1日之后',
'filters[age_range][min]=18' => '最小年龄18岁',
'filters[age_range][max]=60' => '最大年龄60岁',
'api_key=your_api_key_here' => 'API密钥'
];
foreach ($paramDetails as $param => $description) {
echo "<tr><td><code>" . htmlspecialchars($param) . "</code></td><td>" . htmlspecialchars(explode('=', $param)[1] ?? '') . "</td><td>" . htmlspecialchars($description) . "</td></tr>";
}
echo "</tbody></table>";
?>
下面的示例演示如何处理对象以及包含特殊字符的数据。
<?php
// 创建一个示例对象
class UserProfile {
public $username = 'john_doe';
public $email = 'john@example.com';
public $preferences = ['theme' => 'dark', 'language' => 'en'];
private $password = 'secret'; // 私有属性不会被包含
public function getData() {
return [
'username' => $this->username,
'email' => $this->email
];
}
}
// 包含特殊字符的数据
$specialData = [
'search' => 'price > 100 & rating < 5', // 包含HTML特殊字符
'message' => 'Hello "World" & Friends', // 包含引号和特殊字符
'path' => '/home/user/files/document.pdf', // 包含斜杠
'unicode' => '中文 Español Français', // 包含Unicode字符
'spaces' => 'multiple spaces here', // 多个空格
'special_chars' => '!@#$%^&*()_+-=[]{}|;:,./<>?' // 各种特殊字符
];
echo "<h4>1. 处理对象:</h4>";
$user = new UserProfile();
$objectQuery = http_build_query($user);
echo "<div class='alert alert-light'>" . htmlspecialchars($objectQuery) . "</div>";
echo "<p><small>注意:只有公共属性会被包含在查询字符串中。</small></p>";
echo "<h4>2. 处理特殊字符(RFC1738编码):</h4>";
$query1 = http_build_query($specialData);
echo "<div class='alert alert-light' style='word-break: break-all;'>" . htmlspecialchars($query1) . "</div>";
echo "<h4>3. 处理特殊字符(RFC3986编码):</h4>";
$query2 = http_build_query($specialData, '', '&', PHP_QUERY_RFC3986);
echo "<div class='alert alert-light' style='word-break: break-all;'>" . htmlspecialchars($query2) . "</div>";
// 比较两种编码的区别
echo "<h4>4. 编码差异对比:</h4>";
echo "<table class='table table-bordered table-sm'>";
echo "<thead><tr><th>原始字符</th><th>RFC1738编码</th><th>RFC3986编码</th><th>说明</th></tr></thead>";
echo "<tbody>";
$comparisons = [
['字符' => ' ', 'RFC1738' => '+', 'RFC3986' => '%20', '说明' => '空格'],
['字符' => '&', 'RFC1738' => '%26', 'RFC3986' => '%26', '说明' => '与符号'],
['字符' => '=', 'RFC1738' => '%3D', 'RFC3986' => '%3D', '说明' => '等号'],
['字符' => '?', 'RFC1738' => '%3F', 'RFC3986' => '%3F', '说明' => '问号'],
['字符' => '#', 'RFC1738' => '%23', 'RFC3986' => '%23', '说明' => '井号'],
['字符' => '/', 'RFC1738' => '%2F', 'RFC3986' => '%2F', '说明' => '斜杠'],
['字符' => '中', 'RFC1738' => '%E4%B8%AD', 'RFC3986' => '%E4%B8%AD', '说明' => '中文字符']
];
foreach ($comparisons as $row) {
echo "<tr>";
echo "<td><code>" . htmlspecialchars($row['字符']) . "</code></td>";
echo "<td><code>" . htmlspecialchars($row['RFC1738']) . "</code></td>";
echo "<td><code>" . htmlspecialchars($row['RFC3986']) . "</code></td>";
echo "<td>" . htmlspecialchars($row['说明']) . "</td>";
echo "</tr>";
}
echo "</tbody></table>";
?>
下面的示例演示在实际应用中使用 http_build_query() 的场景。
<?php
// 场景1:处理表单数据并重定向
function processFormAndRedirect($formData) {
// 过滤和验证数据
$filteredData = array_filter($formData, function($value) {
return !empty($value) || $value === 0 || $value === '0';
});
// 添加时间戳和哈希(防止重复提交)
$filteredData['timestamp'] = time();
$filteredData['hash'] = md5(serialize($filteredData) . 'secret_salt');
// 生成查询字符串
$queryString = http_build_query($filteredData);
// 重定向到结果页面
$redirectUrl = '/result.php?' . $queryString;
return $redirectUrl;
}
// 模拟表单数据
$formData = [
'name' => '张三',
'email' => 'zhangsan@example.com',
'phone' => '13800138000',
'age' => 25,
'interests' => ['编程', '阅读', '运动'],
'newsletter' => true,
'agree_terms' => 1,
'empty_field' => '', // 空字段将被过滤
'zero_value' => 0 // 0值将被保留
];
echo "<h4>场景1:表单处理与重定向</h4>";
$redirectUrl = processFormAndRedirect($formData);
echo "<div class='alert alert-info'>重定向URL: " . htmlspecialchars($redirectUrl) . "</div>";
// 场景2:构建分页链接
function buildPaginationLinks($baseUrl, $totalItems, $currentPage = 1, $itemsPerPage = 10, $maxLinks = 5) {
$totalPages = ceil($totalItems / $itemsPerPage);
$links = [];
// 计算显示的页码范围
$start = max(1, $currentPage - floor($maxLinks / 2));
$end = min($totalPages, $start + $maxLinks - 1);
// 调整起始页码
if ($end - $start + 1 < $maxLinks) {
$start = max(1, $end - $maxLinks + 1);
}
// 构建页码链接
for ($page = $start; $page <= $end; $page++) {
$params = [
'page' => $page,
'limit' => $itemsPerPage,
'sort' => 'date',
'order' => 'desc'
];
$queryString = http_build_query($params);
$links[$page] = $baseUrl . '?' . $queryString;
}
return [
'total_pages' => $totalPages,
'current_page' => $currentPage,
'links' => $links
];
}
echo "<h4>场景2:分页链接生成</h4>";
$pagination = buildPaginationLinks('/articles.php', 156, 3, 10, 5);
echo "<p>总页数: {$pagination['total_pages']}</p>";
echo "<p>当前页: {$pagination['current_page']}</p>";
echo "<div class='btn-group' role='group'>";
foreach ($pagination['links'] as $page => $url) {
$activeClass = ($page == $pagination['current_page']) ? 'btn-primary' : 'btn-outline-primary';
echo "<a href='" . htmlspecialchars($url) . "' class='btn $activeClass'>$page</a>";
}
echo "</div>";
// 场景3:API请求签名
function signApiRequest($baseUrl, $params, $secretKey) {
// 按参数名排序(签名常用要求)
ksort($params);
// 生成查询字符串(不带签名)
$queryString = http_build_query($params);
// 计算签名
$signature = hash_hmac('sha256', $queryString, $secretKey);
// 添加签名到参数
$params['signature'] = $signature;
// 重新生成查询字符串(带签名)
$signedQueryString = http_build_query($params);
return $baseUrl . '?' . $signedQueryString;
}
echo "<h4>场景3:API请求签名</h4>";
$apiParams = [
'action' => 'get_user',
'user_id' => 12345,
'timestamp' => time(),
'nonce' => uniqid()
];
$secretKey = 'your_secret_key';
$signedUrl = signApiRequest('https://api.example.com/v1/user', $apiParams, $secretKey);
echo "<div class='alert alert-success' style='word-break: break-all;'>";
echo "签名后的URL: <br>" . htmlspecialchars($signedUrl);
echo "</div>";
?>
http_build_query() 会自动处理嵌套数组,生成如 filters[category]=electronics&filters[price][min]=100 的格式。$numeric_prefix 参数。PHP_QUERY_RFC1738 和 PHP_QUERY_RFC3986 对空格等字符的编码方式不同。http_build_query() 和 parse_str() 是互逆操作,可以相互转换。| 函数 | 用途 | 输入 | 输出 | 说明 |
|---|---|---|---|---|
http_build_query() |
生成URL编码的查询字符串 | 数组或对象 | 字符串 | 自动处理嵌套结构,支持多种编码 |
parse_str() |
解析查询字符串到变量 | 字符串 | 数组 | http_build_query()的逆操作 |
urlencode() |
编码URL字符串 | 字符串 | 字符串 | 编码单个字符串,不处理数组结构 |
rawurlencode() |
按照RFC 3986编码URL | 字符串 | 字符串 | 空格编码为%20,适合路径部分 |
urldecode() |
解码URL编码的字符串 | 字符串 | 字符串 | 解码urlencode()编码的字符串 |