有如下代码 setcookie.php
class Cookie{
protected $_key = "person";
protected $_val = "name:ball,sex:male";
public function set(){
$duration = 0;
$path = "/";
setcookie($this->_key, $this->_val, $duration, $path);
}
public function get(){
echo $_COOKIE[$this->_key];
}
}
我们先调用set(), 再调用get()。页面上输出
name:ball,sex:male
按说这是符合预期的。 但是使用chrome的debug工具查看cookie,发现person的值为
name%3Aball%2Csex%3Amale
在console中执行document.cookie,结果为
"person=name%3Aball%2Csex%3Amale"
也就是说,虽然php侧能设置并正常的取到cookie值,但是从浏览器或js侧看来,这个cookie是被编了码的。不方便js使用,也不方便人工排查问题时查看cookie。
查手册,发现setcookie的确是对cookie值进行了urlencode。怎么绕开呢?我们想到setcookie的本质就是在response header中加入Set-Cookie响应头,于是决定尝试直接用header方法。set()代码调整如下:
public function set(){
$str = sprintf("Set-Cookie:%s=%s;path=/", $this->_key, $this->_val);
header($str);
}
这时再从chrome侧查看cookie中的person值如下,并没有进行编码。
name:ball,sex:male
2中的方法虽然解决了cookie值被编码的问题,但是会不会带来风险呢? 答案是会的。比如,如果cookie中带了分号(http协议中,Set-Cookie用来分隔键值对的关键字),就会产生bug。
为了详细说明问题,我们先看下2中例子的response header(主要截取Set-Cookie部分)
Server: nginx/1.4.1
Set-Cookie: person=name:ball,sex:male;path=/
Transfer-Encoding: chunked
下面修改代码
将
protected $_val = "name:ball,sex:male";
改为
protected $_val = "name:ball;sex:male";
response header变为
Server: nginx/1.4.1
Set-Cookie: person=name:ball;sex:male;path=/
Transfer-Encoding: chunked
ball后的分号将person值打断,后面的sex:male;被协议解析为无法识别的键值对,因而忽略。
get()方法的输出及浏览器中看的person值也变为
name:ball
cookie值尽量简单,不含特殊符号,这样即使setcookie进行了urlencode也不会有什么变化。如果一定需要包含特殊字符,请注意避开协议保留字。