PHP如何确定浏览器POST请求数据中的字符编码?

5
当浏览器在POST请求的主体中发送数据(即表单元素中的name=value对),PHP如何确定字符编码,以便将比特流正确解码为其自身内部使用的字符?对于某些任务,我可以理解PHP不需要解码,例如对于SQL INSERT查询,它可能只需将数据/字符串传递给DBMS而无需进行其他处理。但对于文本处理/正则表达式操作,我想PHP将需要将比特流解码为字符,然后才能执行测试、模式匹配等操作。另外,由于编码是由浏览器确定的,因此PHP将需要从浏览器获得关于POST数据所使用的字符集的指导。期望这种指导将在请求头中提供,我设置了一个文本表单。
<meta charset="utf-8">

在包含表单的网页头部,填写一些值并提交表单后,请求头不会显式地包含有关如何编码POST数据的信息。
POST /experiments/foo.php HTTP/1.1
Host: localhost
Connection: keep-alive
Content-Length: 57
Pragma: no-cache
Cache-Control: no-cache
Origin: http://localhost
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Referer: http://localhost/experiments/how_does_php_encode_data_it_receives_from_browser.php
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6

还是有其他问题存在吗?例如,浏览器是否需要将字符编码为某个预先确定的标准?
PHP如何知道如何解码从浏览器POST请求接收到的数据?


关键是Content-Type: application/x-www-form-urlencoded 请参考:https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4 - Sammitch
1
PHP无法从Web请求中解码任何内容。Web服务器通常使用apache处理请求,并在需要时调用PHP解释器。然后浏览器告诉Web服务器它如何编码数据,循环继续进行... - Xorifelse
基本上,您应该已经知道预期的字符编码,并配置PHP接受该编码。 - PHP Guru
2个回答

2
关于GET数据,W3C标准说明

注意。 "get"方法将表单数据集值限制为ASCII字符。
只有使用enctype =“multipart / form-data”的“post”方法才指定覆盖整个[ISO10646]字符集。

所以,对于GET,如果表单元素具有属性enctype =“multipart / form-data”,则浏览器似乎被锁定为ASCII,似乎标准支持更大的字符集[ISO10646]
而且我猜因为它更接近纯比特流,所以默认的Content-type application / x-www-form-url-encoded 支持所有字符编码。 特别是这篇文章指出:
http://www.herongyang.com/PHP/Non-ASCII-Form-Basic-Rules.html

URL编码将所有非ASCII字节转换为“% xx”,其中“xx”是字节的HEX值。

因此,这似乎解释了浏览器可能发送的字符集,但不清楚它如何指示PHP实际接收到的字符集(除了GET,PHP将知道它只能是ASCII)。
否则,从我所理解的来看,浏览器对于发送的表单数据的字符编码没有直接指导。
不过我可能错了,如果有任何反馈/替代方案,我会很感兴趣。
否则,从我所知道的,该方案的完整性基本上取决于服务器简单地“记住”
<meta charset="utf-8">

或者
<form ... accept-charset="utf-8">

它发送给用户的值(并希望用户不通过浏览器“设置”更改字符编码),并期望浏览器会忠实地以该字符集发送后续请求。换句话说,如果您的团队中有一个负责HTML的Web设计师,并且他们设置了HTML元标记,他们需要通知数据库管理员:“嘿,您需要设置您的数据库架构、表等来期望UTF-8编码”。这是因为服务器端开发人员/数据库管理员无法动态检查编码(例如,如果表单提交来自字符集不同的国家的用户,其浏览器可能设置为某种不同的字符集)。并可能拒绝或记录警告等...基本上,似乎开发人员需要显式地为包含表单的每个HTML页面设置字符集,例如,然后只需相信浏览器将以与包含表单的HTML编码相同的字符集发送POST数据。

进一步阅读


如果存在 content-type: text/html; charset=... HTTP 头,则 <meta charset="..."> 的值将被忽略。 - PHP Guru

1

来自PHP.net-核心php.ini指令描述:

default_charset 字符串

从PHP 5.6开始,“UTF-8”是默认值,并且如果省略了编码参数,则其值将用作htmlentities(),html_entity_decode()和htmlspecialchars()的默认字符编码。如果iconv.input_encoding,iconv.output_encoding和iconv.internal_encoding配置选项未设置,则default_charset的值还将用于设置iconv函数的默认字符集,以及如果mbstring.http_input mbstring.http_output mbstring.internal_encoding配置选项未设置,则用于mbstring函数。

如果头部没有被header()调用覆盖,所有版本的PHP都将使用此值作为默认Content-Type标题中的字符集。

示例:

Content-Type: text/html; charset=UTF-8

<meta charset="utf-8">标签仅适用于没有此头的响应。但是,由于content-type头优先级高于meta标记,并且PHP始终添加此头,因此忽略了mega标记字符集属性的值。

当使用method = POST(或GET)提交表单时,它会使用声明的字符集对名称-值对进行URL编码,并将它们添加到POST请求的正文中。然后,PHP再次解码它们,并以声明的字符集将它们添加到$ _POST数组中。(通常这将是UTF-8。)

PHP的内部函数基于php.ini中的设置工作。例如,如果default_charset设置为UTF-8,则像htmlspecialchars这样的函数将在传递包含任何无效UTF-8字节序列的字符串时返回空字符串。来自PHP.net:

返回值

转换后的字符串

如果输入字符串在给定编码中包含无效的代码单元序列,则将返回一个空字符串,除非设置了ENT_IGNORE或ENT_SUBSTITUTE标志。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接