PHP cURLは、1つのリクエストで応答ヘッダーと本文を取得できますか?


314

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

curl_setopt($ch, CURLOPT_HEADER, true);

を返すつもりです 本文とヘッダーますが、解析して本文を取得する必要があります。両方をより使いやすい(そして安全な)方法で取得する方法はありますか?

「単一リクエスト」の場合、GET / POSTの前にHEADリクエストを発行しないようにすることを意味することに注意してください。


3
このための組み込みのソリューションがあります。この回答を参照してください:stackoverflow.com/a/25118032/1334485(このコメントを追加 'cozこの投稿にはまだ多くのビューがあります)
Skacc

この素敵なコメントを見てください:secure.php.net/manual/en/book.curl.php#117138
user956584


私の質問はこの質問と重複していると言われました。重複していない場合、誰かがもう一度開いてください。stackoverflow.com/questions/43770246/…私の質問では、1つの文字列ではなく、ヘッダーと本文が別々のオブジェクトを返すメソッドを使用するという具体的な要件があります。
1.21ギガワット2017年

回答:


466

これに対する1つの解決策は、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の回答は、これらをより確実に処理する可能性があります。


22
することもできlist($header, $body) = explode("\r\n\r\n", $response, 2)ますが、リクエストのサイズによっては少し時間がかかる場合があります。
iblue

43
プロキシサーバーとプロキシサーバー(フィドラーなど)を使用する場合、応答に独自のヘッダーを追加するため、これは悪い解決策です。このヘッダーはすべてのオフセットを壊し、list($header, $body) = explode("\r\n\r\n", $response, 2)
ms

5
@msangelサーバーが302リダイレクトを行う場合など、応答に複数のヘッダーがある場合、ソリューションは機能しません。助言がありますか?
2014

4
@ネイト、はい、私はこれを知っています。申し訳ありませんが、可能なヘッダーは1つしかありません-コード付き100(続き)。このヘッダーについては、リクエストオプションを正しく定義して回避できますcurl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:')); 。このヘッダー応答の送信を無効にします。については302、これは起こらないはずです.302ヘッダーはリダイレクトであるため、本文を予期していませんが、サーバーが302応答付きの本文を送信することがありますが、ブラウザーによって無視されますが、これまでのところ、curlがこれを処理する必要があるのはなぜですか? )
msangel 2014

5
CURLOPT_VERBOSEプロセス情報をSTDERR(CLIでわざわざ)に出力することを目的としており、議論された問題には役に立たない。
hejdav 2015

205

このスレッドを提供する他のソリューションの多くは、これを正しく行っていません。

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

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

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

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

これはRFC822とRFC2616に準拠してい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;

    $headers[strtolower(trim($header[0]))][] = trim($header[1]);

    return $len;
  }
);

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

12
IMOこれはこのスレッドの最良の回答であり、他の回答で発生したリダイレクトに関する問題を修正します。CURLOPT_HEADERFUNCTIONのドキュメントを読んで、それがどのように機能し、潜在的な問題があるかを理解するのが最善です。また、他の人を助けるために、回答にいくつかの改善を加えました。
サイモンイースト

ヘッダーの重複に対応するため、回答を更新しました。今後は、コードを本来あるべきものに再フォーマットしないでください。これは、クロージャ関数の境界がどこにあるかを明確にする方法で書かれています。
ジェフリー2017年

@Geoffreyは$headers = [];有効なphpですか?
thealexbaron 2017

6
@thealexbaronはい、それはPHP 5.4以降です。次を参照してください。php.net
Geoffrey

4
この回答は、このようなきちんとしたRFC準拠のアプローチに対しては非常に過小評価されています。これは粘着性のある答えにして、一番上に移動する必要があります。最初にすべてのヘッダーを解析するのではなく、目的のヘッダーの値を取得するためのより高速なアプローチがあったらいいのにと思います。
Fr0zenFyr 2018年

114

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

上記はすべて、さまざまなプロトコルとプロキシでも機能し、ヘッダーサイズを心配したり、さまざまなカールオプションをたくさん設定したりする必要はありません。

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

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

注意として、コールバック関数はヘッダーごとに呼び出され、トリミングされていないようです。グローバル変数を使用してすべてのヘッダーを保持するか、コールバックに無名関数を使用してローカル変数(無名関数ではなく親スコープにローカル)を使用できます。
MV。

2
@MVありがとう、はい。「行ごと」とは「各ヘッダー」を意味しました。わかりやすくするために回答を編集しました。ヘッダーセクション全体(別名:すべてのヘッダー)を取得するには、コールバックにオブジェクトメソッドを使用して、オブジェクトプロパティがそれらすべてを保持できるようにすることもできます。
Skacc

8
これがIMOの最良の回答です。CURLOPT_FOLLOWLOCATIONを使用する場合、複数の「\ r \ n \ r \ n」の問題は発生せず、プロキシからの追加のヘッダーの影響も受けないと思います。
ラファウG.

私にとって非常にうまく機能しました。問題が発生した場合は、stackoverflow.com
questions / 6482068 /…

1
はい、これが最善のアプローチですが、@ Geoffreyの答えは、グローバル変数などを必要としない無名関数を使用することにより、これをよりクリーンにします。
Simon East

39

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

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

8
これは、HTTP / 1.1 100続行の後にブレイクがあり、HTTP / 1.1 200 OKがある場合を除いて、正常に機能します。私は他の方法で行きます。
ghostfly 2013年

1
このようなものを実装する前に、選択したstackoverflow.com/questions/14459704/…の回答を確認してください。 w3.org/Protocols/rfc2616/rfc2616-sec14.html(14.20) A server that does not understand or is unable to comply with any of the expectation values in the Expect field of a request MUST respond with appropriate error status. The server MUST respond with a 417 (Expectation Failed) status if any of the expectations cannot be met or, if there are other problems with the request, some other 4xx status.
Alrik


このメソッドは、curlがロケーションヘッダーに従うように設定されている場合、302リダイレクトでも失敗します。
Simon East

10

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

  • CURLOPT_HEADER、0

  • CURLOPT_RETURNTRANSFER、1

CURLINFO_HTTP_CODEを指定してcurl_getinfoを使用します(またはopt paramを使用しないと、必要なすべての情報を含む連想配列になります)

詳細:http : //php.net/manual/fr/function.curl-getinfo.php


5
これは応答ヘッダーをまったく返さないようです。または、少なくともを使用してそれらを取得する方法はありませんcurl_getinfo()
Simon East

8

特にが必要な場合は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);

OPは、特定の1つのヘッダーではなく、ヘッダーを取得する方法があるかどうか尋ねましたが、これはOPの質問には答えません。
Geoffrey

2
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);

$parts = explode("\r\n\r\nHTTP/", $response);爆発の3番目のパラメーターを2にすべきではありませんか?
user4271704

@ user4271704いいえ。最後のHTTPメッセージを検索できます。HTTP/1.1 100 Continue何度も現れることができます。
Enyby

しかし、彼は別のことを言っています:stackoverflow.com/questions/9183178/…どちらが正しいですか?
user4271704

HTTP/1.1 100 Continue何度も現れることができます。彼は、それが一度だけ現れるならケースを見るが、一般的なケースではそれは間違っている。たとえば、HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK...\r\n\r\n...彼のコードが適切に機能しない
Enyby

1
\ r \ nでの分割は信頼できません。一部のサーバーはHTTP仕様に準拠せず、\ nのみを送信します。RFC標準では、アプリケーションは最大の信頼性を得るために\ rを無視して\ nで分割する必要があると述べています。
ジェフリー2017年

1

私のやり方は

$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

これが議論への私の貢献です...これは、データが分離され、ヘッダーがリストされた単一の配列を返します。これは、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;
    }
}

0

ここでの多くの回答の問題"\r\n\r\n"は、HTMLの本文に正当に表示される可能性があるため、ヘッダーを正しく分割していることを確認できないことです。

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

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


1
正当に表示される可能性がありますが、答えは正しくありません。Content-Lengthは、HTTP応答に存在する必要はありません。ヘッダーを手動で解析する正しい方法は、\ r \ n(または\ n \ n)の最初のインスタンスを探すことです。これはlist($head, $body) = explode("\r\n\r\n", $response, 2);、explodeを2つの要素のみを返すように制限することで簡単に実行できます。つまり、を使用する場合、CURLはすでにこれを実行していますcurl_setopt($ch, CURLOPT_HEADERFUNCTION, $myFunction);
Geoffrey

-1

万が一CURLOPT_HEADERFUNCTION、他のソリューションを使用できない/使用しない場合。

$nextCheck = function($body) {
    return ($body && strpos($body, 'HTTP/') === 0);
};

[$headers, $body] = explode("\r\n\r\n", $result, 2);
if ($nextCheck($body)) {
    do {
        [$headers, $body] = explode("\r\n\r\n", $body, 2);
    } while ($nextCheck($body));
}

-2

参照パラメーター付きの応答ヘッダーを返します。

<?php
$data=array('device_token'=>'5641c5b10751c49c07ceb4',
            'content'=>'测试测试test'
           );
$rtn=curl_to_host('POST', 'http://test.com/send_by_device_token', array(), $data, $resp_headers);
echo $rtn;
var_export($resp_headers);

function curl_to_host($method, $url, $headers, $data, &$resp_headers)
         {$ch=curl_init($url);
          curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $GLOBALS['POST_TO_HOST.LINE_TIMEOUT']?$GLOBALS['POST_TO_HOST.LINE_TIMEOUT']:5);
          curl_setopt($ch, CURLOPT_TIMEOUT, $GLOBALS['POST_TO_HOST.TOTAL_TIMEOUT']?$GLOBALS['POST_TO_HOST.TOTAL_TIMEOUT']:20);
          curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
          curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
          curl_setopt($ch, CURLOPT_HEADER, 1);

          if ($method=='POST')
             {curl_setopt($ch, CURLOPT_POST, true);
              curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
             }
          foreach ($headers as $k=>$v)
                  {$headers[$k]=str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $k)))).': '.$v;
                  }
          curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
          $rtn=curl_exec($ch);
          curl_close($ch);

          $rtn=explode("\r\n\r\nHTTP/", $rtn, 2);    //to deal with "HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK...\r\n\r\n..." header
          $rtn=(count($rtn)>1 ? 'HTTP/' : '').array_pop($rtn);
          list($str_resp_headers, $rtn)=explode("\r\n\r\n", $rtn, 2);

          $str_resp_headers=explode("\r\n", $str_resp_headers);
          array_shift($str_resp_headers);    //get rid of "HTTP/1.1 200 OK"
          $resp_headers=array();
          foreach ($str_resp_headers as $k=>$v)
                  {$v=explode(': ', $v, 2);
                   $resp_headers[$v[0]]=$v[1];
                  }

          return $rtn;
         }
?>

本当に$rtn=explode("\r\n\r\nHTTP/", $rtn, 2);正しいですか?explodeの3番目のパラメーターを削除すべきではありませんか?
user4271704

@ user4271704、3番目のパラメータは「HTTP / 1.1 100 Continue \ r \ n \ r \ nHTTP / 1.1 200 OK ... \ r \ n \ r \ n ...」ヘッダーを
扱うこと

しかし、彼は別のことを言いました:stackoverflow.com/questions/9183178/…どちらが正しいですか?
user4271704

@ user4271704あなたが参照しているリンクも使用するexplode("\r\n\r\n", $parts, 2); ので、両方が正しいです。
サイボーグ

-5

curlを使用する必要がない場合。

$body = file_get_contents('http://example.com');
var_export($http_response_header);
var_export($body);

どの出力

array (
  0 => 'HTTP/1.0 200 OK',
  1 => 'Accept-Ranges: bytes',
  2 => 'Cache-Control: max-age=604800',
  3 => 'Content-Type: text/html',
  4 => 'Date: Tue, 24 Feb 2015 20:37:13 GMT',
  5 => 'Etag: "359670651"',
  6 => 'Expires: Tue, 03 Mar 2015 20:37:13 GMT',
  7 => 'Last-Modified: Fri, 09 Aug 2013 23:54:35 GMT',
  8 => 'Server: ECS (cpm/F9D5)',
  9 => 'X-Cache: HIT',
  10 => 'x-ec-custom-error: 1',
  11 => 'Content-Length: 1270',
  12 => 'Connection: close',
)'<!doctype html>
<html>
<head>
    <title>Example Domain</title>...

http://php.net/manual/en/reserved.variables.httpresponseheader.phpを参照してください


16
ええと、実際にはPHPも必要ありませんが、それはたまたま問題です...
Hans Z.
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.