Andrej Pandovich
Andrej Pandovich

Reputation: 326

How can I parse $_SERVER['HTTP_COOKIE'] in pure PHP?

I need to parse $_SERVER['HTTP_COOKIE'] string in pure PHP. I have no access to $_COOKIE or functions like http_parse_cookie (PECL).

I tried with a function like:

function ParseCookies($strHeaders)
{
$result = array();              
    $aPairs = explode(';', $strHeaders);
    foreach ($aPairs as $pair)
    {
        $aKeyValues = explode('=', trim($pair), 2);
        if (count($aKeyValues) == 2)
        {
            switch ($aKeyValues[0])
            {
                case 'path':
                case 'domain':
                    $aTmp[trim($aKeyValues[0])] = urldecode(trim($aKeyValues[1]));
                    break;
                case 'expires':
                    $aTmp[trim($aKeyValues[0])] = strtotime(urldecode(trim($aKeyValues[1])));
                    break;
                default:
                    $aTmp['name'] = trim($aKeyValues[0]);
                    $aTmp['value'] = trim($aKeyValues[1]);
                    break;
            }
        }
        $result[] = $aTmp;
    }
return $result;
}

but the explode pattern is trivial, because I can have a cookie like name="a;b;c=1;";

I need something to tokenize the string in the proper way. Any ideas?

Upvotes: 1

Views: 6596

Answers (3)

nggit
nggit

Reputation: 720

To parse raw value of Cookie:/Set-Cookie:, e.g.

datr=xxx; expires=Wed, 10-Feb-2021 03:05:45 GMT; Max-Age=63071999; path=/; domain=.facebook.com; secure; httponly

in PHP is quite simple, use parse_str:

$raw_cookie = 'datr=xxx; expires=Wed, 10-Feb-2021 03:05:45 GMT; Max-Age=63071999; path=/; domain=.facebook.com; secure; httponly';

parse_str(strtr($raw_cookie, array('&' => '%26', '+' => '%2B', ';' => '&')), $cookies);

print_r($cookies);

Array
(
    [datr] => xxx
    [expires] => Wed, 10-Feb-2021 03:05:45 GMT
    [Max-Age] => 63071999
    [path] => /
    [domain] => .facebook.com
    [secure] => 
    [httponly] => 
)

You might notice, but we don't even need to worry about space behind the ;. Also parse_str will do urldecode.

Beware that dots (.) and spaces ( ) in cookie names are being replaced with underscores (_). Just like $_COOKIE. Even though RFC 6265 allows dot (.).

The pair function will be:

echo http_build_query($cookies, '', '; ', PHP_QUERY_RFC3986);

It will convert back to raw cookie string except that the spaces will be encoded to %20.

Edit: In case you need to keep multiple cookies with the same name:

function parse_cookie($str) {
    $cookies = array();
    $tok     = strtok($str, ';');
    while ($tok !== false) {
        $a                       = sscanf($tok, "%[^=]=%[^;]");
        $cookies[ltrim($a[0])][] = urldecode($a[1]);
        $tok                     = strtok(';');
    }
    return $cookies;
}

$str = 'datr=xxx; datr=yyy; datr=zzz; expires=Wed, 10-Feb-2021 03:05:45 GMT; Max-Age=63071999; path=/; domain=.facebook.com; secure; httponly';
print_r(parse_cookie($str));

Array
(
    [datr] => Array
        (
            [0] => xxx
            [1] => yyy
            [2] => zzz
        )

    [expires] => Array
        (
            [0] => Wed, 10-Feb-2021 03:05:45 GMT
        )

    [Max-Age] => Array
        (
            [0] => 63071999
        )

    [path] => Array
        (
            [0] => /
        )

    [domain] => Array
        (
            [0] => .facebook.com
        )

    [secure] => Array
        (
            [0] => 
        )

    [httponly] => Array
        (
            [0] => 
        )

)

Upvotes: 5

hiburn8
hiburn8

Reputation: 481

Really liked @Andrej's answer... but i wanted a function (not a class) and also something that could keep multiple cookies of the same name... which none of these answers can currently do as you cannot have an associative array with multiple same-name keys.

function CookieParser($str){
    $cookies = array();
    foreach(explode('; ',$str) as $k => $v){
        preg_match('/^(.*?)=(.*?)$/i',trim($v),$matches);
        array_push($cookies, array(trim($matches[1]), urldecode($matches[2])));
    }
   return $cookies;
}

$CP =  CookieParser($_SERVER['HTTP_COOKIE']);

Produces:

Array
(
[0] => Array
    (
        [0] => username
        [1] => admin
    )

[1] => Array
    (
        [0] => page
        [1] => 5
    )

[2] => Array
    (
        [0] => page
        [1] => 9
    )

)

Be careful processing multiple cookies with the same name though... don't accidentally validate value 1 but then use value 2.

Upvotes: 1

Andrej Pandovich
Andrej Pandovich

Reputation: 326

Some improvements, but not tokens!

Class CookieParser{
    public $cookies = array();
    function __construct($str){
        foreach(explode('; ',$str) as $k => $v){
            preg_match('/^(.*?)=(.*?)$/i',trim($v),$matches);
            $this->cookies[trim($matches[1])] = urldecode($matches[2]);
        }
    }
}

$CP = new CookieParser($_SERVER['HTTP_COOKIE']);
print_r($CP->cookies);

Upvotes: -1

Related Questions