ThinkPHP3.1URL分发BUG修正

请先留意以下PHP脚本

PHP脚本A(http://127.0.0.1:8110/test.php):

 1 $url = ‘http://127.0.0.1:8110/demo.php‘;
 2 //curl请求
 3 $ch = curl_init();
 4 curl_setopt($ch, CURLOPT_URL, $url);
 5 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 6 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
 7 curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
 8 curl_setopt($ch, CURLOPT_POST, true);
 9 curl_setopt($ch, CURLOPT_POSTFIELDS, ‘a=dwadwafadwadwadwa&m=dwadwa‘);
10 $result = curl_exec($ch);
11 var_dump($result);
12 exit();

PHP脚本B(127.0.0.1:8110/demo.php):

1 var_dump("PHP input:" . file_get_contents(‘php://input‘));
2 var_dump("POST data:" . $_POST);

执行脚本A的输出结果:

string(81) "string(38) "PHP input:a=dwadwafadwadwadwa&m=dwadwa" string(15) "POST data:Array" "

咦?为何会如此呢?

其实在PHP跟其他编程语言一样,也是有它自己默认的输入流和输出流的。在Web环境下,输入流就是HTTP请求的POST的原始数据,输出流就是HTTP Body,在CLI环境下,就是当前标准的输入输出流(STDIN STDOUT)。而PHP的$_POST全局数组,则是从php://input中解析获得的。

注意1:$HTTP_RAW_POST_DATA也能获取HTTP请求的POST的原始数据,但仅在碰到为识别的MIME类型时产生

注意2:php://input和$HTTP_RAW_POST_DATA均不能在enctype="multipart/form-data"的表单提交下获得数据

接着进入正题,可以看到ThinkPHP3.1的Lib/Core/Dispatcher.class.php有如下代码片段(约在233行):

 1     /**
 2      * 获得实际的操作名称
 3      * @access private
 4      * @return string
 5      */
 6     static private function getAction($var) {
 7         $action   = !empty($_POST[$var]) ?
 8             $_POST[$var] :
 9             (!empty($_GET[$var])?$_GET[$var]:C(‘DEFAULT_ACTION‘));
10         unset($_POST[$var],$_GET[$var]);

在ThinkPHP中Lib/Core/Dispatcher.class.php充当了路由分发的功能(也可以叫前端控制器),这个类负责解析URL并根据约定和配置把后续请求交给指定的Controller和Action执行。

一旦我们在开发中使用了依赖php://input读取请求数据的约定(如一些Flash上传插件,XML-RPC,内部接口服务等等),而刚好提交的数据(可能是二进制数据)又恰好被理解成 /&.+=.+/ 这类字符串,则会导致ThinkPHP出现莫名其妙的URL分发错误(失败)或请求的数据不完整的问题。

而这都是因为ThinkPHP在分发过程中,先检查$_POST再检查$_GET,而且检查获取后又多此一举进行unset造成的。

至此,Lib/Core/Dispatcher.class.php可以修改如下:

 1     /**
 2      * 获得实际的操作名称
 3      * @access private
 4      * @return string
 5      */
 6     static private function getAction($var) {
 7 //         $action   = !empty($_POST[$var]) ?
 8 //             $_POST[$var] :
 9 //             (!empty($_GET[$var])?$_GET[$var]:C(‘DEFAULT_ACTION‘));
10 //         unset($_POST[$var],$_GET[$var]);
11         
12         $action   = (!empty($_GET[$var])?$_GET[$var]:C(‘DEFAULT_ACTION‘));        
13         unset($_GET[$var]);        

问题则解决。

 

总结:该问题很难被发现,也难以定位原因,而且似乎只在ThinkPHP运行在 ‘URL_MODEL‘ => 0 模式下才发生。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。