按道理请求转发并不是PHP所擅长的,至少有更好的可以做到的办法能做到,在服务器层面Nginx能很容易做到(http_proxy),在语言层面我感觉node.js会更好写。但有时候环境所限,就是有这种需求,在此记录一下。

梳理下原理

抛开PHP这个语言因素,从反向代理这个需求入手,做的无非就是一个事:转发请求。客户端发送到A的请求,A转发到B,再把从B处得到的响应返回给客户端。
那么就可以大致知道,A想要实现这个请求转发的功能,就需要做的是:

  • 判断这个请求的方法:get/post
  • 根据请求方法的不同,从请求中解析出参数/数据
    • 如果是get请求,就比较简单,从URL中就可以直接拿到参数了
    • 如果是post请求,就先要判断请求的Content-Type:
      • 如果是 application/x-www-form-urlencoded,就可以从$_post中拿数据了,
      • 如果是其他情况,比如:multipart/form-data等设计文件操作,或者是传递是文本数据(raw data),就要另外考虑了。因为很难在几十行的代码里把所有情况列清楚,这里也只着考虑上一种情况。
  • 根据解析出来的数据/参数,构造一个类似的请求,发送到B
  • 将拿到的响应返回给客户端

根据以上步骤就可以开始写代码了,来试试反向代理百度:

<?

$host = "http://www.baidu.com";
$path = $_SERVER['REQUEST_URI'];
$url = $host.$path;

switch ($_SERVER['REQUEST_METHOD']) {
    case "GET":
        $HTML = sendRequest($url,'GET',null);
        break;
    case "POST":
        switch($_SERVER['CONTENT_TYPE']) {
            case 'application/x-www-form-urlencoded':
                $HTML = sendRequest($url,'POST');
                break;        
            //暂不考虑其他数据上传的情况
        }
    default:
        exit;
}
echo $HTML;

function sendRequest($url,$type){
    $curlHanddle = curl_init();
    if($type == 'POST'){
        //如果是post请求,则设置相关post参数和传递数据
        curl_setopt($curlHanddle, CURLOPT_POST,true);
        $strPOST = json_encode($_POST);
        curl_setopt($curlHanddle, CURLOPT_POSTFIELDS,$strPOST);
    }

    curl_setopt($curlHanddle, CURLOPT_URL, $url);
    curl_setopt($curlHanddle, CURLOPT_RETURNTRANSFER, 1 );
    $resultContent = curl_exec($curlHanddle);
    $resultStatus = curl_getinfo($curlHanddle);
    curl_close($curlHanddle);
    if(intval($resultStatus["http_code"])==200){
        return $resultContent;
    }else{
        return false;
    }
}

通过几十行代码,一个PHP反向代理的demo就出来了,当然完成度还比较低,http博大精深很多情况没考虑,但思路大致如此,运行试试:


跑起来了!