wie man die Cookies von einer PHP-Locke in eine Variable bekommt

126

Also dachte ein Typ von einer anderen Firma, es wäre großartig, wenn er anstelle von Seife oder XML-RPC oder Rest oder einem anderen vernünftigen Kommunikationsprotokoll einfach alle seine Antworten als Cookies in den Header einbetten würde.

Ich muss diese Cookies als hoffentlich ein Array aus dieser Curl-Antwort herausziehen. Wenn ich einen Teil meines Lebens damit verschwenden muss, einen Parser dafür zu schreiben, werde ich sehr unglücklich sein.

Weiß jemand, wie dies einfach gemacht werden kann, vorzugsweise ohne etwas in eine Datei zu schreiben?

Ich werde sehr dankbar sein, wenn mir jemand dabei helfen kann.

durstig93
quelle

Antworten:

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
quelle
31
Leider habe ich das Gefühl, dass dies die richtige Antwort ist. Ich finde es lächerlich, dass Curl mir nicht einfach ein zugeordnetes Array geben kann.
durstig93
3
Ich werde es dir geben, aber das preg_match war falsch. Ich wollte nicht nur die Sitzung, obwohl ich verstehe, warum du das denkst. Aber das Genie, das sein System erstellt hat, lädt den Cookie mit einer vollständigen Antwortkarte wie mit einem Get oder Post. Scheiße wie folgt: Set-Cookie: Preis = 1 Set-Cookie: Status = Akzeptieren Ich brauchte ein preg_match_all mit '/ ^ Set-Cookie: (. *?) = (. *?) $ /
Sm
7
@ dursty93 Curl kann Ihnen kein zugeordnetes Array geben. Aber zeigt Ihnen einen Weg, um es zu rettencurl_setopt($ch, CURLOPT_HEADERFUNCTION, 'callback_SaveHeaders');
Shiplu Mokaddim
2
Abhängig von der zurückgegebenen Cookie-Struktur muss die letzte Zeile möglicherweise so geändert werden parse_str($m[1], $cookies), dass die Cookies in ein assoziatives Array in der $cookiesVariablen
eingefügt
7
Für die kombinierten Korrekturen, die mehr als ein 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); }
abrufen
39

Obwohl diese Frage ziemlich alt ist und die akzeptierte Antwort gültig ist, finde ich sie etwas unangenehm, da der Inhalt der HTTP-Antwort (HTML, XML, JSON, Binär oder was auch immer) mit den Headern gemischt wird.

Ich habe eine andere Alternative gefunden. CURL bietet eine Option (CURLOPT_HEADERFUNCTION ) zum Festlegen eines Rückrufs, der für jede Antwortheaderzeile aufgerufen wird. Die Funktion empfängt das Curl-Objekt und eine Zeichenfolge mit der Kopfzeile.

Sie können einen Code wie diesen verwenden (angepasst aus der TML-Antwort):

$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
}

Diese Lösung hat den Nachteil, dass eine globale Variable verwendet wird, aber ich denke, dies ist kein Problem für kurze Skripte. Und Sie können immer statische Methoden und Attribute verwenden, wenn Curl in eine Klasse eingeschlossen wird.

Googol
quelle
10
Anstelle eines globalen können Sie einen Abschluss verwenden, der einen Verweis auf enthält $cookies. $curlResponseHeaderCallback = function ($ch, $headerLine) use (&$cookies) {dann curl_setopt($ch, CURLOPT_HEADERFUNCTION, $curlResponseHeaderCallback);.
Seph
Was passiert, wenn Sie das alles in einer Klasse haben? Wie verweisen Sie auf die Klassenfunktion $class->curlResponseHeaderCallback()? Oder haben Sie nur curlResponseHeaderCallbackaußerhalb der Klasse?
Sevenearths
13

Dies geschieht ohne reguläre Ausdrücke, erfordert jedoch die PECL-HTTP-Erweiterung .

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.
quelle
2
Danke dafür. Eine klare, semantische Lösung ist die Mühe wert, eine Erweiterung zu installieren.
Ben Jacobs
2
Dies wäre die beste Lösung, wenn sie nur pecl installtatsächlich funktionieren würde. Grrr.
Robin Winslow
11

Wenn Sie CURLOPT_COOKIE_FILE und CURLOPT_COOKIE_JAR verwenden, liest / schreibt curl die Cookies aus / in eine Datei. Nachdem das Einrollen abgeschlossen ist, können Sie es nach Belieben lesen und / oder ändern.

Seifenkiste
quelle
12
Ich denke, das Ziel ist nicht, diese Datei zu verwenden
Nicolas Thery
3

libcurl bietet auch CURLOPT_COOKIELIST, das alle bekannten Cookies extrahiert. Sie müssen lediglich sicherstellen, dass die PHP / CURL-Bindung sie verwenden kann.

Daniel Stenberg
quelle
12
Es kann nicht über die PHP-API verwendet werden.
Emre Yazici
1

Jemand hier könnte es nützlich finden. hhb_curl_exec2 funktioniert ziemlich ähnlich wie curl_exec, aber arg3 ist ein Array, das mit den zurückgegebenen http-Headern (numerischer Index) gefüllt wird, und arg4 ist ein Array, das mit den zurückgegebenen Cookies gefüllt wird ($ cookies ["expires"] => " Fr, 06-May-2016 05:58:51 GMT ") und arg5 werden mit ... Informationen über die Rohanforderung von curl gefüllt.

Der Nachteil ist, dass CURLOPT_RETURNTRANSFER aktiviert sein muss, andernfalls wird ein Fehler ausgegeben und CURLOPT_STDERR und CURLOPT_VERBOSE werden überschrieben , wenn Sie sie bereits für etwas anderes verwendet haben. (Ich könnte dies später beheben.)

Beispiel für die Verwendung:

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

und die Funktion selbst ..

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

Die akzeptierte Antwort scheint die gesamte Antwortnachricht zu durchsuchen. Dies kann zu falschen Übereinstimmungen für Cookie-Header führen, wenn das Wort "Set-Cookie" am Anfang einer Zeile steht. Während es in den meisten Fällen in Ordnung sein sollte. Der sicherere Weg könnte darin bestehen, die Nachricht vom Anfang bis zur ersten leeren Zeile durchzulesen, die das Ende der Nachrichtenkopfzeilen angibt. Dies ist nur eine alternative Lösung, die nach der ersten Leerzeile suchen und dann preg_grep in diesen Zeilen verwenden sollte, um nur "Set-Cookie" zu finden.

    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
quelle
1
Die akzeptierte Antwort scheint die gesamte Antwortnachricht zu durchsuchen. Dies kann zu falschen Übereinstimmungen für Cookie-Header führen, wenn das Wort "Set-Cookie" am Anfang einer Zeile steht. Während es in den meisten Fällen in Ordnung sein sollte. Der sicherere Weg könnte darin bestehen, die Nachricht vom Anfang bis zur ersten leeren Zeile durchzulesen, die das Ende der Nachrichtenkopfzeilen angibt. Dies ist nur eine alternative Lösung, die nach der ersten Leerzeile suchen und dann preg_grep in diesen Zeilen verwenden sollte, um nur "Set-Cookie" zu finden.
Rich Wandell
0

Nach meinem Verständnis curlmüssen Cookies von in eine Datei ( curl -c cookie_file) geschrieben werden. Wenn Sie PHPs oder Funktionen (oder irgendetwas in dieser Familie) curldurchlaufen , sollten Sie in der Lage sein, die Cookies in einer Datei zu speichern, dann die Datei zu öffnen und sie einzulesen.execsystem

Kyle
quelle
4
Er bezieht sich mit ziemlicher Sicherheit auf php.net/curl :)
TML