cómo obtener las cookies de un curl php en una variable

126

Entonces, un tipo de otra compañía pensó que sería increíble si en lugar de usar jabón o xml-rpc o descansar o cualquier otro protocolo de comunicación razonable, simplemente incrustara toda su respuesta como cookies en el encabezado.

Necesito sacar estas cookies con suerte como una matriz de esta respuesta de rizo. Si tengo que desperdiciar un montón de mi vida escribiendo un analizador para esto, seré muy infeliz.

¿Alguien sabe cómo se puede hacer esto, preferiblemente sin escribir nada en un archivo?

Estaré muy agradecido si alguien me puede ayudar con esto.

sediento93
fuente

Respuestas:

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
fuente
31
Lamentablemente tengo la sensación de que esta es la respuesta correcta. Sin embargo, creo que es ridículo que curl no pueda entregarme una matriz mapeada.
sed93
3
Te lo daré, pero el preg_match estaba mal. No solo quería la sesión, aunque entiendo por qué piensas eso. Pero el genio que creó su sistema está cargando la cookie con un mapa de respuesta completo, como con un get o post. Mierda como esta: Set-Cookie: price = 1 Set-Cookie: status = accept Necesité un preg_match_all con '/ ^ Set-Cookie: (. *?) = (. *?) $ / Sm'
thirsty93
77
@ thirsty93 Curl no puede darte una matriz mapeada. Pero te muestra una manera de salvarlocurl_setopt($ch, CURLOPT_HEADERFUNCTION, 'callback_SaveHeaders');
Shiplu Mokaddim
2
Dependiendo de la estructura de cookies devuelta, es posible que la última línea deba modificarse a algo así parse_str($m[1], $cookies), lo que rellenará las cookies en una matriz asociativa en la $cookiesvariable ...
random_user_name
77
Para las soluciones combinadas que capturan más de una 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

Aunque esta pregunta es bastante antigua y la respuesta aceptada es válida, me resulta un poco incómodo porque el contenido de la respuesta HTTP (HTML, XML, JSON, binario o lo que sea) se mezcla con los encabezados.

He encontrado una alternativa diferente. CURL proporciona una opción (CURLOPT_HEADERFUNCTION ) para establecer una devolución de llamada que se llamará para cada línea de encabezado de respuesta. La función recibirá el objeto curl y una cadena con la línea de encabezado.

Puede usar un código como este (adaptado de la respuesta 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 solución tiene el inconveniente de usar una variable global, pero supongo que esto no es un problema para los scripts cortos. Y siempre puede usar métodos y atributos estáticos si curl se está envolviendo en una clase.

Googol
fuente
10
En lugar de un global, puede usar un cierre con una referencia a $cookies. $curlResponseHeaderCallback = function ($ch, $headerLine) use (&$cookies) {entonces curl_setopt($ch, CURLOPT_HEADERFUNCTION, $curlResponseHeaderCallback);.
Seph
¿Qué pasa si tienes todo esto en una clase? ¿Cómo se hace referencia a la función de clase $class->curlResponseHeaderCallback()? ¿O simplemente tienes curlResponseHeaderCallbackfuera de la clase?
Sevenearths
13

Esto lo hace sin expresiones regulares, pero requiere la extensión 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
fuente
2
Gracias por esto. Una solución clara y semántica merece la pena instalar una extensión.
Ben Jacobs
2
Esta sería la mejor solución, si pecl installrealmente funcionara. Grrr.
Robin Winslow
11

Si usa CURLOPT_COOKIE_FILE y CURLOPT_COOKIE_JAR curl leerá / escribirá las cookies de / a un archivo. Puede, después de terminar el rizo, leerlo y / o modificarlo como desee.

Plataforma improvisada
fuente
12
Creo que el objetivo no es usar este archivo
Nicolas Thery
3

libcurl también proporciona CURLOPT_COOKIELIST que extrae todas las cookies conocidas. Todo lo que necesita es asegurarse de que el enlace PHP / CURL pueda usarlo.

Daniel Stenberg
fuente
12
No se puede usar a través de la API de PHP.
Emre Yazici
1

alguien aquí puede encontrarlo útil. hhb_curl_exec2 funciona más o menos como curl_exec, pero arg3 es una matriz que se completará con los encabezados http devueltos (índice numérico), y arg4 es una matriz que se completará con las cookies devueltas ($ cookies ["caduca"] => " Vie, 06-May-2016 05:58:51 GMT "), y arg5 se completará con ... información sobre la solicitud sin procesar realizada por curl.

La desventaja es que requiere que CURLOPT_RETURNTRANSFER esté activado, de lo contrario, se producirá un error y sobrescribirá CURLOPT_STDERR y CURLOPT_VERBOSE, si ya los estaba usando para otra cosa ... (podría arreglar esto más adelante)

ejemplo de cómo usarlo:

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

y la función en sí ..

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

Parece que la respuesta aceptada buscará en todo el mensaje de respuesta. Esto podría darle coincidencias falsas para los encabezados de cookies si la palabra "Set-Cookie" está al comienzo de una línea. Si bien debería estar bien en la mayoría de los casos. La forma más segura podría ser leer el mensaje desde el principio hasta la primera línea vacía que indica el final de los encabezados del mensaje. Esta es solo una solución alternativa que debería buscar la primera línea en blanco y luego usar preg_grep en esas líneas solo 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
fuente
1
Parece que la respuesta aceptada buscará en todo el mensaje de respuesta. Esto podría darle coincidencias falsas para los encabezados de cookies si la palabra "Set-Cookie" está al comienzo de una línea. Si bien debería estar bien en la mayoría de los casos. La forma más segura podría ser leer el mensaje desde el principio hasta la primera línea vacía que indica el final de los encabezados del mensaje. Esta es solo una solución alternativa que debería buscar la primera línea en blanco y luego usar preg_grep en esas líneas solo para encontrar "Set-Cookie".
Rich Wandell
0

Tengo entendido que las cookies curldeben escribirse en un archivo ( curl -c cookie_file). Si está ejecutando curlPHP execo systemfunciones (o cualquier cosa de esa familia), debería poder guardar las cookies en un archivo, luego abrir el archivo y leerlo.

Kyle
fuente
44
Es casi seguro que se refiere a php.net/curl :)
TML