comment obtenir les cookies d'une curl php dans une variable

126

Donc, un gars d'une autre entreprise a pensé que ce serait génial si au lieu d'utiliser soap ou xml-rpc ou rest ou tout autre protocole de communication raisonnable, il intégrait simplement toute sa réponse sous forme de cookies dans l'en-tête.

Je dois retirer ces cookies comme, espérons-le, un tableau de cette réponse curl. Si je dois perdre une partie de ma vie à écrire un analyseur pour cela, je serai très malheureux.

Est-ce que quelqu'un sait comment cela peut être fait simplement, de préférence sans rien écrire dans un fichier?

Je serai très reconnaissant si quelqu'un peut m'aider avec cela.

soif93
la source

Réponses:

174
$ch = curl_init('http://www.google.com/');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// get headers too with this line
curl_setopt($ch, CURLOPT_HEADER, 1);
$result = curl_exec($ch);
// get cookie
// multi-cookie variant contributed by @Combuster in comments
preg_match_all('/^Set-Cookie:\s*([^;]*)/mi', $result, $matches);
$cookies = array();
foreach($matches[1] as $item) {
    parse_str($item, $cookie);
    $cookies = array_merge($cookies, $cookie);
}
var_dump($cookies);
TML
la source
31
Malheureusement, j'ai le sentiment que c'est la bonne réponse. Je pense que c'est ridicule que curl ne puisse pas simplement me donner un tableau mappé.
thirsty93
3
Je vais vous le donner mais le preg_match était faux. Je ne voulais pas seulement la session, mais je comprends pourquoi vous penseriez cela. Mais le génie qui a créé son système charge le cookie avec une carte de réponse entière, comme un get ou un post. Merde comme ça: Set-Cookie: price = 1 Set-Cookie: status = accept J'avais besoin d'un preg_match_all avec '/ ^ Set-Cookie: (. *?) = (. *?) $ /
Sm
7
@ thirsty93 Curl ne peut pas vous donner un tableau mappé. Mais vous montre un moyen de le sauvercurl_setopt($ch, CURLOPT_HEADERFUNCTION, 'callback_SaveHeaders');
Shiplu Mokaddim
2
Selon la structure des cookies renvoyée, la dernière ligne devra peut-être être modifiée en quelque chose comme parse_str($m[1], $cookies), ce qui remplira les cookies dans un tableau associatif dans la $cookiesvariable ....
random_user_name
7
Pour les correctifs combinés qui attrapent plus d'un cookie: preg_match_all('/^Set-Cookie:\s*([^;]*)/mi', $result, $matches); $cookies = array(); foreach($matches[1] as $item) { parse_str($item, $cookie); $cookies = array_merge($cookies, $cookie); }
Combuster
39

Bien que cette question soit assez ancienne et que la réponse acceptée soit valide, je la trouve un peu inconfortable car le contenu de la réponse HTTP (HTML, XML, JSON, binaire ou autre) se mélange avec les en-têtes.

J'ai trouvé une alternative différente. CURL fournit une option ( CURLOPT_HEADERFUNCTION) pour définir un rappel qui sera appelé pour chaque ligne d'en-tête de réponse. La fonction recevra l'objet curl et une chaîne avec la ligne d'en-tête.

Vous pouvez utiliser un code comme celui-ci (adapté de la réponse TML):

$cookies = Array();
$ch = curl_init('http://www.google.com/');
// Ask for the callback.
curl_setopt($ch, CURLOPT_HEADERFUNCTION, "curlResponseHeaderCallback");
$result = curl_exec($ch);
var_dump($cookies);

function curlResponseHeaderCallback($ch, $headerLine) {
    global $cookies;
    if (preg_match('/^Set-Cookie:\s*([^;]*)/mi', $headerLine, $cookie) == 1)
        $cookies[] = $cookie;
    return strlen($headerLine); // Needed by curl
}

Cette solution a l'inconvénient d'utiliser une variable globale, mais je suppose que ce n'est pas un problème pour les scripts courts. Et vous pouvez toujours utiliser des méthodes et des attributs statiques si curl est encapsulé dans une classe.

Googol
la source
10
Au lieu d'un global, vous pouvez utiliser une fermeture contenant une référence à $cookies. $curlResponseHeaderCallback = function ($ch, $headerLine) use (&$cookies) {alors curl_setopt($ch, CURLOPT_HEADERFUNCTION, $curlResponseHeaderCallback);.
Seph
Que se passe-t-il si vous avez tout cela dans une classe? Comment référencez-vous la fonction de classe $class->curlResponseHeaderCallback()? Ou avez-vous juste en curlResponseHeaderCallbackdehors de la classe?
Sevenearths
13

Cela le fait sans regexps, mais nécessite l' extension HTTP PECL .

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
$result = curl_exec($ch);
curl_close($ch);

$headers = http_parse_headers($result);
$cookobjs = Array();
foreach($headers AS $k => $v){
    if (strtolower($k)=="set-cookie"){
        foreach($v AS $k2 => $v2){
            $cookobjs[] = http_parse_cookie($v2);
        }
    }
}

$cookies = Array();
foreach($cookobjs AS $row){
    $cookies[] = $row->cookies;
}

$tmp = Array();
// sort k=>v format
foreach($cookies AS $v){
    foreach ($v  AS $k1 => $v1){
        $tmp[$k1]=$v1;
    }
}

$cookies = $tmp;
print_r($cookies);
Alex P
la source
2
Merci pour cela. Une solution claire et sémantique vaut la peine d'installer une extension.
Ben Jacobs
2
Ce serait la meilleure solution, si elle pecl installfonctionnait réellement. Grrr.
Robin Winslow
11

Si vous utilisez CURLOPT_COOKIE_FILE et CURLOPT_COOKIE_JAR, curl lira / écrira les cookies depuis / dans un fichier. Vous pouvez, une fois curl terminé, le lire et / ou le modifier comme vous le souhaitez.

Caisse à savon
la source
12
Je pense que le but n'est pas d'utiliser ce fichier
Nicolas Thery
3

libcurl fournit également CURLOPT_COOKIELIST qui extrait tous les cookies connus. Tout ce dont vous avez besoin est de vous assurer que la liaison PHP / CURL peut l'utiliser.

Daniel Stenberg
la source
12
Il n'est pas utilisable via l'API PHP.
Emre Yazici
1

quelqu'un ici peut le trouver utile. hhb_curl_exec2 fonctionne à peu près comme curl_exec, mais arg3 est un tableau qui sera rempli avec les en-têtes http renvoyés (index numérique), et arg4 est un tableau qui sera rempli avec les cookies renvoyés ($ cookies ["expires"] => " Ven, 06-May-2016 05:58:51 GMT "), et arg5 sera rempli avec ... des informations sur la requête brute faite par curl.

l'inconvénient est que CURLOPT_RETURNTRANSFER doit être activé, sinon l'erreur est supprimée et qu'il écrasera CURLOPT_STDERR et CURLOPT_VERBOSE, si vous les utilisiez déjà pour autre chose .. (je pourrais corriger cela plus tard)

exemple d'utilisation:

<?php
header("content-type: text/plain;charset=utf8");
$ch=curl_init();
$headers=array();
$cookies=array();
$debuginfo="";
$body="";
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
$body=hhb_curl_exec2($ch,'https://www.youtube.com/',$headers,$cookies,$debuginfo);
var_dump('$cookies:',$cookies,'$headers:',$headers,'$debuginfo:',$debuginfo,'$body:',$body);

et la fonction elle-même.

function hhb_curl_exec2($ch, $url, &$returnHeaders = array(), &$returnCookies = array(), &$verboseDebugInfo = "")
{
    $returnHeaders    = array();
    $returnCookies    = array();
    $verboseDebugInfo = "";
    if (!is_resource($ch) || get_resource_type($ch) !== 'curl') {
        throw new InvalidArgumentException('$ch must be a curl handle!');
    }
    if (!is_string($url)) {
        throw new InvalidArgumentException('$url must be a string!');
    }
    $verbosefileh = tmpfile();
    $verbosefile  = stream_get_meta_data($verbosefileh);
    $verbosefile  = $verbosefile['uri'];
    curl_setopt($ch, CURLOPT_VERBOSE, 1);
    curl_setopt($ch, CURLOPT_STDERR, $verbosefileh);
    curl_setopt($ch, CURLOPT_HEADER, 1);
    $html             = hhb_curl_exec($ch, $url);
    $verboseDebugInfo = file_get_contents($verbosefile);
    curl_setopt($ch, CURLOPT_STDERR, NULL);
    fclose($verbosefileh);
    unset($verbosefile, $verbosefileh);
    $headers       = array();
    $crlf          = "\x0d\x0a";
    $thepos        = strpos($html, $crlf . $crlf, 0);
    $headersString = substr($html, 0, $thepos);
    $headerArr     = explode($crlf, $headersString);
    $returnHeaders = $headerArr;
    unset($headersString, $headerArr);
    $htmlBody = substr($html, $thepos + 4); //should work on utf8/ascii headers... utf32? not so sure..
    unset($html);
    //I REALLY HOPE THERE EXIST A BETTER WAY TO GET COOKIES.. good grief this looks ugly..
    //at least it's tested and seems to work perfectly...
    $grabCookieName = function($str)
    {
        $ret = "";
        $i   = 0;
        for ($i = 0; $i < strlen($str); ++$i) {
            if ($str[$i] === ' ') {
                continue;
            }
            if ($str[$i] === '=') {
                break;
            }
            $ret .= $str[$i];
        }
        return urldecode($ret);
    };
    foreach ($returnHeaders as $header) {
        //Set-Cookie: crlfcoookielol=crlf+is%0D%0A+and+newline+is+%0D%0A+and+semicolon+is%3B+and+not+sure+what+else
        /*Set-Cookie:ci_spill=a%3A4%3A%7Bs%3A10%3A%22session_id%22%3Bs%3A32%3A%22305d3d67b8016ca9661c3b032d4319df%22%3Bs%3A10%3A%22ip_address%22%3Bs%3A14%3A%2285.164.158.128%22%3Bs%3A10%3A%22user_agent%22%3Bs%3A109%3A%22Mozilla%2F5.0+%28Windows+NT+6.1%3B+WOW64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F43.0.2357.132+Safari%2F537.36%22%3Bs%3A13%3A%22last_activity%22%3Bi%3A1436874639%3B%7Dcab1dd09f4eca466660e8a767856d013; expires=Tue, 14-Jul-2015 13:50:39 GMT; path=/
        Set-Cookie: sessionToken=abc123; Expires=Wed, 09 Jun 2021 10:18:14 GMT;
        //Cookie names cannot contain any of the following '=,; \t\r\n\013\014'
        //
        */
        if (stripos($header, "Set-Cookie:") !== 0) {
            continue;
            /**/
        }
        $header = trim(substr($header, strlen("Set-Cookie:")));
        while (strlen($header) > 0) {
            $cookiename                 = $grabCookieName($header);
            $returnCookies[$cookiename] = '';
            $header                     = substr($header, strlen($cookiename) + 1); //also remove the = 
            if (strlen($header) < 1) {
                break;
            }
            ;
            $thepos = strpos($header, ';');
            if ($thepos === false) { //last cookie in this Set-Cookie.
                $returnCookies[$cookiename] = urldecode($header);
                break;
            }
            $returnCookies[$cookiename] = urldecode(substr($header, 0, $thepos));
            $header                     = trim(substr($header, $thepos + 1)); //also remove the ;
        }
    }
    unset($header, $cookiename, $thepos);
    return $htmlBody;
}

function hhb_curl_exec($ch, $url)
{
    static $hhb_curl_domainCache = "";
    //$hhb_curl_domainCache=&$this->hhb_curl_domainCache;
    //$ch=&$this->curlh;
    if (!is_resource($ch) || get_resource_type($ch) !== 'curl') {
        throw new InvalidArgumentException('$ch must be a curl handle!');
    }
    if (!is_string($url)) {
        throw new InvalidArgumentException('$url must be a string!');
    }

    $tmpvar = "";
    if (parse_url($url, PHP_URL_HOST) === null) {
        if (substr($url, 0, 1) !== '/') {
            $url = $hhb_curl_domainCache . '/' . $url;
        } else {
            $url = $hhb_curl_domainCache . $url;
        }
    }
    ;

    curl_setopt($ch, CURLOPT_URL, $url);
    $html = curl_exec($ch);
    if (curl_errno($ch)) {
        throw new Exception('Curl error (curl_errno=' . curl_errno($ch) . ') on url ' . var_export($url, true) . ': ' . curl_error($ch));
        // echo 'Curl error: ' . curl_error($ch);
    }
    if ($html === '' && 203 != ($tmpvar = curl_getinfo($ch, CURLINFO_HTTP_CODE)) /*203 is "success, but no output"..*/ ) {
        throw new Exception('Curl returned nothing for ' . var_export($url, true) . ' but HTTP_RESPONSE_CODE was ' . var_export($tmpvar, true));
    }
    ;
    //remember that curl (usually) auto-follows the "Location: " http redirects..
    $hhb_curl_domainCache = parse_url(curl_getinfo($ch, CURLINFO_EFFECTIVE_URL), PHP_URL_HOST);
    return $html;
}
hanshenrik
la source
1

La réponse acceptée semble rechercher dans tout le message de réponse. Cela pourrait vous donner de fausses correspondances pour les en-têtes de cookies si le mot "Set-Cookie" est au début d'une ligne. Bien que cela devrait être bien dans la plupart des cas. Le moyen le plus sûr pourrait être de lire le message du début jusqu'à la première ligne vide qui indique la fin des en-têtes de message. Ceci est juste une solution alternative qui devrait rechercher la première ligne vide et ensuite utiliser preg_grep sur ces lignes uniquement pour trouver "Set-Cookie".

    curl_setopt($ch, CURLOPT_HEADER, 1);
    //Return everything
    $res = curl_exec($ch);
    //Split into lines
    $lines = explode("\n", $res);
    $headers = array();
    $body = "";
    foreach($lines as $num => $line){
        $l = str_replace("\r", "", $line);
        //Empty line indicates the start of the message body and end of headers
        if(trim($l) == ""){
            $headers = array_slice($lines, 0, $num);
            $body = $lines[$num + 1];
            //Pull only cookies out of the headers
            $cookies = preg_grep('/^Set-Cookie:/', $headers);
            break;
        }
    }
Rich Wandell
la source
1
La réponse acceptée semble rechercher dans tout le message de réponse. Cela pourrait vous donner de fausses correspondances pour les en-têtes de cookies si le mot "Set-Cookie" est au début d'une ligne. Bien que cela devrait être bien dans la plupart des cas. La manière la plus sûre pourrait être de lire le message du début jusqu'à la première ligne vide qui indique la fin des en-têtes de message. Ceci est juste une solution alternative qui devrait rechercher la première ligne vide et ensuite utiliser preg_grep sur ces lignes uniquement pour trouver "Set-Cookie".
Rich Wandell
0

Je crois comprendre que les cookies de curldoivent être écrits dans un fichier ( curl -c cookie_file). Si vous utilisez curlPHP execou des systemfonctions (ou tout autre élément de cette famille), vous devriez pouvoir enregistrer les cookies dans un fichier, puis ouvrir le fichier et les lire.

Kyle
la source
4
Il fait presque certainement référence à php.net/curl :)
TML