首页 > 代码库 > ThinkPHP3.1URL分发BUG修正

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 模式下才发生。

ThinkPHP3.1URL分发BUG修正