PHP cURL peut-il récupérer les en-têtes et le corps de la réponse en une seule requête?

Existe-t-il un moyen d'obtenir à la fois les en-têtes et le corps pour une requête cURL en utilisant PHP? J'ai trouvé que cette option:

curl_setopt($ch, CURLOPT_HEADER, true);

Va retourner le body plus les en-têtes, mais ensuite j'ai besoin de l'analyser pour obtenir le corps. Existe-t-il un moyen d'obtenir les deux d'une manière plus utilisable (et sécurisée)?

Notez que pour "requête unique", je veux dire éviter d'émettre une requête HEAD avant GET / POST.

256
demandé sur halfer 2012-02-08 00:13:03

12 réponses

Une solution à cela a été publiée dans la documentation PHP Commentaires: http://www.php.net/manual/en/function.curl-exec.php#80442

Exemple de Code:

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

Avertissement: comme indiqué dans les commentaires ci-dessous, cela peut ne pas être fiable lorsqu'il est utilisé avec des serveurs proxy ou lors de la gestion de certains types de redirections. La réponse de @ Geoffrey peut les gérer de manière plus fiable.

398
répondu iblue 2017-06-09 06:49:16

Curl a une option intégrée pour cela, appelée CURLOPT_HEADERFUNCTION. La valeur de cette option doit être le nom d'une fonction de rappel. Curl passera l'en-tête (et l'en-tête seulement!) à cette fonction de rappel, ligne par ligne (donc la fonction est appelée pour chaque ligne d'en-tête, en commençant par le haut de la section d'en-tête). Votre fonction de rappel peut alors faire n'importe quoi avec elle (et doit renvoyer le nombre d'octets de la ligne donnée). Voici un code de travail testé:

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

Ce qui précède fonctionne avec tout, différents protocoles et proxies aussi, et vous n'avez pas besoin de vous soucier de la taille de l'en-tête, ou de définir beaucoup d'options de curl différentes.

P.S.: pour gérer les lignes d'en-tête avec une méthode d'objet, procédez comme suit:

curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(&$object, 'methodName'))
106
répondu Skacc 2015-04-26 12:01:44

Beaucoup des autres solutions proposées à ce thread ne le font pas correctement.

  • Fractionnement sur \r\n\r\n n'est pas fiable lorsque CURLOPT_FOLLOWLOCATION est en marche ou lorsque le serveur répond avec un 100 code.
  • Tous les serveurs ne sont pas conformes aux normes et ne transmettent qu'un \n pour les nouvelles lignes.
  • la détection de la taille des en-têtes via CURLINFO_HEADER_SIZE n'est pas toujours fiable, en particulier lorsque des proxies sont utilisés ou dans certains des mêmes scénarios de redirection.

Le la méthode la plus correcte utilise CURLOPT_HEADERFUNCTION.

Voici une méthode très propre pour effectuer cela en utilisant des fermetures PHP. Il convertit également tous les en-têtes en minuscules pour une gestion cohérente entre les serveurs et les versions HTTP.

Cette version conservera les en-têtes dupliqués

Ceci est conforme aux RFC822 et RFC2616, veuillez ne pas suggérer des modifications pour utiliser les fonctions de chaîne mb_, c'est incorrect!

$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
répondu Geoffrey 2017-08-13 03:43:14

Est-ce à quoi vous cherchez?

curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
$response = curl_exec($ch); 
list($header, $body) = explode("\r\n\r\n", $response, 2);
39
répondu user1031143 2013-06-30 18:45:32

Il suffit de définir les options:

  • CURLOPT_HEADER, 0

  • CURLOPT_RETURNTRANSFER, 1

Et utilisez curl_getinfo avec CURLINFO_HTTP_CODE (ou pas opt param et vous aurez un tableau associatif avec toutes les informations que vous voulez)

Plus à: http://php.net/manual/fr/function.curl-getinfo.php

10
répondu Cyril H. 2014-06-20 11:03:47

Si vous voulez spécifiquement le Content-Type, Il y a une option cURL spéciale pour le récupérer:

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
$content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
8
répondu pr1001 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);

Fonctionne avec HTTP/1.1 100 Continue avant les autres en-têtes.

Si vous avez besoin de travailler avec des serveurs bogués qui n'envoient que LF au lieu de CRLF comme sauts de ligne, vous pouvez utiliser preg_split comme suit:

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
répondu Enyby 2017-06-11 03:38:24

Mon chemin est

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

Si nécessaire, appliquez une boucle for et supprimez la limite d'explosion.

0
répondu Roy 2015-12-22 19:28:20

Le problème avec beaucoup de réponses ici est que "\r\n\r\n" peut légitimement apparaître dans le corps du html, donc vous ne pouvez pas être sûr que vous divisez correctement les en-têtes.

Il semble que la seule façon de stocker les en-têtes séparément avec un appel à curl_exec est d'utiliser un rappel comme suggéré ci-dessus dans https://stackoverflow.com/a/25118032/3326494

Et puis pour (de manière fiable) obtenir juste le corps de la requête, vous devez passer la valeur de l'en-tête Content-Length à substr() en tant que valeur de départ négative.

0
répondu mal 2017-05-23 11:55:10

Soyez prudent lorsque vous avez besoin des derniers éléments retournés par le serveur. Ce code peut briser votre attente en attendant de vrais (derniers) en-têtes et corps: list($headers, $body) = explode("\r\n\r\n", $result, 2);

Voici un moyen simple d'obtenir les en-têtes finaux et les parties du corps;

$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
répondu K-Gun 2017-08-04 11:36:13

Retourne les en-têtes de réponse avec un paramètre de référence:

<?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
répondu diyism 2014-11-12 11:32:43

Si vous n'avez pas vraiment besoin d'utiliser curl;

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

Qui génère

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

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

-5
répondu Bevan 2015-02-24 20:39:47