como obter os cookies de um php curl em uma variável

126

Então, alguém de outra empresa achou que seria incrível se, em vez de usar sabão ou xml-rpc ou rest ou qualquer outro protocolo de comunicação razoável, ele apenas incorporasse toda a sua resposta como cookies no cabeçalho.

Eu preciso puxar esses cookies como espero que uma matriz desta resposta de curl. Se eu tiver que perder um monte de minha vida escrevendo um analisador para isso, ficarei muito infeliz.

Alguém sabe como isso pode ser feito simplesmente, de preferência sem gravar nada em um arquivo?

Ficarei muito grato se alguém puder me ajudar com isso.

thirsty93
fonte

Respostas:

173
$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
fonte
31
Infelizmente, sinto que esta é a resposta certa. Eu acho ridículo que o enrolamento não possa me dar apenas uma matriz mapeada.
Thirsty93 22/05/09
3
Vou dar a você, mas a preg_match estava errada. Não queria apenas a sessão, mas entendo por que você pensaria isso. Mas o gênio que criou o sistema está carregando o cookie com um mapa de respostas inteiro, como com um get ou post. Merda assim: Set-Cookie: price = 1 Set-Cookie: status = accept Eu precisava de um preg_match_all com '/ ^ Set-Cookie: (. *?) = (. *?) $ / Sm'
thirsty93
7
@ thirsty93 O Curl não pode fornecer uma matriz mapeada. Mas mostra uma maneira de salvá-locurl_setopt($ch, CURLOPT_HEADERFUNCTION, 'callback_SaveHeaders');
Shiplu Mokaddim
2
Dependendo da estrutura do cookie retornou, a última linha pode precisar ser modificada para algo como parse_str($m[1], $cookies), que irá encher os biscoitos em uma matriz associativa na $cookiesvariável ....
random_user_name
7
Para as correções combinadas que capturam mais de um 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

Embora essa pergunta seja bastante antiga e a resposta aceita seja válida, acho um pouco desconfortável porque o conteúdo da resposta HTTP (HTML, XML, JSON, binário ou o que for) se mistura com os cabeçalhos.

Eu encontrei uma alternativa diferente. CURL fornece uma opção (CURLOPT_HEADERFUNCTION ) para definir um retorno de chamada que será chamado para cada linha de cabeçalho de resposta. A função receberá o objeto curl e uma string com a linha do cabeçalho.

Você pode usar um código como este (adaptado da resposta 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
}

Esta solução tem a desvantagem de usar uma variável global, mas acho que isso não é um problema para scripts curtos. E você sempre pode usar métodos e atributos estáticos se o enrolamento estiver sendo agrupado em uma classe.

Googol
fonte
10
Em vez de global, você pode usar um fechamento com uma referência a $cookies. $curlResponseHeaderCallback = function ($ch, $headerLine) use (&$cookies) {então curl_setopt($ch, CURLOPT_HEADERFUNCTION, $curlResponseHeaderCallback);.
Seph
O que acontece se você tiver tudo isso em uma aula? Como você faz referência à função de classe $class->curlResponseHeaderCallback()? Ou você apenas tem curlResponseHeaderCallbackfora da classe?
Sevenearths
13

Isso é feito sem regexps, mas requer a extensão 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
fonte
2
Obrigado por isso. Uma solução clara e semântica vale a pena ao instalar uma extensão.
Ben Jacobs
2
Essa seria a melhor solução, se pecl installrealmente funcionasse. Grrr.
Robin Winslow
11

Se você usar CURLOPT_COOKIE_FILE e CURLOPT_COOKIE_JAR, o curl lerá / gravará os cookies de / para um arquivo. Você pode, depois que o enrolamento terminar, ler e / ou modificá-lo da maneira que desejar.

SoapBox
fonte
12
Eu acho que o objetivo não é usar esse arquivo
Nicolas Thery
3

A libcurl também fornece CURLOPT_COOKIELIST que extrai todos os cookies conhecidos. Tudo que você precisa é garantir que a ligação PHP / CURL possa usá-la.

Daniel Stenberg
fonte
12
Não é utilizável via API PHP.
Emre Yazici
1

alguém aqui pode achar útil. hhb_curl_exec2 funciona praticamente como curl_exec, mas arg3 é uma matriz que será preenchida com os cabeçalhos http retornados (índice numérico) e arg4 é uma matriz que será preenchida com os cookies retornados ($ cookies ["expires"] => " Sex, 06 de maio de 2016 05:58:51 GMT ") e arg5 serão preenchidos com ... informações sobre a solicitação bruta feita por curl.

A desvantagem é que ele requer que CURLOPT_RETURNTRANSFER esteja ativado, caso contrário, ele gera um erro e substitui CURLOPT_STDERR e CURLOPT_VERBOSE, se você já os estiver usando para outra coisa .. (eu poderia corrigir isso mais tarde)

exemplo de como usá-lo:

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

e a própria função ..

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
fonte
1

A resposta aceita parece que procurará por toda a mensagem de resposta. Isso pode fornecer correspondências falsas para os cabeçalhos dos cookies se a palavra "Set-Cookie" estiver no início de uma linha. Embora deva ficar bem na maioria dos casos. A maneira mais segura pode ser ler a mensagem desde o início até a primeira linha vazia que indica o fim dos cabeçalhos da mensagem. Esta é apenas uma solução alternativa que deve procurar a primeira linha em branco e usar preg_grep nessas linhas apenas para encontrar "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
fonte
1
A resposta aceita parece que procurará por toda a mensagem de resposta. Isso pode fornecer correspondências falsas para os cabeçalhos dos cookies, se a palavra "Set-Cookie" estiver no início de uma linha. Embora deva ficar bem na maioria dos casos. A maneira mais segura pode ser ler a mensagem desde o início até a primeira linha vazia, que indica o fim dos cabeçalhos da mensagem. Esta é apenas uma solução alternativa que deve procurar a primeira linha em branco e usar preg_grep nessas linhas apenas para encontrar "Set-Cookie".
Ricos Wandell
0

Entendo que os cookies de curldevem ser gravados em um arquivo ( curl -c cookie_file). Se você estiver executando curlas funções execou PHP system(ou qualquer coisa dessa família), poderá salvar os cookies em um arquivo, abra o arquivo e leia-os.

kyle
fonte
4
Ele está quase certamente referindo-se php.net/curl :)
TML