前天,在搞个功能的时候,发现页面ajax过来的值太多了,导致不论是$_POST还是php://input都为空了,很奇怪。
于是弄了demo测试找原因:
<script>
var data = 'username=111&email=222';
for (var i = 0; i < 1750; i++)
{
data += '&a' + i + '=' + i;
}
var xhr = new XMLHttpRequest();
xhr.open('post', './test.php');
// xhr.open('put', './test.php');
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
// xhr.setRequestHeader('Content-type', 'application/json;charset=utf-8');
// data = JSON.stringify({a: data});
xhr.onload = function () { console.log(xhr.responseText); }
xhr.send(data);
</script>
因为method和Content-type都感觉是对的,但为什么就是拿不到值呢?
我换了application/json和application/x-www-form-urlencoded都会出现这样的问题,
而且好像有个临界值,代码里循环1650就能看到打印数据,1750次就空了。test.php只是简单的
$content = file_get_contents('php://input');
// print_r(count($_POST) . "\n");
print_r($content);
有点纳闷,不停地google,百度找资料,知道post方法,php.ini里是有指令限制传参的个数、大小的,
但是我是file_get_contents('php://input')的拿的鸭,而且php输入流是不受php.ini配置文件指令的影响的啊。
为什么循环少点就可以,多点就不行呢?error_reporting(-1)也没报错,也没警告,没有头绪。
后来,我将ajax的请求方法,改成了PUT,就发现了点端倪(此时还没弄懂post和put的区别)
报了一个warning,有错误才更容易找到踩坑的原因啊,继续追
不能创建temporary临时文件,因为没有权限,而且看了数据,也不全,没有拿到循环到1749次的数据,好像截断了,然后报这个警告
接着查看PHP脚本执行时会操作到的临时文件目录
echo sys_get_temp_dir();
发现,果然没有权限!
然后,我发现我忘记了一项很重要的事情,就是看日志!啊啊啊啊啊,我忘了多少次排错找bug的时候,没去看日志
果然是有东西的,在apache的error_log,具体看自己配置的日志文件目录。
其实就是上图那个Warning的信息。可是我将ajax的方法改为原先的post测试时,却得到不一样的错误日志。
打印数据依旧为空,但是从日志里好像知道了原因:
看到没,因为没权限打开临时操作文件,无法将post传输的数据buffer缓冲起来,然后discard 全部舍弃掉了,所以print_r一直没空咯。
到这里,好像是找到问题的解决办法了?
就是设置临时操作文件的权限,图快又不考虑安全性的话,chmod 777 /tmp
。。。
将temp临时文件的权限设置好可写之后,我需要重新测试一遍。
改为post请求,Content-type为application/x-www-form-urlencoded,将循环次数改为1750次或更大,
打印$_POST能得到数据,只要数据大小不超过post_max_size(默认是8m)就好,但是因为变量数超过max_input_vars(默认1000个)就被PHP解析器截断了。
打印file_get_content('php://input')能获取到所有的数据,因为php://input输入流不关配置文件影响,没被截断。
改为put请求,打印$_POST为空,打印file_get_content('php://input')能获取到所有的数据正常。
但是,为什么?当临时文件没权限写入的情况下,情况是不一样呢????
没有权限的前提下,
post请求,循环1650次,正常打印,循环1750次,都变空了,看错误日志是被直接discard舍弃掉。
而
put请求,循环1650次,正常打印,循环1750次,file_get_contents报了个Warning,然后打印出部分数据了,不完整
为什么会这样的原因,我还没搞得彻底清除。猜测是这样,
file_get_content('php://input'),是可以获取所有输入流的数据,且不受php.ini指令影响,但是使用这个函数获取内容时,
有两个要点:
1.file_get_contents需要有可写入临时文件的权限
2.获取的内容大小存在一个设定值(具体是多少,还是配置文件控制,没搞懂),当内容大小超过这个值之后,会把剩下的内容写到一个临时文件里面。
为什么会出现这样的一个情况呢?
了解了一下post和put的区别,大概也就是基于restful规范上的语义本质区别吧。
没搞懂这其中的原因 = =
猜想应该是PHP的输入流都需要拥有临时文件的写入权限,
不只是file_get_contents('php://input'),还有GPC
当传输内容大小没超过某个定值(好像是8k?)时,不需要操作临时文件,超过时
需要有临时文件的权限把内容写入,否则全部数据都会被丢空。
post和put请求出现结果的不同可能就再于:
当没临时文件写入权限时