http_build_query — 生成 URL-encode 之后的请求字符串(http_build_query最新中文翻译)

本文详细介绍了PHP函数http_build_query的基本用法、处理不同类型的数组、对象及多维数组的方法,并通过示例代码展示了如何生成URL-encode之后的请求字符串。
http_build_query — 生成 URL-encode 之后的请求字符串


说明
string http_build_query ( mixed $query_data [, string $numeric_prefix [, string $arg_separator [, int $enc_type = PHP_QUERY_RFC1738 ]]] )
使用给出的关联(或下标)数组生成一个经过 URL-encode 的请求字符串.


参数说明:
query_data
可以是数组或是包含属性的对象, 若query_data是一个数组, 它可能是一维数组, 也可以是包含其它数组的数组.
若query_data是一个对象, 则只有公有的属生才能被合成到结果中.


numeric_prefix
若query_data是一个下标数组, 并且这个参数被提供, 则它将作为数字下标的前缀.
This is meant to allow for legal variable names when the data is decoded by PHP or another CGI application later on.


arg_separator
若此参数没有被提供, 默认情况下, arg_separator.output被提供为分隔参数.


enc_type
这个参数默认值是PHP_QUERY_RFC1738.
If enc_type is PHP_QUERY_RFC1738, then encoding is performed per » RFC 1738 and the application/x-www-form-urlencoded media type, which implies that spaces are encoded as plus (+) signs.
若enc_type为PHP_QUERY_RFC1738, 空格将被编码为 + ;


If enc_type is PHP_QUERY_RFC3986, then encoding is performed according to » RFC 3986, and spaces will be percent encoded (%20).
若enc_type为PHP_QUERY_RFC1738, 空格将被编码为 %20 ;


返回值
生成 URL-encode 之后的请求字符串


官方Demo
1)基本用法
<?php
$data = array('foo'=>'bar',
'baz'=>'boom',
'cow'=>'milk',
'php'=>'hypertext processor');


echo http_build_query($data) . "\n";
echo http_build_query($data, '', '&amp;');


?>


输出为
foo=bar&baz=boom&cow=milk&php=hypertext+processor
foo=bar&amp;baz=boom&amp;cow=milk&amp;php=hypertext+processor


2)http_build_query()处理带数字下标的数组


<?php
$data = array('foo', 'bar', 'baz', 'boom', 'cow' => 'milk', 'php' =>'hypertext processor');


echo http_build_query($data) . "\n";
echo http_build_query($data, 'myvar_');
?>


输出为
0=foo&1=bar&2=baz&3=boom&cow=milk&php=hypertext+processor
myvar_0=foo&myvar_1=bar&myvar_2=baz&myvar_3=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


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
<think>我们被要求编写一个类,将键值对封装为HTTP报文格式并返回字符串。根据引用[2],HTTP请求报文由四个部分组成:请求行、请求头部、空行和请求数据。同时,引用[3]提到了两种常见的请求数据格式:application/x-www-form-urlencoded(类似于查询字符串)和multipart/form-data(用于文件上传)。这里我们假设要封装的是普通的键值对,使用application/x-www-form-urlencoded格式。 步骤: 1. 确定请求方法(如GET、POST)和请求的资源路径(URL的路径部分)。 2. 如果是GET请求,通常将键值对作为查询字符串附加在URL后面,而请求数据部分为空。 3. 如果是POST请求,则将键值对放在请求数据部分,并在请求头中指定Content-Type和Content-Length。 4. 构建请求行:方法、路径和HTTP版本(如"GET /path HTTP/1.1")。 5. 构建请求头部:包括Host、User-Agent、Content-Type(如果是POST且有请求数据)、Content-Length(如果是POST且有请求数据)等。 6. 空行:用一个空行分隔头部和请求数据。 7. 请求数据:对于POST请求,将键值对编码为查询字符串格式(即key1=value1&key2=value2,并进行URL编码)。 注意:我们这里不处理文件上传(multipart/form-data)的情况,只处理简单的键值对。 设计类: 类名:HttpRequestBuilder 属性: - method: 请求方法(如'GET', 'POST') - path: 请求的路径(如'/'或'/index.html') - headers: 一个字典,存储请求头(除了自动计算的内容,其他头由用户设置) - params: 一个字典,存储键值对参数 方法: - __init__(self, method='GET', path='/', headers=None, params=None): 初始化 - add_header(self, key, value): 添加一个请求- add_param(self, key, value): 添加一个参数 - build(self) -> str: 构建并返回整个HTTP请求报文字符串build方法中: 1. 根据方法决定参数放在哪里:GET方法放在URL的查询字符串中,POST方法放在请求体中。 2. 对于GET请求,需要将参数编码后附加到path后面(注意如果path已经有查询字符串,需要处理,但为了简单,我们假设没有,直接附加)。 3. 对于POST请求,需要设置Content-Type为'application/x-www-form-urlencoded',并计算请求体的长度,然后自动添加Content-Type和Content-Length头(如果用户没有设置的话,如果设置了则用用户的)。 4. 构建请求行。 5. 构建请求头部分:每个头字段一行,格式为"Key: Value" 6. 空行后,如果有请求体,则添加请求体。 注意:URL编码使用urllib.parse中的urlencode,但注意urlencode会返回key=value&...的字符串,并且已经进行了编码。 但是注意:在GET请求中,查询字符串不需要放在请求体中,而是放在请求行中的路径后面。例如:GET /path?key1=value1&key2=value2 HTTP/1.1 实现细节: - 对于GET请求,如果params不为空,则构建查询字符串,附加到path后面(注意问号的处理)。 - 对于POST请求,将参数编码后作为请求体,并自动添加Content-Type和Content-Length头(除非用户已经提供了)。 我们假设用户可能会设置自己的Content-Type或Content-Length,所以我们在构建时,如果用户没有设置,则自动添加;如果用户设置了,则使用用户的。 另外,Host头是必须的,但我们可以要求用户在初始化时或通过add_header方法设置,或者在build方法中检查是否设置了Host,如果没有则报错(因为不知道主机名)。但是根据HTTP/1.1规范,Host头是必须的。所以我们可以要求用户必须设置Host。 另一种做法:在构建时,如果发现没有Host头,则抛出一个异常。 为了简单,我们不在类中自动设置Host,而是要求用户设置。 示例: builder = HttpRequestBuilder(method='GET', path='/index.html') builder.add_header('Host', 'www.example.com') builder.add_param('key1', 'value1') builder.add_param('key2', 'value2') print(builder.build()) 对于这个GET请求,构建的请求行应该是:GET /index.html?key1=value1&key2=value2 HTTP/1.1 对于POST请求builder = HttpRequestBuilder(method='POST', path='/submit') builder.add_header('Host', 'www.example.com') builder.add_header('User-Agent', 'MyClient') builder.add_param('name', 'John Doe') builder.add_param('email', 'john@example.com') print(builder.build()) 构建的请求体应该是:name=John+Doe&email=john%40example.com 并且自动添加: Content-Type: application/x-www-form-urlencoded Content-Length: 长度 注意:在POST请求中,参数不会附加在URL上,而是放在请求体中。 代码实现: 我们将使用urllib.parse.urlencode来进行参数的编码。但是注意,urlencode返回的字符串中,空格被编码为'+',而特殊字符被编码为%XX形式,这是application/x-www-form-urlencoded的标准。 步骤: - 导入urllib.parse -build方法中,根据方法处理参数: if self.method == 'GET' and self.params: query_string = urllib.parse.urlencode(self.params) # 将查询字符串附加到path if '?' in self.path: # 如果path已经有查询字符串,我们简单地附加,但注意可能已经有参数,这里为了简单,我们假设没有,所以直接加? # 实际上,如果已经有查询字符串,应该用&连接,但我们的类不处理复杂情况,所以直接加& # 但是为了简单,我们只处理没有?的情况 # 更严谨的做法是检查path是否已有查询字符串,但这里我们假设没有 self.path += '&' + query_string else: self.path += '?' + query_string # 注意,GET请求没有请求体,所以清空请求体数据(即后面不会构建请求体) body = '' elif self.method == 'POST' and self.params: body = urllib.parse.urlencode(self.params) # 自动设置Content-Type和Content-Length,如果用户没有设置的话 if 'Content-Type' not in self.headers: self.add_header('Content-Type', 'application/x-www-form-urlencoded') if 'Content-Length' not in self.headers: # 注意:Content-Length是字符串,但需要是字节长度,而不是字符长度 # 因为中文字符在URL编码后可能占多个字节,所以我们要用字节长度 # 这里我们先将body字符串编码为字节,然后取长度 self.add_header('Content-Length', str(len(body.encode('utf-8')))) else: # 其他方法(如PUT、DELETE)我们暂时不支持,但可以类似POST处理,但这里我们只处理GET和POST body = '' - 构建请求行:f"{self.method} {self.path} HTTP/1.1\r\n" - 构建头部:遍历self.headers,每个头字段为"Key: Value\r\n" - 然后加上一个空行"\r\n" - 如果是POST请求(或有请求体的其他方法),则加上body 注意:我们这里只处理GET和POST,其他方法可以留作扩展。 另外,我们假设用户添加的头部字段是符合规范的(即字段名使用首字母大写,单词用连字符,如'User-Agent'),但我们不强制,直接使用用户设置的键。 但是,在HTTP头部中,字段名是不区分大小写的,但习惯上使用首字母大写。我们这里不进行转换,直接使用用户设置的键。 代码实现如下:</think>下面是一个将键值对封装为HTTP报文的Python类实现,遵循HTTP/1.1规范: ```python from urllib.parse import urlencode class HTTPRequestBuilder: def __init__(self, method="GET", path="/", headers=None, params=None): """ 初始化HTTP请求构建器 :param method: 请求方法 (GET/POST等) :param path: 请求路径 :param headers: 请求头字典 (可选) :param params: 查询参数/表单数据字典 (可选) """ self.method = method.upper() self.path = path self.headers = headers or {} self.params = params or {} # 设置必要头部 if 'Host' not in self.headers: raise ValueError("必须提供'Host'请求头") if 'User-Agent' not in self.headers: self.headers['User-Agent'] = 'CustomHTTPClient/1.0' def build(self): """构建HTTP请求报文并返回字符串""" # 处理查询参数 (GET请求) query_str = "" if self.method == "GET" and self.params: query_str = "?" + urlencode(self.params) # 构建请求行 request_line = f"{self.method} {self.path}{query_str} HTTP/1.1\r\n" # 处理请求(POST请求) body = "" if self.method == "POST": body = urlencode(self.params) self.headers['Content-Type'] = 'application/x-www-form-urlencoded' self.headers['Content-Length'] = str(len(body.encode('utf-8'))) # 构建头部字段 headers_section = "" for key, value in self.headers.items(): headers_section += f"{key}: {value}\r\n" # 组合所有部分 return request_line + headers_section + "\r\n" + body # 使用示例 if __name__ == "__main__": # 创建GET请求 get_builder = HTTPRequestBuilder( method="GET", path="/search", headers={"Host": "www.example.com"}, params={"q": "http协议", "page": "1"} ) print("GET请求报文:") print(get_builder.build()) # 创建POST请求 post_builder = HTTPRequestBuilder( method="POST", path="/submit", headers={ "Host": "api.example.com", "Accept": "application/json" }, params={"username": "john", "password": "secure123"} ) print("\nPOST请求报文:") print(post_builder.build()) ``` ### 关键实现说明: 1. **请求行构造**:根据RFC规范,第一行包含方法、路径和协议版本 - GET请求参数附加在路径后:`$path?key1=value1&key2=value2$` - POST请求参数放在请求体中 2. **头部处理**: - 强制要求`Host`头部(HTTP/1.1必需字段) - 自动添加`User-Agent`默认值 - POST请求自动设置`Content-Type`和`Content-Length` 3. **参数编码**: - 使用`urllib.parse.urlencode`进行URL编码 - 特殊字符自动转义(如空格转为`+`) 4. **报文结构**: ```http GET /search?q=http%E5%8D%8F%E8%AE%AE&page=1 HTTP/1.1 Host: www.example.com User-Agent: CustomHTTPClient/1.0 ``` ```http POST /submit HTTP/1.1 Host: api.example.com Accept: application/json User-Agent: CustomHTTPClient/1.0 Content-Type: application/x-www-form-urlencoded Content-Length: 33 username=john&password=secure123 ``` ### 技术要点: 1. GET和POST的参数处理方式不同: - GET参数在URL查询字符串- POST参数在请求体中 2. `Content-Length`计算的是**字节长度**而非字符长度(使用`encode('utf-8')`确保正确) 3. 遵循HTTP报文格式标准:请求行 + 头部 + 空行 + 请求体[^2] 4. 使用URL编码格式`application/x-www-form-urlencoded`处理表单数据[^3] ### 注意事项: - 需要处理更复杂数据时(如文件上传),应使用`multipart/form-data`格式 - 实际应用中需考虑添加Cookie、Authorization等常用头部 - 对于敏感数据(如密码),建议使用HTTPS而非纯HTTP
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值