关于php:curl跟随位置错误

curl follow location error

我收到此错误消息:

CURLOPT_FOLLOWLOCATION cannot be activated when in safe_mode or an open_basedir is set in.

在我的网络托管上,safe_mode已关闭。

open_basedir是"。

我该如何解决?


解决方法是在PHP代码中实现重定向。

这是我自己的实现。它有两个已知的局限性:

  • 它将强制CURLOPT_RETURNTRANSFER
  • CURLOPT_HEADERFUNCTION不兼容
  • 代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    function curl_exec_follow(/*resource*/ &$ch, /*int*/ $redirects = 20, /*bool*/ $curlopt_header = false) {
        if ((!ini_get('open_basedir') && !ini_get('safe_mode')) || $redirects < 1) {
            curl_setopt($ch, CURLOPT_HEADER, $curlopt_header);
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $redirects > 0);
            curl_setopt($ch, CURLOPT_MAXREDIRS, $redirects);
            return curl_exec($ch);
        } else {
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
            curl_setopt($ch, CURLOPT_HEADER, true);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_FORBID_REUSE, false);

            do {
                $data = curl_exec($ch);
                if (curl_errno($ch))
                    break;
                $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                if ($code != 301 && $code != 302)
                    break;
                $header_start = strpos($data,"

    "
    )+2;
                $headers = substr($data, $header_start, strpos($data,"



    "
    , $header_start)+2-$header_start);
                if (!preg_match("!

    (?:Location|URI): *(.*?) *

    !"
    , $headers, $matches))
                    break;
                curl_setopt($ch, CURLOPT_URL, $matches[1]);
            } while (--$redirects);
            if (!$redirects)
                trigger_error('Too many redirects. When following redirects, libcurl hit the maximum amount.', E_USER_WARNING);
            if (!$curlopt_header)
                $data = substr($data, strpos($data,"



    "
    )+4);
            return $data;
        }
    }


    打印此警告消息的唯一位置是ext / curl / interface.c。

    1
    2
    3
    4
    5
    6
    7
    if ((PG(open_basedir) && *PG(open_basedir)) || PG(safe_mode)) {
      if (Z_LVAL_PP(zvalue) != 0) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING,"CURLOPT_FOLLOWLOCATION cannot be activated when in safe_mode or an open_basedir is set");
        RETVAL_FALSE;
        return 1;
      }
    }

    从if条件可以看出,必须启用open_basedir或safe_mode。


    不久前,我遇到了类似的情况,并在下面找到了解决方案。如果您大致知道将您重定向到的位置,则可能对您有用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
        function curl($url, $postVars)
    {
        $go = curl_init($url);
        curl_setopt ($go, CURLOPT_URL, $url);
        curl_setopt($go, CURLOPT_VERBOSE, 1);

        //follow on location problems
        if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off'))
        {
            curl_setopt ($go, CURLOPT_FOLLOWLOCATION, $l);
            $syn = curl_exec($go);
            if(curl_error($go))
                return false;
        }
        else
            $syn = curl_redir_exec($go, $postVars);
        curl_close($go);
        return $syn;
    }

    function curl_redir_exec($ch, $postVars)
    {
        static $curl_loops = 0;
        static $curl_max_loops = 20;
        if ($curl_loops++>= $curl_max_loops)
        {
            $curl_loops = 0;
            return FALSE;
        }
        curl_setopt($ch, CURLOPT_HEADER, 1);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postVars);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);

        $data = curl_exec($ch);
        if(curl_error($ch))
            return false;
        list($header, $data) = explode("

    "
    , $data, 2);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

        $redirect_page ="[0-9]*.html";
        $base_redirect ="http://example.com/";

        if ($http_code == 301 || $http_code == 302)
        {
            $matches = array();
            $pregs = eregi($redirect_page, $data, $matches);
            $new_url = $base_redirect . $matches[0];
            if (!$new_url)
            {
                //couldn't process the url to redirect to
                $curl_loops = 0;
                return $data;
            }
            curl_setopt($ch, CURLOPT_URL, $new_url);

            return curl_redir_exec($ch, $postVars);
        }
        else
        {
            $curl_loops=0;
            return $data;
        }
    }

    请注意:

    此处包含代码的所有答案均手动解析curl请求中的标头,以找到Location:标头。

    但是,从PHP 5.3.7开始,有一个选项CURLINFO_REDIRECT_URL可与curl_getinfo()一起使用。无需重复执行两次请求,如果您不需要标题,则无需启用标头,也不需要正则表达式。


    从未在实际环境中进行过测试,但是curl_exec具有更高的透明度(标头和returntransfer选项没有问题)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    function curl_exec_follow(/*resource*/ $ch, /*int*/ $maxredirect = 5) {
        if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off')) {
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        } else {
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
            $newurl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);

            $rch = curl_copy_handle($ch);
            curl_setopt($rch, CURLOPT_HEADER, true);
            curl_setopt($rch, CURLOPT_NOBODY, true);
            curl_setopt($rch, CURLOPT_RETURNTRANSFER, true);
            do {
                curl_setopt($rch, CURLOPT_URL, $newurl);
                $header = curl_exec($rch);
                if (curl_errno($rch)) {
                    $code = 0;
                } else {
                    $code = curl_getinfo($rch, CURLINFO_HTTP_CODE);
                    if ($code == 301 || $code == 302) {
                        preg_match('/Location:(.*?)
    /'
    , $header, $matches);
                        $newurl = trim(array_pop($matches));
                    } else {
                        $code = 0;
                    }
                }
            } while ($code && $maxredirect--);
            curl_close($rch);
            curl_setopt($ch, CURLOPT_URL, $newurl);
        }
        return curl_exec($ch);
    }


    如果已经配置了$curl实例,并且只想在启用FOLLOWLOCATION的情况下模拟curl_exec,则可以使用以下示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    function curl_follow_exec($curl, $url = null)
    {
        curl_setopt($curl, CURLOPT_HEADER, true);
        if (!is_null($url))
        {
            $opts = array (
                CURLOPT_URL => $url,
                CURLOPT_POST => false,
                CURLOPT_PUT => false,
            );
            curl_setopt_array($curl, $opts);
        }
        $data = curl_exec($curl);
        $status = curl_getinfo($curl);
        $arr = explode("



    "
    , $data);
        while (strpos(reset($arr), 'HTTP/1.1 100 Continue') !== false)
        {
            array_shift($arr);
        }
        $header = $arr[0];
        $body = implode("

    "
    , array_slice($arr, 1));
        if ($status['http_code'] == 301 || $status['http_code'] == 302)
        {
            $matches = array ();
            preg_match("/(Location:|URI:)[^(
    )]*/"
    , $header, $matches);
            $url = trim(str_replace($matches[1],"", $matches[0]));
            return curl_follow_exec($curl, $url);
        }
        return $body;
    }

    注意:如果您已经指定了选项,则在调用此函数时不要提供URL,它仅用于递归目的。

    我从可接受的答案中启发了这段代码,并添加了一些东西来管理多个标题。

    此功能就像是针对ie6的丑陋骇客:如果可以,请更改您的托管:-)。