curl跟随位置错误
我得到这个错误信息:
CURLOPT_FOLLOWLOCATION在safe_mode或者open_basedir被设置时不能被激活。
我的虚拟主机上closuressafe_mode。
open_basedir是“”。
我如何解决这个问题?
解决方法是在PHP代码中实现redirect。
这是我自己的实现。 它有两个已知的限制:
- 这将强制
CURLOPT_RETURNTRANSFER
- 它与
CURLOPT_HEADERFUNCTION
不兼容
代码:
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, "\r\n")+2; $headers = substr($data, $header_start, strpos($data, "\r\n\r\n", $header_start)+2-$header_start); if (!preg_match("!\r\n(?:Location|URI): *(.*?) *\r\n!", $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, "\r\n\r\n")+4); return $data; } }
打印此警告消息的唯一地方是在ext / curl / interface.c中
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必须启用。
我后来遇到了类似的情况,find了下面的解决办法。 如果你一般知道你将被redirect到这个位置,可能适合你。
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("\n\r", $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; } }
只是一个说明:
所有代码的答案在这里手动parsingcurl请求头来find一个Location:
头。
但是,从PHP 5.3.7开始,有一个选项CURLINFO_REDIRECT_URL
可以和curl_getinfo()
。 不需要两次请求,不需要启用标题,如果你不想要他们,不需要正则expression式。
从来没有在真实环境中testing过,但curl_exec具有更多的透明度(header和returntransfer选项没有问题)。
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:(.*?)\n/', $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); }
如果你已经有了一个已经configurationcurl_exec
$curl
实例,并且只想在启用了FOLLOWLOCATION的情况下模拟curl_exec
,那么你可以使用这个:
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("\r\n\r\n", $data); while (strpos(reset($arr), 'HTTP/1.1 100 Continue') !== false) { array_shift($arr); } $header = $arr[0]; $body = implode("\r\n", array_slice($arr, 1)); if ($status['http_code'] == 301 || $status['http_code'] == 302) { $matches = array (); preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches); $url = trim(str_replace($matches[1], "", $matches[0])); return curl_follow_exec($curl, $url); } return $body; }
注意:如果你已经指定了这个选项,在调用这个函数的时候不要提供一个URL,它只是用于recursion目的。
我从接受的答案中启发了这段代码,并添加了一些东西来pipe理多个头文件。
这个function就像ie6的一个丑陋的黑客攻击:如果可以的话,改变你的主机:-)。