как получить куки из php curl в переменную

Итак, какой-то парень из другой компании подумал, что было бы здорово, если бы вместо использования мыла, xml-rpc, rest или любого другого разумного протокола связи он просто встроил весь свой ответ в виде файлов cookie в заголовок.

Мне нужно вытащить эти файлы cookie как, надеюсь, массив из этого ответа curl. Если мне придется потратить кучу своей жизни на написание парсера для этого, я буду очень недоволен.

Кто-нибудь знает, как это просто сделать, желательно ничего не записывая в файл?

Буду очень признателен, если кто-нибудь сможет мне с этим помочь.


person thirsty93    schedule 21.05.2009    source источник


Ответы (8)


Хотя этот вопрос довольно старый и принятый ответ действителен, я нахожу его немного неудобным, потому что содержимое HTTP-ответа (HTML, XML, JSON, двоичный код или что-то еще) смешивается с заголовками.

Я нашел другую альтернативу. CURL предоставляет параметр (CURLOPT_HEADERFUNCTION) для установки обратного вызова, который будет вызываться для каждой строки заголовка ответа. Функция получит объект curl и строку со строкой заголовка.

Вы можете использовать такой код (адаптированный из ответа 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
}

У этого решения есть недостаток использования глобальной переменной, но я думаю, это не проблема для коротких скриптов. И вы всегда можете использовать статические методы и атрибуты, если curl переносится в класс.

person Googol    schedule 02.08.2014
comment
Вместо глобального можно использовать замыкание, содержащее ссылку на $cookies. $curlResponseHeaderCallback = function ($ch, $headerLine) use (&$cookies) {, затем curl_setopt($ch, CURLOPT_HEADERFUNCTION, $curlResponseHeaderCallback);. - person Seph; 26.04.2016
comment
Что произойдет, если у вас будет все это в классе? Как вы ссылаетесь на функцию класса $class->curlResponseHeaderCallback()? Или у вас просто curlResponseHeaderCallback вне класса? - person Robert Johnstone; 15.05.2020

Это делает это без регулярных выражений, но требует расширения 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);
person Alex P    schedule 24.08.2011
comment
Спасибо за это. Ясное семантическое решение стоит проблем с установкой расширения. - person Ben Jacobs; 01.12.2012
comment
Это было бы лучшим решением, если бы действительно pecl install работало. Grrr. - person Robin Winslow; 28.10.2013

Если вы используете CURLOPT_COOKIE_FILE и CURLOPT_COOKIE_JAR, curl будет читать / записывать файлы cookie из / в файл. Вы можете, после того, как curl будет с ним, читать и / или изменять его, как хотите.

person SoapBox    schedule 21.05.2009
comment
Думаю, цель не в том, чтобы использовать этот файл - person Nicolas Thery; 04.01.2013

libcurl также предоставляет CURLOPT_COOKIELIST, который извлекает все известные файлы cookie. Все, что вам нужно, это убедиться, что привязка PHP / CURL может его использовать.

person Daniel Stenberg    schedule 06.06.2009
comment
Его нельзя использовать через PHP API. - person Emre Yazici; 17.11.2010

кто-то здесь может найти это полезным. hhb_curl_exec2 работает почти так же, как curl_exec, но arg3 - это массив, который будет заполнен возвращенными заголовками http (числовой индекс), а arg4 - массив, который будет заполнен возвращенными куки ($ cookies ["expires"] => " Пт, 06-мая-2016 05:58:51 GMT "), а arg5 будет заполнен ... информацией о необработанном запросе, сделанном curl.

Обратной стороной является то, что для этого требуется, чтобы CURLOPT_RETURNTRANSFER был включен, иначе он выйдет из строя и перезапишет CURLOPT_STDERR и CURLOPT_VERBOSE, если вы уже использовали их для чего-то другого ... (я могу исправить это позже)

пример того, как его использовать:

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

и сама функция ..

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;
}
person hanshenrik    schedule 05.09.2015

Принятый ответ выглядит так, как будто он будет перебирать все ответное сообщение. Это может дать вам ложные совпадения для заголовков файлов cookie, если слово «Set-Cookie» находится в начале строки. Хотя в большинстве случаев это должно быть нормально. Более безопасным способом может быть чтение сообщения от начала до первой пустой строки, которая указывает конец заголовков сообщения. Это просто альтернативное решение, которое должно искать первую пустую строку, а затем использовать preg_grep в этих строках только для поиска «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;
        }
    }
person Rich Wandell    schedule 28.04.2016
comment
Принятый ответ выглядит так, как будто он будет перебирать все ответное сообщение. Это может дать вам ложные совпадения для заголовков файлов cookie, если слово Set-Cookie находится в начале строки. Хотя в большинстве случаев это должно быть нормально. Более безопасным способом может быть чтение сообщения от начала до первой пустой строки, которая указывает конец заголовков сообщения. Это просто альтернативное решение, которое должно искать первую пустую строку, а затем использовать preg_grep в этих строках только для поиска Set-Cookie. - person Rich Wandell; 28.04.2016

Насколько я понимаю, файлы cookie из curl должны быть записаны в файл (curl -c cookie_file). Если вы используете curl через функции PHP exec или system (или что-нибудь в этом семействе), вы сможете сохранить файлы cookie в файл, затем открыть файл и прочитать их.

person kyle    schedule 21.05.2009
comment
Он почти наверняка имеет в виду php.net/curl :) - person TML; 22.05.2009

person    schedule
comment
К сожалению, я чувствую, что это правильный ответ. Я думаю, что это смешно, что curl не может просто передать мне сопоставленный массив. - person thirsty93; 22.05.2009
comment
Я отдам его вам, но preg_match ошибся. Я не просто хотел сеанс, хотя понимаю, почему вы так думаете. Но гений, создавший их систему, загружает cookie с полной картой ответов, например, с помощью get или post. Вот дерьмо: Set-Cookie: price = 1 Set-Cookie: status = accept Мне нужен preg_match_all с '/ ^ Set-Cookie: (. *?) = (. *?) $ / Sm' - person thirsty93; 23.05.2009
comment
Нет, просто я немного не был за компьютером :) - person thirsty93; 26.05.2009
comment
@ thirsty93 Curl не может передать вам сопоставленный массив. Но показывает, как его спасти curl_setopt($ch, CURLOPT_HEADERFUNCTION, 'callback_SaveHeaders'); - person Shiplu Mokaddim; 14.01.2012
comment
В зависимости от возвращаемой структуры файлов cookie, последнюю строку может потребоваться изменить на что-то вроде parse_str($m[1], $cookies), что позволит поместить файлы cookie в ассоциативный массив в переменной $cookies .... - person random_user_name; 09.03.2013
comment
@cale_b Неплохое предложение, я отредактировал его. Спасибо. - person TML; 10.03.2013
comment
Этот ответ был близок к тому, что мне было нужно, но в итоге я использовал: preg_match_all('/(?<=Set-Cookie: ).[a-zA-Z0-9_=]+/', $curl_header, $matches) - person ryanpf; 05.02.2014
comment
Для комбинированных исправлений, которые захватывают более одного файла 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); } - person Combuster; 07.05.2015
comment
значения файлов cookie имеют кодировку URL. этот код не выполняет urldecode () значений. конечно, он работает для a-zA-Z0-9, но вы получите закодированные в URL-адресе значения таких вещей, как @ как% 40, пробел как% 20 и т. д .: p - person hanshenrik; 05.09.2015
comment
Если у вас нет точки с запятой в конце файла cookie, переменная будет продолжаться до конца заголовков. Вы можете предотвратить это, используя следующее: preg_match_all('/^Set-Cookie:\s*([^;\r\n]*)/mi', $header, $matches); Благодаря stackoverflow.com/a/35438727/594602 - person Erik Allen; 16.02.2016
comment
Как я видел, это получает значения только для Set-Cookies. Что происходит с файлами cookie, которые уже были отправлены в предыдущем запросе? - person maddo7; 04.02.2017
comment
@TML: я не могу получить все файлы cookie с помощью этого скрипта / кода. Я использовал "medium.com", чтобы получить подробную информацию о файлах cookie, но есть только две записи, Если я проверю консоль, на этом веб-сайте установлено множество других файлов cookie. Как получить все куки? Пока не повезло. - person Yogesh Chauhan; 08.02.2020