завиток следит за ошибкой местоположения

Я получил сообщение об ошибке:

Функция CURLOPT_FOLLOWLOCATION не может быть активирована, если в safe_mode установлен параметр open_basedir.

safe_mode отключен на моем веб-хостинге.

open_basedir is "".

Как это разрешить?

Обходной путь заключается в реализации перенаправления в PHP-коде.

Вот моя собственная реализация. Он имеет два известных ограничения:

  1. Это заставит CURLOPT_RETURNTRANSFER
  2. Это несовместимо с 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.

Я столкнулся с подобной ситуацией некоторое время назад и нашел решение ниже. Если вы знаете, как правило, где вы будете перенаправлены на это, это может сработать для вас.

  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; } } 

Просто примечание:

Все ответы с кодом здесь вручную анализируют заголовки из запроса curl, чтобы найти заголовок Location:

Однако, начиная с PHP 5.3.7, существует опция CURLINFO_REDIRECT_URL вы можете использовать с curl_getinfo() . Не нужно делать запросы дважды, нет необходимости включать заголовки, если вы не хотите их, нет необходимости в регулярных выражениях.

Никогда не тестировался в реальной среде, но имеет большую прозрачность с 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); } 

Если у вас уже настроен экземпляр $curl уже настроен и просто хотите имитировать curl_exec с включенным FOLLOWLOCATION, вы можете использовать его:

 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-адрес при вызове этой функции, если вы уже указали этот параметр, он используется только для рекурсивных целей.

Я вдохновил этот код на принятый ответ и добавил некоторые материалы для управления несколькими заголовками.

Эта функция подобна уродливому взлому для ie6: если вы можете, измените свой хостинг :-).