web-dev-qa-db-fra.com

PHP cURL peut-il extraire les en-têtes de réponse ET le corps en une seule requête?

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

curl_setopt($ch, CURLOPT_HEADER, true);

va retourner le corps plus en-têtes, mais ensuite, je dois l’analyser pour obtenir le corps. Existe-t-il un moyen d'obtenir les deux de manière plus utilisable (et sécurisée)?

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

295
gremo

Une solution à cela a été publiée dans les PHP commentaires de la documentation: 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, ceci peut ne pas être fiable s'il est utilisé avec des serveurs proxy ou lors du traitement de certains types de redirections. La réponse de @Geoffrey pourrait traiter ces problèmes de manière plus fiable.

439
iblue

Beaucoup des autres solutions proposées par ce fil sont not ​​faites-le correctement.

  • Le fractionnement sur _\r\n\r\n_ n'est pas fiable si _CURLOPT_FOLLOWLOCATION_ est activé ou lorsque le serveur répond avec un code 100.
  • Tous les serveurs ne sont pas conformes aux normes et transmettent simplement un _\n_ pour les nouvelles lignes.
  • La détection de la taille des en-têtes via _CURLINFO_HEADER_SIZE_ n'est également pas toujours fiable, en particulier lorsque des mandataires sont utilisés ou dans certains des mêmes scénarios de redirection.

La méthode la plus correcte utilise CURLOPT_HEADERFUNCTION .

Voici une méthode très propre pour effectuer cela en utilisant PHP fermetures. Il convertit également tous les en-têtes en minuscules pour une gestion cohérente sur 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 de 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;

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

    return $len;
  }
);

$data = curl_exec($ch);
print_r($headers);
_
151
Geoffrey

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 (la fonction sera donc appelée pour chaque ligne d'en-tête, en partant du 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, avec différents protocoles et procurations, et vous n’avez pas à vous soucier de la taille de l’en-tête ni à définir de nombreuses options de bouclage.

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

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

est-ce que vous cherchez?

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

Il suffit de définir les options:

  • CURLOPT_HEADER, 0

  • CURLOPT_RETURNTRANSFER, 1

et utilisez curl_getinfo avec CURLINFO_HTTP_CODE (ou no opt param et vous aurez un tableau associatif avec toutes les informations souhaitées)

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

10
Cyril H.

Si vous voulez spécifiquement le Content-Type, il existe une option spéciale cURL 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);
7
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);

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 envoient uniquement LF au lieu de CRLF sous forme de 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
Enyby

Voici ma contribution au débat ... Ceci renvoie un tableau unique avec les données séparées et les en-têtes répertoriés. Cela fonctionne sur la base que CURL renverra une donnée d'en-tête de bloc [ligne vide]

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

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.

1
Roy

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

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

Ensuite, pour obtenir (de manière fiable) uniquement le corps de la demande, vous devez transmettre la valeur de l'en-tête Content-Length à substr() en tant que valeur de départ négative.

0
mal