jak przenieść pliki cookie z curl php do zmiennej

126

Więc jakiś facet z innej firmy pomyślał, że byłoby wspaniale, gdyby zamiast używać mydła, xml-rpc, reszty lub innego rozsądnego protokołu komunikacyjnego, po prostu umieścił całą swoją odpowiedź w nagłówku jako ciasteczka.

Muszę wyciągnąć te ciasteczka jako, mam nadzieję, tablicę z tej odpowiedzi curl. Jeśli będę musiał marnować trochę życia na pisanie do tego parsera, będę bardzo nieszczęśliwy.

Czy ktoś wie, jak można to po prostu zrobić, najlepiej bez zapisywania czegokolwiek do pliku?

Będę bardzo wdzięczny, jeśli ktoś może mi w tym pomóc.

spragniony93
źródło

Odpowiedzi:

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
źródło
31
Niestety mam wrażenie, że to dobra odpowiedź. Myślę, że to śmieszne, że curl nie może po prostu podać mi zmapowanej tablicy.
spragniony93
3
Dam ci to, ale preg_match było złe. Nie chciałem tylko sesji, ale rozumiem, dlaczego tak myślisz. Ale geniusz, który stworzył swój system, ładuje plik cookie z całą mapą odpowiedzi, jak w przypadku pobierania lub postu. Gówno takie: Set-Cookie: price = 1 Set-Cookie: status = accept Potrzebowałem preg_match_all z '/ ^ Set-Cookie: (. *?) = (. *?) $ / Sm'
thirsty93
7
@ thirsty93 Curl nie może podać zmapowanej tablicy. Ale pokazuje sposób, aby to uratowaćcurl_setopt($ch, CURLOPT_HEADERFUNCTION, 'callback_SaveHeaders');
Shiplu Mokaddim
2
W zależności od zwróconej struktury pliku cookie, ostatnia linia może wymagać modyfikacji do czegoś podobnego parse_str($m[1], $cookies), co spowoduje umieszczenie plików cookie w tablicy asocjacyjnej w $cookieszmiennej ....
random_user_name
7
W przypadku połączonych poprawek, które przechwytują więcej niż jeden plik 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

Chociaż to pytanie jest dość stare, a zaakceptowana odpowiedź jest prawidłowa, uważam to za trochę niewygodne, ponieważ treść odpowiedzi HTTP (HTML, XML, JSON, binarne lub cokolwiek innego) jest mieszana z nagłówkami.

Znalazłem inną alternatywę. CURL udostępnia opcję (CURLOPT_HEADERFUNCTION ) do ustawienia wywołania zwrotnego, które będzie wywoływane dla każdego wiersza nagłówka odpowiedzi. Funkcja otrzyma obiekt curl i ciąg z wierszem nagłówka.

Możesz użyć takiego kodu (dostosowanego z odpowiedzi 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
}

To rozwiązanie ma tę wadę, że używa zmiennej globalnej, ale myślę, że nie jest to problem w przypadku krótkich skryptów. I zawsze możesz użyć statycznych metod i atrybutów, jeśli curl jest pakowany do klasy.

Googol
źródło
10
Zamiast globalnego można użyć zamknięcia zawierającego odniesienie do $cookies. $curlResponseHeaderCallback = function ($ch, $headerLine) use (&$cookies) {wtedy curl_setopt($ch, CURLOPT_HEADERFUNCTION, $curlResponseHeaderCallback);.
Seph
Co się stanie, jeśli masz to wszystko w klasie? Jak odnosisz się do funkcji klasy $class->curlResponseHeaderCallback()? Czy po prostu wychodzisz curlResponseHeaderCallbackpoza zajęcia?
Sevenearths
13

Odbywa się to bez wyrażeń regularnych, ale wymaga rozszerzenia PECL HTTP .

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
źródło
2
Dzięki za to. Przejrzyste, semantyczne rozwiązanie jest warte wysiłku związanego z instalacją rozszerzenia.
Ben Jacobs
2
Byłoby to najlepsze rozwiązanie, gdyby tylko pecl installfaktycznie zadziałało. Grrr.
Robin Winslow
11

Jeśli używasz CURLOPT_COOKIE_FILE i CURLOPT_COOKIE_JAR, curl odczyta / zapisze ciasteczka z / do pliku. Po zakończeniu curl możesz go przeczytać i / lub zmodyfikować, jak chcesz.

SoapBox
źródło
12
Myślę, że celem nie jest użycie tego pliku
Nicolas Thery
3

libcurl udostępnia również CURLOPT_COOKIELIST, który wyodrębnia wszystkie znane pliki cookie. Wszystko, czego potrzebujesz, to upewnić się, że powiązanie PHP / CURL może go używać.

Daniel Stenberg
źródło
12
Nie można go używać za pośrednictwem interfejsu API PHP.
Emre Yazici
1

ktoś tutaj może uznać to za przydatne. hhb_curl_exec2 działa podobnie jak curl_exec, ale arg3 to tablica, która będzie wypełniona zwróconymi nagłówkami http (indeksem liczbowym), a arg4 to tablica, która będzie wypełniona zwróconymi ciasteczkami ($ cookies ["expires"] => " Fri, 06-May-2016 05:58:51 GMT ”), a argument arg5 zostanie wypełniony… informacjami o nieprzetworzonym żądaniu wysłanym przez curl.

Wadą jest to, że wymaga włączenia CURLOPT_RETURNTRANSFER, w przeciwnym razie wystąpi błąd i nadpisze CURLOPT_STDERR i CURLOPT_RETURNTRANSFER, w przeciwnym razie wystąpi CURLOPT_VERBOSE, jeśli już ich używałeś do czegoś innego ... (mogę to naprawić później)

przykład użycia:

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

i samą funkcję.

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
źródło
1

Zaakceptowana odpowiedź wydaje się przeszukiwać całą wiadomość z odpowiedzią. Może to dać fałszywe dopasowania dla nagłówków plików cookie, jeśli słowo „Set-Cookie” znajduje się na początku wiersza. Chociaż w większości przypadków powinno być dobrze. Bezpieczniejszym sposobem może być przeczytanie wiadomości od początku do pierwszej pustej linii, która wskazuje koniec nagłówków wiadomości. Jest to tylko alternatywne rozwiązanie, które powinno szukać pierwszej pustej linii, a następnie używać preg_grep w tych wierszach tylko w celu znalezienia „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
źródło
1
Zaakceptowana odpowiedź wydaje się przeszukiwać całą wiadomość z odpowiedzią. Może to dać fałszywe dopasowania dla nagłówków plików cookie, jeśli słowo „Set-Cookie” znajduje się na początku wiersza. Chociaż w większości przypadków powinno być dobrze. Bezpieczniejszym sposobem może być przeczytanie wiadomości od początku do pierwszej pustej linii, która wskazuje koniec nagłówków wiadomości. Jest to tylko alternatywne rozwiązanie, które powinno szukać pierwszej pustej linii, a następnie używać preg_grep w tych wierszach tylko w celu znalezienia „Set-Cookie”.
Rich Wandell
0

Rozumiem, że pliki cookie z domeny curlmuszą być zapisywane do pliku ( curl -c cookie_file). Jeśli curlkorzystasz z PHP execlub systemfunkcji (lub czegokolwiek z tej rodziny), powinieneś być w stanie zapisać pliki cookie do pliku, a następnie otworzyć plik i wczytać je.

kyle
źródło
4
Prawie na pewno ma na myśli php.net/curl :)
TML