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.
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.
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'))
Beaucoup des autres solutions proposées à ce thread ne le font pas correctement.
- Fractionnement sur
\r\n\r\n
n'est pas fiable lorsqueCURLOPT_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);
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);
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)
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);
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);
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 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.
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;
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;
}
?>
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