Czy PHP cURL może pobierać nagłówki odpowiedzi i treść w jednym żądaniu?

Czy Jest jakiś sposób, aby uzyskać zarówno nagłówki, jak i body dla żądania cURL przy użyciu PHP? Znalazłem, że ta opcja:

curl_setopt($ch, CURLOPT_HEADER, true);

Zwróci ciało plus nagłówki , ale potem muszę je przeanalizować, aby uzyskać ciało. Czy jest jakiś sposób, aby uzyskać oba w bardziej użyteczny (i bezpieczny) sposób?

Zauważ, że dla "pojedynczego żądania" mam na myśli unikanie wystawiania HEAD request przed GET/POST.

 256
Author: halfer, 2012-02-08

12 answers

Jedno rozwiązanie na to zostało zamieszczone w dokumentacji PHP Komentarze: http://www.php.net/manual/en/function.curl-exec.php#80442

Przykład kodu:

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

Ostrzeżenie: Jak wspomniano w komentarzach poniżej, może to nie być wiarygodne, gdy jest używane z serwerami proxy lub podczas obsługi niektórych rodzajów przekierowań. @Geoffrey ' s answer may handle them more reliable.

 398
Author: iblue,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-09 06:49:16

Curl ma wbudowaną opcję o nazwie CURLOPT_HEADERFUNCTION. Wartością tej opcji musi być nazwa funkcji wywołania zwrotnego. Curl przekaże nagłówek(i tylko nagłówek!) do tej funkcji zwrotnej, linia po linii (więc funkcja będzie wywoływana dla każdej linii nagłówka, zaczynając od góry sekcji nagłówka). Funkcja wywołania zwrotnego może z nią zrobić wszystko (i musi zwrócić liczbę bajtów danej linii). Oto sprawdzony kod roboczy:

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

Powyżej działa ze wszystkim, różnymi protokołami i serwerami proxy i nie musisz się martwić o rozmiar nagłówka lub ustawić wiele różnych opcji curl.

P. S.: Aby obsłużyć linie nagłówka za pomocą metody object, wykonaj następujące czynności:

curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(&$object, 'methodName'))
 106
Author: Skacc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-26 12:01:44

Wiele innych rozwiązań oferowanych w tym wątku to Nie robi to poprawnie.

  • podział na {[1] } nie jest niezawodny, gdy {[2] } jest włączony lub gdy serwer odpowiada kodem 100.
  • nie wszystkie serwery są zgodne ze standardami i przesyłają tylko \n dla nowych linii.
  • Wykrywanie rozmiaru nagłówków przez CURLINFO_HEADER_SIZE również nie zawsze jest niezawodne, zwłaszcza gdy używane są proxy lub w niektórych scenariuszach przekierowania.

Na najbardziej poprawną metodą jest użycie CURLOPT_HEADERFUNCTION.

Oto bardzo czysta metoda wykonania tego za pomocą zamknięć PHP. Konwertuje również wszystkie nagłówki na małe litery, aby zapewnić spójną obsługę między serwerami i wersjami HTTP.

Ta wersja zachowa zduplikowane nagłówki

Jest to zgodne z RFC822 i RFC2616, proszę nie sugerować edycji, aby korzystać z funkcji mb_ string, jest to nieprawidłowe!

$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);
 86
Author: Geoffrey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-13 03:43:14

Tego szukasz?

curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
$response = curl_exec($ch); 
list($header, $body) = explode("\r\n\r\n", $response, 2);
 39
Author: user1031143,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-30 18:45:32

Wystarczy ustawić opcje:

  • CURLOPT_HEADER, 0

  • CURLOPT_RETURNTRANSFER, 1

I użyj curl_getinfo z CURLINFO_HTTP_CODE (lub bez opt param i będziesz miał tablicę asocjacyjną z wszystkimi informacjami, które chcesz)

Więcej na: http://php.net/manual/fr/function.curl-getinfo.php

 10
Author: Cyril H.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-20 11:03:47

Jeśli chcesz użyć Content-Type, istnieje specjalna opcja cURL, aby ją odzyskać:

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
$content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
 8
Author: pr1001,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-24 21:41:25
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);

Działa z HTTP/1.1 100 Continue przed innymi nagłówkami.

Jeśli potrzebujesz pracować z błędnymi serwerami, które wysyłają tylko LF zamiast CRLF jako podziały linii, możesz użyć preg_split w następujący sposób:

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
Author: Enyby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-11 03:38:24

My way is

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

W razie potrzeby zastosuj pętlę for i usuń limit wybuchu.

 0
Author: Roy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-22 19:28:20

Problem z wieloma odpowiedziami polega na tym, że "\r\n\r\n" może legalnie pojawić się w treści html, więc nie możesz być pewien, że prawidłowo dzielisz nagłówki.

Wydaje się, że jedynym sposobem, aby przechowywać nagłówki oddzielnie z jednym wywołaniem do curl_exec jest użycie wywołania zwrotnego, jak sugerowano powyżej w https://stackoverflow.com/a/25118032/3326494

A następnie, aby (niezawodnie) uzyskać tylko treść żądania, musisz przekazać wartość nagłówka Content-Length do substr() jako ujemna wartość początkowa.

 0
Author: mal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:10

Bądź ostrożny, gdy potrzebujesz ostatnich rzeczy, które wróciły z serwera. Ten kod może złamać Twoje oczekiwania podczas oczekiwania na prawdziwe (Ostatnie) nagłówki i body: list($headers, $body) = explode("\r\n\r\n", $result, 2);

Oto prosty sposób na uzyskanie końcowych nagłówków & części ciała;

$result = explode("\r\n\r\n", $result);

// drop redirect etc. headers
while (count($result) > 2) {
    array_shift($result);
}

// split headers / body parts
@ list($headers, $body) = $result;
 -1
Author: K-Gun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-04 11:36:13

Zwraca nagłówki odpowiedzi z parametrem referencyjnym:

<?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;
         }
?>
 -2
Author: diyism,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-12 11:32:43

Jeśli naprawdę nie musisz używać curl;

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

Które wyjścia

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

Zobacz http://php.net/manual/en/reserved.variables.httpresponseheader.php

 -5
Author: Bevan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-24 20:39:47