PHP cURL이 단일 요청으로 응답 헤더와 본문을 검색 할 수 있습니까?


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.21 기가 와트

답변:


466

이에 대한 한 가지 해결책은 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)만 작동 변형으로
— msangel

5
@msangel 서버가 302 리디렉션을 수행하는 경우와 같이 응답에 여러 헤더가 있으면 솔루션이 작동하지 않습니다. 어떤 제안?
— Nate

4
@Nate, 예, 알고 있습니다. AFAIK이지만 가능한 추가 헤더는 코드와 함께 만 있습니다 100(계속). 이 헤더의 경우 request 옵션을 올바르게 정의 curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:')); 하여이 헤더 응답 전송을 비활성화 할 수 있습니다 . 에 관해서는 302,이 그러나 난 서버가 일부 신체를 보내 때때로 알고, 302 헤더 리디렉션을하기 때문에, 그것은 몸을 기대하지 않고, 일이되어서는 안 302응답 있지만 어쨌든 브라우저에 의해 무시됩니다, 지금까지 컬이 왜 처리해야합니까? )
— msangel

5
CURLOPT_VERBOSE프로세스 정보를 STDERR(CLI에서 귀찮게 할 수 있음) 출력하기위한 것이며 논의 된 문제는 쓸모가 없습니다.
— hejdav

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 의 설명서를 읽고 작동 방식 및 잠재적 문제점을 이해하는 것이 가장 좋습니다. 또한 다른 사람들을 돕기 위해 답변을 약간 개선했습니다.
— Simon East

복제본 헤더에 대한 답변을 업데이트했습니다. 앞으로 코드를 원하는대로 다시 포맷하지 마십시오. 이것은 클로저 함수 경계가 어디에 있는지 명확하게하기 위해 작성되었습니다.
— Geoffrey

@Geoffrey $headers = [];유효한 PHP입니까?
— thealexbaron

6
@thealexbaron 예 PHP 5.4부터입니다 : php.net/manual/en/migration54.new-features.php
— Geoffrey

4
이 답변은 깔끔하고 RFC 호환 방식에 대해 과소 평가되었습니다. 이것은 끈적 끈적한 대답을하고 상단으로 이동해야합니다. 모든 헤더를 먼저 파싱하는 대신 원하는 헤더 값을 얻는 더 빠른 방법이 있었으면 좋겠습니다.
— Fr0zenFyr

114

Curl에는 CURLOPT_HEADERFUNCTION이라는 옵션이 내장되어 있습니다. 이 옵션의 값은 콜백 함수의 이름이어야합니다. Curl은 헤더 (및 헤더 만!)를이 콜백 함수에 한 줄씩 전달합니다 (따라서 헤더 섹션의 맨 위에서 시작하여 각 헤더 행에 대해 함수가 호출됩니다). 콜백 함수는 그와 함께 무엇이든 할 수 있습니다 (그리고 주어진 줄의 바이트 수를 반환해야합니다). 테스트 된 작업 코드는 다음과 같습니다.

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

위의 내용은 모든 프로토콜과 프록시에서도 작동하며 헤더 크기에 대해 걱정하거나 다른 컬 옵션을 많이 설정할 필요가 없습니다.

추신 : 객체 메소드로 헤더 라인을 처리하려면 다음을 수행하십시오.

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

참고로 콜백 함수는 각 헤더마다 호출되며 잘리지 않는 것 같습니다. 전역 변수를 사용하여 모든 헤더를 보유하거나 콜백에 익명 함수를 사용하고 로컬 변수 (익명 함수가 아닌 상위 범위에 대한 로컬)를 사용할 수 있습니다.
— MV.

2
@MV 감사합니다. "line-by-line"으로 "각 헤더"를 의미했습니다. 명확성을 위해 답변을 편집했습니다. 전체 헤더 섹션 (일명 모든 헤더)을 가져 오려면 콜백에 객체 메소드를 사용하여 객체 속성이 모든 헤더를 보유 할 수 있습니다.
— Skacc

8
이것이 최고의 답변 IMO입니다. CURLOPT_FOLLOWLOCATION을 사용할 때 여러 "\ r \ n \ r \ n"에 문제가 발생하지 않으며 프록시의 추가 헤더에 영향을받지 않습니다.
— Rafał G.

나를 위해 아주 잘 일했다. stackoverflow.com/questions/6482068/… 문제가있는 경우
— RHH

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 Continue가 있고 break가 HTTP / 1.1 200 OK 인 경우를 제외하고는 정상적으로 작동합니다. 다른 방법으로 갈 것입니다.
— ghostfly

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.
— 알릭


curl이 위치 헤더를 따르도록 설정된 경우이 방법은 302 리디렉션에서도 실패합니다.
— Simon East

10

옵션을 설정하십시오.

  • CURLOPT_HEADER, 0

  • CURLOPT_RETURNTRANSFER, 1

CURLINFO_HTTP_CODE와 함께 curl_getinfo를 사용하십시오 (또는 opt 매개 변수가 없으면 원하는 모든 정보가 포함 된 연관 배열이 있습니다)

자세한 내용은 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는 특정 헤더가 아닌 헤더를 검색하는 방법이 있는지 물었지만 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);2로 폭발 3 번째 매개 변수가?
— 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으로 분할해야합니다.
— Geoffrey

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이 헤더 청크 [blank line] 데이터를 반환한다는 사실에 근거하여 작동합니다.

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에 제안 된대로 콜백을 사용하는 것 같습니다.

그런 다음 요청 본문을 (신뢰하게) 얻으려면 Content-Length헤더 값을 substr()음의 시작 값 으로 전달해야 합니다.


1
합법적으로 나타날 수 있지만 답변이 잘못되었습니다. Content-Length는 HTTP 응답에 없어도됩니다. 헤더를 수동으로 구문 분석하는 올바른 방법은 \ r \ n (또는 \ n \ n)의 첫 번째 인스턴스를 찾는 것입니다. :이 두 요소, 즉 돌아 폭발 제한하여 간단하게 할 수있는 list($head, $body) = explode("\r\n\r\n", $response, 2);당신이 사용하는 경우, 그러나 CURL 이미 당신을 위해이 작업을 수행curl_setopt($ch, CURLOPT_HEADERFUNCTION, $myFunction);
— 제프리

-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, 세 번째 매개 변수는 "HTTP / 1.1 100 Continue \ r \ n \ r \ nHTTP / 1.1 200 OK ... \ r \ n \ r \ n ..."헤더를 처리하는 것입니다.
— diyism

그러나 그는 다른 말을했습니다 : stackoverflow.com/questions/9183178/… 당신 중 어느 쪽이 맞습니까?
— user4271704

@ user4271704 참조하는 링크도 사용하십시오. explode("\r\n\r\n", $parts, 2); 그래서 둘 다 맞습니다.
— Cyborg

-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.