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.
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.
Beaucoup des autres solutions proposées par ce fil sont not faites-le correctement.
\r\n\r\n
_ n'est pas fiable si _CURLOPT_FOLLOWLOCATION
_ est activé ou lorsque le serveur répond avec un code 100.\n
_ pour les nouvelles lignes.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);
_
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'))
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);
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)
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);
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);
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;
}
}
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.
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.