Reputation: 326
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
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
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
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