web-dev-qa-db-ja.com

できる PHP cURLは単一のリクエストでレスポンスヘッダとボディを取得しますか?

PHPを使用してcURLリクエストのヘッダーとボディの両方を取得する方法はありますか?私はこのオプションを見つけました:

curl_setopt($ch, CURLOPT_HEADER, true);

ボディとヘッダ を返すつもりですが、それからボディを取得するためにそれを解析する必要があります。両方をより使いやすく(そして安全に)する方法はありますか?

"シングルリクエスト"の場合、GET/POSTの前にHEADリクエストを発行しないことを意味します。

286
gremo

これに対する一つの解決策はPHPドキュメントのコメントに投稿されています: http://www.php.net/manual/en/function.curl-exec.php#80442

コード例:

$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
// ...

$response = curl_exec($ch);

// Then, after your curl_exec call:
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($response, 0, $header_size);
$body = substr($response, $header_size);

警告: 以下のコメントにあるように、これはプロキシサーバーで使用されたり、特定の種類のリダイレクトを処理する際には信頼できないかもしれません。 @ Geoffreyの答えはこれらをもっと確実に扱うかもしれません。

429
iblue

このスレッドで提供されている他の解決策の多くはnot正しくこれを行うこと)です。

  • \r\n\r\nがオンになっているとき、またはサーバーが100コードで応答するときは、CURLOPT_FOLLOWLOCATIONで分割することは信頼できません。
  • すべてのサーバーが標準に準拠しているわけではなく、新しい行には\nだけを送信します。
  • CURLINFO_HEADER_SIZEを介してヘッダーのサイズを検出することは、特にプロキシが使用されている場合や、同じリダイレクトシナリオの中では特に信頼できるとは限りません。

最も正しい方法は CURLOPT_HEADERFUNCTION を使うことです。

これはPHPクロージャを使ってこれを実行する非常にクリーンな方法です。また、サーバー間およびHTTPバージョン間で一貫した処理を行うために、すべてのヘッダーを小文字に変換します。

このバージョンは重複したヘッダを保持します

これはRFC 822とRFC 2616に準拠しています。mb_文字列関数を利用するための編集を提案しないでください、それは誤りです!

$ch = curl_init();
$headers = [];
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

// this function is called by curl for each header received
curl_setopt($ch, CURLOPT_HEADERFUNCTION,
  function($curl, $header) use (&$headers)
  {
    $len = strlen($header);
    $header = explode(':', $header, 2);
    if (count($header) < 2) // ignore invalid headers
      return $len;

    $name = strtolower(trim($header[0]));
    if (!array_key_exists($name, $headers))
      $headers[$name] = [trim($header[1])];
    else
      $headers[$name][] = trim($header[1]);

    return $len;
  }
);

$data = curl_exec($ch);
print_r($headers);
131
Geoffrey

CurlにはCURLOPT_HEADERFUNCTIONと呼ばれるこのための組み込みオプションがあります。このオプションの値はコールバック関数の名前でなければなりません。 Curlは、このコールバック関数にヘッダー(およびヘッダーのみ!)を1行ずつ渡します(そのため、この関数はヘッダー行の先頭から各ヘッダー行に対して呼び出されます)。それからあなたのコールバック関数はそれで何でもすることができます(そして与えられた行のバイト数を返さなければなりません)。これがテスト済みの作業コードです。

function HandleHeaderLine( $curl, $header_line ) {
    echo "<br>YEAH: ".$header_line; // or do whatever
    return strlen($header_line);
}


$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://www.google.com");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADERFUNCTION, "HandleHeaderLine");
$body = curl_exec($ch); 

上記はすべて、プロトコルやプロキシが異なる場合でも動作します。ヘッダサイズを気にしたり、さまざまなカールオプションを設定したりする必要はありません。

P.S .:オブジェクトメソッドでヘッダ行を処理するには、次のようにします。

curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(&$object, 'methodName'))
111
Skacc

これはあなたが探しているものですか?

curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
$response = curl_exec($ch); 
list($header, $body) = explode("\r\n\r\n", $response, 2);
40
user1031143

オプションを設定するだけです。

  • CURLOPT_HEADER、0

  • CURLOPT_RETURNTRANSFER、1

そしてcurl_getinfoをCURLINFO_HTTP_CODEと一緒に使用してください(またはopt paramを指定しなければ、必要なすべての情報を含む連想配列が得られます)

さらに詳しく: http://php.net/manual/fr/function.curl-getinfo.php

10
Cyril H.

特にContent-Typeが必要な場合は、それを取得するための特別なcURLオプションがあります。

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
$content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
8
pr1001
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);

$parts = explode("\r\n\r\nHTTP/", $response);
$parts = (count($parts) > 1 ? 'HTTP/' : '').array_pop($parts);
list($headers, $body) = explode("\r\n\r\n", $parts, 2);

他のヘッダーより前にHTTP/1.1 100 Continueを処理します。

改行としてCRLFの代わりにLFのみを送信するバグのあるサーバーで作業する必要がある場合は、次のようにpreg_splitを使用できます。

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);

$parts = preg_split("@\r?\n\r?\nHTTP/@u", $response);
$parts = (count($parts) > 1 ? 'HTTP/' : '').array_pop($parts);
list($headers, $body) = preg_split("@\r?\n\r?\n@u", $parts, 2);
2
Enyby

これが私の議論への貢献です...これは分離されたデータとリストされたヘッダを持つ単一の配列を返します。これはCURLがヘッダチャンク[空白行]データを返すことに基づいて機能します。

curl_setopt($ch, CURLOPT_HEADER, 1); // we need this to get headers back
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_VERBOSE, true);

// $output contains the output string
$output = curl_exec($ch);

$lines = explode("\n",$output);

$out = array();
$headers = true;

foreach ($lines as $l){
    $l = trim($l);

    if ($headers && !empty($l)){
        if (strpos($l,'HTTP') !== false){
            $p = explode(' ',$l);
            $out['Headers']['Status'] = trim($p[1]);
        } else {
            $p = explode(':',$l);
            $out['Headers'][$p[0]] = trim($p[1]);
        }
    } elseif (!empty($l)) {
        $out['Data'] = $l;
    }

    if (empty($l)){
        $headers = false;
    }
}
1
Antony

私のやり方は

$response = curl_exec($ch);
$x = explode("\r\n\r\n", $v, 3);
$header=http_parse_headers($x[0]);
if ($header=['Response Code']==100){ //use the other "header"
    $header=http_parse_headers($x[1]);
    $body=$x[2];
}else{
    $body=$x[1];
}

必要に応じてforループを適用して爆発限界を取り除きます。

1
Roy

ここでの多くの答えの問題は、"\r\n\r\n"が正当にhtmlの本体に現れることがあるということです、それであなたは正しくあなたがヘッダーを分割していることを確信できません。

curl_execの1回の呼び出しでヘッダーを別々に保存する唯一の方法は、 https://stackoverflow.com/a/25118032/3326494 で提案されているように、コールバックを使用することです。

そして(確実に)リクエストの本文だけを取得するには、Content-Lengthヘッダーの値を負の開始値としてsubstr()に渡す必要があります。

0
mal