cara mendapatkan cookie dari php curl menjadi variabel

126

Jadi beberapa orang di beberapa perusahaan lain berpikir itu akan luar biasa jika alih-alih menggunakan sabun atau xml-rpc atau istirahat atau protokol komunikasi masuk akal lainnya, dia hanya menyematkan semua tanggapannya sebagai cookie di tajuk.

Saya perlu mengeluarkan cookie ini karena mudah-mudahan merupakan array dari respons curl ini. Jika saya harus menyia-nyiakan banyak hidup saya menulis parser untuk ini, saya akan sangat tidak bahagia.

Adakah yang tahu bagaimana ini bisa dilakukan dengan mudah, lebih disukai tanpa menulis apapun ke file?

Saya akan sangat berterima kasih jika ada yang bisa membantu saya dalam hal ini.

haus93
sumber

Jawaban:

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
sumber
31
Sayangnya saya merasa ini adalah jawaban yang benar. Saya pikir konyol bahwa ikal tidak bisa begitu saja memberi saya array yang dipetakan.
haus93
3
Saya akan memberikannya kepada Anda tetapi preg_match salah. Saya tidak hanya ingin sesi itu, tetapi saya mengerti mengapa Anda berpikir seperti itu. Tetapi jenius yang membuat sistem mereka memuat cookie dengan seluruh peta respons seperti dengan get atau posting. Sial seperti ini: Set-Cookie: price = 1 Set-Cookie: status = accept Saya membutuhkan preg_match_all dengan '/ ^ Set-Cookie: (. *?) = (. *?) $ /
Sm
7
@ haus93 Curl tidak bisa memberi Anda array yang dipetakan. Tetapi menunjukkan cara untuk menyimpannyacurl_setopt($ch, CURLOPT_HEADERFUNCTION, 'callback_SaveHeaders');
Shiplu Mokaddim
2
Bergantung pada struktur cookie yang dikembalikan, baris terakhir mungkin perlu dimodifikasi menjadi sesuatu seperti parse_str($m[1], $cookies), yang akan memasukkan cookie ke dalam array asosiatif dalam $cookiesvariabel ....
random_user_name
7
Untuk perbaikan gabungan yang mengambil lebih dari satu 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

Meskipun pertanyaan ini sudah cukup lama, dan respons yang diterima valid, saya merasa agak tidak nyaman karena konten respons HTTP (HTML, XML, JSON, biner, atau apa pun) bercampur dengan header.

Saya telah menemukan alternatif lain. CURL menyediakan opsi ( CURLOPT_HEADERFUNCTION) untuk menyetel panggilan balik yang akan dipanggil untuk setiap baris header respons. Fungsi ini akan menerima objek curl dan string dengan garis header.

Anda dapat menggunakan kode seperti ini (diadaptasi dari respons 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
}

Solusi ini memiliki kelemahan menggunakan variabel global, tapi saya rasa ini bukan masalah untuk skrip pendek. Dan Anda selalu dapat menggunakan metode dan atribut statis jika curl digabungkan ke dalam kelas.

Googol
sumber
10
Alih-alih global, Anda bisa menggunakan closure yang menyimpan referensi $cookies. $curlResponseHeaderCallback = function ($ch, $headerLine) use (&$cookies) {lalu curl_setopt($ch, CURLOPT_HEADERFUNCTION, $curlResponseHeaderCallback);.
Seph
Apa yang terjadi jika Anda memiliki semua ini di kelas? Bagaimana Anda mereferensikan fungsi kelas $class->curlResponseHeaderCallback()? Atau apakah Anda baru saja di curlResponseHeaderCallbackluar kelas?
Sevenearths
13

Ini melakukannya tanpa regexps, tetapi membutuhkan ekstensi 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.
sumber
2
Terima kasih untuk ini. Solusi semantik yang jelas sepadan dengan kesulitan memasang ekstensi.
Ben Jacobs
2
Ini akan menjadi solusi terbaik, jika hanya pecl installbenar-benar berhasil. Grrr.
Robin Winslow
11

Jika Anda menggunakan CURLOPT_COOKIE_FILE dan CURLOPT_COOKIE_JAR curl akan membaca / menulis cookie dari / ke file. Anda dapat, setelah curl selesai, membaca dan / atau memodifikasinya sesuka Anda.

Kotak sabun
sumber
12
Saya pikir tujuannya bukan untuk menggunakan file ini
Nicolas Thery
3

libcurl juga menyediakan CURLOPT_COOKIELIST yang mengekstrak semua cookie yang dikenal. Yang Anda butuhkan hanyalah memastikan pengikatan PHP / CURL dapat menggunakannya.

Daniel Stenberg
sumber
12
Ini tidak dapat digunakan melalui API PHP.
Emre Yazici
1

seseorang di sini mungkin merasa berguna. hhb_curl_exec2 bekerja sangat mirip dengan curl_exec, tetapi arg3 adalah larik yang akan diisi dengan header http yang dikembalikan (indeks numerik), dan arg4 adalah larik yang akan diisi dengan cookie yang dikembalikan ($ cookies ["expires"] => " Jum, 06-Mei-2016 05:58:51 GMT "), dan arg5 akan diisi dengan ... info tentang permintaan mentah yang dibuat oleh curl.

downside adalah bahwa itu membutuhkan CURLOPT_RETURNTRANSFER untuk aktif, jika tidak kesalahan akan keluar, dan itu akan menimpa CURLOPT_STDERR dan CURLOPT_VERBOSE, jika Anda sudah menggunakannya untuk hal lain .. (saya mungkin memperbaikinya nanti)

contoh cara menggunakannya:

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

dan fungsinya sendiri ..

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

Jawaban yang diterima sepertinya akan menelusuri seluruh pesan respons. Ini bisa memberi Anda kecocokan palsu untuk header cookie jika kata "Set-Cookie" ada di awal baris. Meskipun seharusnya baik-baik saja dalam banyak kasus. Cara yang lebih aman mungkin dengan membaca pesan dari awal hingga baris kosong pertama yang menunjukkan akhir header pesan. Ini hanyalah solusi alternatif yang harus mencari baris kosong pertama dan kemudian menggunakan preg_grep pada baris tersebut hanya untuk menemukan "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
sumber
1
Jawaban yang diterima sepertinya akan menelusuri seluruh pesan respons. Ini bisa memberi Anda kecocokan palsu untuk header cookie jika kata "Set-Cookie" ada di awal baris. Meskipun seharusnya baik-baik saja dalam banyak kasus. Cara yang lebih aman mungkin dengan membaca pesan dari awal hingga baris kosong pertama yang menunjukkan akhir header pesan. Ini hanyalah solusi alternatif yang harus mencari baris kosong pertama dan kemudian menggunakan preg_grep pada baris tersebut hanya untuk menemukan "Set-Cookie".
Rich Wandell
0

Pemahaman saya adalah bahwa cookie dari curlharus ditulis ke file ( curl -c cookie_file). Jika Anda menjalankan curlmelalui PHP execatau systemfungsi (atau apa pun di keluarga itu), Anda harus dapat menyimpan cookie ke file, lalu buka file dan membacanya di.

kyle
sumber
4
Dia hampir pasti mengacu pada php.net/curl :)
TML