注:这是我去年写在知乎里的文章。后来要查阅,来CSDN博客里翻了底朝天都没找到,甚至一度怀疑CSDN丢文章。特此转发已备后用。另外发现在知乎的原文好像已经不能编辑了,而我又需要编辑它,这是转发至此的又一个理由。
原文
我前两天(当时时间是2017年1月)用易语言写一个(原以为很简单的)客户端软件,将JSON文本POST给PHP,Content-Type
是application/json
,内容就是JSON文本。结果PHP的同事把整个$_POST
都输出看了,里面根本就没有数据。我郁闷了很久。后来各种尝试,最后把Content-Type
改成application/x-www-form-urlencoded
,把POST内容改为data=json
形式,并且对json部分应用URL编码,才搞定了与PHP的通讯。但依然很迷惑。
后来我专程去查了$_POST
的官方文档,人家说的很明白,只有Content-Type
为application/x-www-form-urlencoded
或multipart/form-data
的情况下,$_POST
里面才有值。在其他情况下,$_POST
里面是没有值的。
对于客户端POST提交的 Content-Type: application/json
的情况,或其他未明确指定 Content-Type
的情况,PHP可以用 file_get_contents("php://input");
读出POST进来的数据。(作者注:本文末尾有 php://input
的说明文档,带官方链接。)
现在总算明白了。
以下是 20180807 Liigo补记:
呵呵,有点尴尬呀。前面说“现在总算明白了”,事实证明还是有点不太明白。
后来又发现了奇怪的现象。客户端POST提交GB18030编码的带汉字的文本,PHP端用file_get_contents("php://input")
接收然后用 file_put_contents()
写出文件,用文本编辑器打开文件显示正常。而如果客户端POST提交UTF-8编码的相同内容的文本,PHP端用相同的方法接收和写出文件,用文本编辑器打开文件就是乱码。后来证明文本编辑器的编码识别没有问题,进而证明第二次写出的文件编码即不是UTF-8也不是GB18030。这就很奇怪。
我心中提出了几个问题:
file_get_contents("php://input")
视其输入数据为什么编码?其输出数据是什么编码?内部有没有进行过编码转换?- 用
file_put_contents()
写出文件的过程中有没有编码转换?其输入和输出数据各是什么编码呢? - 前面提到的POST提交数据时没有明确指定
Content-Type
,结果是否于此有关呢? - 第一次POST的编码是GB18030,写出的文件编码也是GB18030,这能说明什么呢?
多个问号有待解答。
最基础的测试代码
PHP端代码:
echo file_get_contents("php://input");
易语言端代码:
respone = 网页_访问_对象 (url, 1, "POST数据")
调试输出 (respone, 编码_Utf8到Ansi (respone), 到文本 (respone))
目前能确认的结论是:(在上述情况下)POST只能提交GB18030编码,不能提交UTF-8编码,否则得到的结果是乱码。
在提交GB18030编码的情况下(情况1),PHP网页返回的是UTF-8编码的Respone(估计是 echo
默认输出UTF-8),转换为GB18030后,还原为原始的数据,结果是正确的。在提交UTF-8编码的情况下(情况2),PHP网页返回的数据不知道是什么编码,既不是UTF-8也不是GB18030,怎么转换结果都不对。仔细校验后发现,情况2返回的respone数据是在UTF-8编码基础上再执行UTF-8编码的结果,由此可以断定,PHP是把客户端提交的UTF-8编码的数据当作GB18030编码处理了。
客户端POST提交数据时并没有明确指定数据编码,故PHP假定其为某个编码(此处为GB18030,估计跟PHP配置有关)也有其合理性。那么POST提交时能否指定编码呢?好像是有,Content-Type: application/json; charset=utf-8
。然而好像并不管用:
post = “A汉字Z”
post = 编码_gb2312到utf8 (post)
headers = “Content-Type: application/json; charset=utf-8”
respone = 网页_访问_对象 (“http://yishoudan.com/m/order/test”, 1, post, , , headers)
调试输出 (respone, 编码_Utf8到Ansi (respone), 到文本 (respone))
php://input
下面补充一些 php://input
的官方文档:
php://input
is a read-only stream that allows you to read raw data from the request body. In the case of POST requests, it is preferable to usephp://input
instead of$HTTP_RAW_POST_DATA
as it does not depend on specialphp.ini
directives. Moreover, for those cases where$HTTP_RAW_POST_DATA
is not populated by default, it is a potentially less memory intensive alternative to activatingalways_populate_raw_post_data
.php://input
is not available withenctype="multipart/form-data"
.