setcookie

(PHP 3, PHP 4, PHP 5)

setcookie -- 发送一个 cookie 信息

说明

bool setcookie ( string name [, string value [, int expire [, string path [, string domain [, bool secure]]]]] )

setcookie() 定义一个和其余的 HTTP 标头一起发送的 cookie。和其它标头一样,cookie 必须在脚本的任何其它输出之前发送(这是协议限制)。这需要将本函数的调用放到任何输出之前,包括 <html><head> 标签以及任何空格。如果在调用 setcookie() 之前有任何输出,本函数将失败并返回 FALSE。如果 setcookie() 函数成功运行,将返回 TRUE。这并不说明用户是否接受了 cookie。

注: 自 PHP 4 起,可以用输出缓存来在调用本函数前输出内容,代价是把所有向浏览器的输出都缓存在服务器,直到下命令发送它们。可以在代码中使用 ob_start()ob_end_flush() 来实现这样的功能,或者通过修改 php.ini 中的 output_buffering 配置选项来实现,也可以通过修改服务器配置文件来实现。

除了 name 外,其它所有参数都是可选的。可以用空字符串("")替换某参数以跳过该参数。因为参数 expire 是整型,不能用空字符串掉过,可以用零(0)来代替 。下面的表格对 setcookie() 的每一个参数都进行了解释。可以对照 Netscape cookie 规范以了解 setcookie() 的每一个参数的细节以及通过阅读 RFC 2965 了解 HTTP cookie 的工作方式。

表格 1. setcookie() 参数详解

参数说明举例
name Cookie 的名字。 使用 $_COOKIE['cookiename'] 调用名为 cookiename 的 cookie。
value Cookie 的值。此值保存在客户端,不要用来保存敏感数据。 假定 name 是 'cookiename',可以通过 $_COOKIE['cookiename'] 取得其值。
expire Cookie 过期的时间。这是个 Unix 时间戳,即从 Unix 纪元开始的秒数。换而言之,通常用 time() 函数再加上秒数来设定 cookie 的失效期。或者用 mktime()来实现。 time()+60*60*24*30 将设定 cookie 30 天后失效。如果未设定,cookie 将会在会话结束后(一般是浏览器关闭)失效。
path Cookie 在服务器端的有效路径。 如果该参数设为 '/' 的话,cookie 就在整个 domain 内有效,如果设为 '/foo/',cookie 就只在 domain 下的 /foo/ 目录及其子目录内有效,例如 /foo/bar/。默认值为设定 cookie 的当前目录。
domain 该 cookie 有效的域名。 要使 cookie 能在如 example.com 域名下的所有子域都有效的话,该参数应该设为 '.example.com'。虽然 . 并不必须的,但加上它会兼容更多的浏览器。如果该参数设为 www.example.com 的话,就只在 www 子域内有效。细节见 Cookie 规范中的 tail matching。
secure 指明 cookie 是否仅通过安全的 HTTPS 连接传送。当设成 TRUE 时,cookie 仅在安全的连接中被设置。默认值为 FALSE 01

当 cookie 被设置后,便可以在其它页面通过 $_COOKIE$HTTP_COOKIE_VARS 数组取得其值。需要注意的是,autoglobals$_COOKIE 形式适用于 PHP 4.1.0 或更高版本。而 $HTTP_COOKIE_VARS 则从 PHP 3 起就可以使用。Cookie 的值也会被保存到 $_REQUEST 数组中。

注: 如果 PHP 的选项 register_globals 被设为 on 的话,cookie 的值仍然会被斌到变量内。在下面的例子中,$TestCookie 会被注册,但是仍然推荐使用 $_COOKIE 数组。

常见缺陷:

  • Cookies 不会在设置它的本页生效,要测试一个 cookie 是否被成功的设定,可以在其到期之前通过另外一个页面来访问其值。过期时间是通过参数 expire 来设置的。可以简单地使用 print_r($_COOKIE); 来调试现有的 cookies。

  • Cookie 必须用和设定时的同样的参数才能删除。如果其值一个空字符串,或者是 FALSE,并且其它的参数都和前一次调用 setcookie 时相同,那么所指定名称的 cookie 将会在远程客户端被删除。

  • 由于把 cookie 的值设为 FALSE 会使客户端尝试删除这个 cookie,所以要在 cookie 上保存 TRUEFALSE 时不应该直接使用 boolean 值,而应该用 0 来表示 FALSE,用 1 来表示 TRUE

  • 可以把 cookie 的名称设置成一个数组,但是数组 cookie 中的每个元素的值将会被单独保存在用户的系统中。考虑使用 explode() 函数用多个名称和值设定一个 cookie。不推荐将 serialize() 用于此目的,因为它可能会导致一个安全漏洞。

在 PHP 3 中,在同一个 PHP 脚本中多次使用 setcookie() 来设置 cookie,将会按照倒序的方式来分别执行,如果想要在插入另外一个 cookie 之前删除一个 cookie,要把插入放到删除之前。自 PHP 4 起,多次调用 setcookie() 则是按照顺序来执行的。

下面一些例子说明了如何发送 cookie:

例子 1. setcookie() 发送例子

$value = 'something from somewhere';

setcookie("TestCookie", $value);
setcookie("TestCookie", $value,time()+3600);  /* expire in 1 hour */
setcookie("TestCookie", $value,time()+3600, "/~rasmus/", ".utoronto.ca", 1);

注意 cookie 中值的部分在发送的时候会被自动用 urlencode 编码并在接收到的时候被自动解码并把值赋给与自己同名的 cookie 变量。如果不想这样并且在使用 PHP 5 的话,可以用 setrawcookie() 来代替。下面这个简单的例子可以得到刚才所设定的 cookie 的值:

<?php
// 输出单独的 cookie
echo $_COOKIE["TestCookie"];
echo
$HTTP_COOKIE_VARS["TestCookie"];

// 另一个调试的方法就是输出所有的 cookie
print_r($_COOKIE);
?>

要删除 cookie 需要确保它的失效期是在过去,才能触发浏览器的删除机制。下面的例子说明了如何删除刚才设置的 cookie:

例子 2. setcookie() 删除例子

// 将过期时间设为一小时前
setcookie("TestCookie", "", time() - 3600);
setcookie("TestCookie", "", time() - 3600, "/~rasmus/", ".utoronto.ca", 1);

也可以通过在 cookie 名称中使用数组符号来设定数组 cookie,可以设定多个 cookie 作为数组单元,在脚本提取 cookie 时所有的值都放在一个数组种:

例子 3. setcookie() 中使用数组的例子

<?php
// 设定 cookie
setcookie("cookie[three]", "cookiethree");
setcookie("cookie[two]", "cookietwo");
setcookie("cookie[one]", "cookieone");

// 刷新页面后,显示出来
if (isset($_COOKIE['cookie'])) {
    foreach (
$_COOKIE['cookie'] as $name => $value) {
        echo
"$name : $value <br />\n";
    }
}
?>

上例将输出:

three : cookiethree
two : cookietwo
one : cookieone

注: 下面的一些 RFC 也具参考价值:RFC 2109RFC 2695

注意 expire 参数的接受的是 Unix 时间戳,而不是日期格式 Wdy, DD-Mon-YYYY HH:MM:SS GMT,这是因为 PHP 在内部进行了转换。

expire 是与客户端的时间相比较,和服务器时间可能不同。

注: 向 Microsoft Internet Explorer 4 Service Pack 1 不能正确处理设定了 path 的 cookie。

Netscape Communicator 4.05 及 Microsoft Internet Explorer 3.x 不能正确处理没有设定 path 和 time 的 cookie。

参见 header()setrawcookie() 和本手册中的 cookie 一章。


add a note add a note User Contributed Notes
shitdeposit at hotmail dot com
22-Oct-2006 07:34
An undocumented "feature" of setcookie:

Example:
setcookie( 'delete_me', '', time() - 1000, '/', '', '' );

The setcookie function will silently change the '' cookie value to 'deleted' and override your expiry time to time() - 1 year.

I only noticed this when the computer I was testing on had it's date set incorrectly to the > 1 year in the past.  The computer connects to the server which has it's date correctly set and thus the cookie on the client doesn't delete but stores the value "deleted" instead.
jlevene at gmail dot com
19-Oct-2006 11:05
It's just too "dangerous" to use setcookie() directly, since so many things have to be just right if you ever want to be able to delete the cookie (or even use it again, sometimes). 

Here's a very simple little function to painlessly set and delete a cookie.  I tried to incorporate all the "tricks" the people below mentioned, like stripping the "www." and any port number from the domain, and making the cookie available to any script on the server.

This function is ready for use by any newbie.  Don't be afraid; it's really as easy as it looks!  Just use a value of "" when you want to delete the cookie.  The only things you have to know to use this are:

  1) You need to (un)set any cookies you set BEFORE you
     produce any output, including <html> or a blank line.

  2) A "session" cookie is one that expires when the browser
     window is closed.

This function and the 2 above points are intended to make it unnecessary for a newbie to wade through all this doc to "just use cookies."  The example is for a 1-day cookie, but it should be obvious how to use different durations (bear in mind that the expiration is done by the browser which may be in a different timezone).

(The $host is global so it only has to be calculated once, in case you have several cookies.)

<?php
  $cookieExpire
= time() + (60*60*24);  // 1 day ("= 0" for Session)
...
 
$host = preg_replace('/^[Ww][Ww][Ww]\./', '',
          
preg_replace('/:[0-9]*$/', '', $_SERVER[HTTP_HOST]));

  function
mySetCookie($cName, $cValue)
   {
   global
$cookieExpire, $host;

   if (
strcmp($cValue, "") == 0)
     {
    
setcookie($cName, "", 1, "/", ".$host", FALSE);    // Delete
    
}
   else
     {
    
setcookie($cName, $cValue, $cookieExpire, "/", ".$host", FALSE);
    
$_COOKIE[$cName] = $cValue;        // Pretend it's already set
    
}
   }
?>
geoff at spacevs dot n0spm dot com
17-Oct-2006 09:55
I had two sites for the same domain under different names that I wanted the cookie to be maintained between:

forum.*
www.*

Because forum is running a phpbb2 forum, I did not want to modify the forum code to set the domain in the cookie, so to override the default behaviour of using the entire domain name, I wrote the following code and pre-pended it to the output using the php_auto_prepend setting.

<?PHP
  $domain
= 'example.com';
  function
fix_cookies($data) {
   global
$domain;
   foreach(
headers_list() as $value) {
     list(
$name, $value) = split(': ', $value, 2);
     if (
$name == 'Set-Cookie')
      
header("$name: $value; domain=.$domain");
   }
   return
$data;
  }

 
ob_start('fix_cookies');
?>

Now the domain for all cookies is set to .example.com and will now work across subdomains.

If there is a better way, please let me know.
abdullah dot a at gmail dot com
18-Sep-2006 02:20
Now, for a non-buggy setcookie that doesn't vanish on Location: redirects. In fact, I suggest you use this instead of the built-in setcookie() function. It also supports "HttpOnly". Usage is the same as the original setcookie() function:

<?php

function set_cookie($name, $value = '', $expires = 0, $path = '', $domain = '', $secure = false, $http_only = false)
{
  
header('Set-Cookie: ' . rawurlencode($name) . '=' . rawurlencode($value)
                         . (empty(
$expires) ? '' : '; expires=' . gmdate('D, d-M-Y H:i:s \\G\\M\\T', $expires))
                         . (empty(
$path)    ? '' : '; path=' . $path)
                         . (empty(
$domain)  ? '' : '; domain=' . $domain)
                         . (!
$secure        ? '' : '; secure')
                         . (!
$http_only    ? '' : '; HttpOnly'), false);
}

To make a "raw" version of this, get rid of the rawurlencode functions.
Lemonrock Editor
20-Aug-2006 04:23
If you try to call setcookie() before issuing a 302 via a header("Location:..."), the cookie will not be sent as part of the 302 response. To get the cookie in the header, use the header() function instead of setcookie(), i.e. header("Set-Cookie: cookiename=cookievalue; expires=18-Oct-2008 GMT; path=/; domain=.www.domain.com");

Note also that PHP setcookie() formats the date without dashes, which is accepted by major browsers, but is not in accordance with the Netscape cookie spec, which specifies the date with dashes.
adruff at gmail dot com
06-Aug-2006 01:14
If you intend to use persistent cookies (vice session cookies that are deleted when the browser is closed) be aware:
1) Firefox appears to require that you include all paramaters, or it will ignore the expiration and treat the cookie as a session cookie
2) My version of firefox (1.5.0.6) defaults to 'keep cookies until i close firefox' , which essentially makes every cookie a session cookie. This of course sucks for devs, but i suppose is supposed to be a security feature for the end user. If the user wants to configure firefox to respect the expiration date and retain cookies beyond the session, the user must change it to 'keep cookies until they expire'.
Charles Martin
24-Jul-2006 07:58
If a user accesses a page by using www.example.com/page.php and this page issues a cookie, and if the user tomorrow accesses the same page by using example.com/page.php, your page will not be able to retrieve the previous cookie because it was set for "www.example.com" and will set a new cookie for "example.com".

To avoid this kind of problems, you can use a solution like this:

<?php

// Get domain without www and any port (if present)
$domain = getDomain();

// Set expiration (30 days)
$expire = time() + (86400*30);

// Set the cookie
setcookie('cookieName', 'cookieValue', $expire, '/', $domain);

function
getDomain() {
   if ( isset(
$_SERVER['HTTP_HOST']) ) {
      
// Get domain
      
$dom = $_SERVER['HTTP_HOST'];
      
// Strip www from the domain
      
if (strtolower(substr($dom, 0, 4)) == 'www.') { $dom = substr($dom, 4); }
      
// Check if a port is used, and if it is, strip that info
      
$uses_port = strpos($dom, ':');
       if (
$uses_port) { $dom = substr($dom, 0, $uses_port); }
      
// Add period to Domain (to work with or without www and on subdomains)
      
$dom = '.' . $dom;
   } else {
      
$dom = false;
   }
   return
$dom
}
?>

This sets the cookie's domain without the "www" (and any port) and you can access the cookie from any page on the domain and also from subdomains.

If someones find any problem with this solution, please post a comment.

HTH
kael dot shipman at DONTSPAMIT! dot gmail dot com
14-Jul-2006 09:20
As I've interpreted it, only the cookies with a path set to the server name to which the request was made are available to the PHP superglobal $_COOKIE. If all were available, you'd probably have a huge security hole. Therefore, if you have a cookie on your computer set by www.cha.com, and you then visit www.fah.com, www.fah.com cannot see that your www.cha.com cookie exists (at least through the PHP $_COOKIE superglobal).

I've tested this in Safari and Firefox on Mac. I can't imagine that it would be any different on other browsers, as I thought that was specifically what the path parameters were for.
Eric Mueller
09-Jul-2006 01:44
for some weird reason, on Safari, set_cookie doesn't work unless you specify all parameters. my code was flopping until I added a '0' at the end (for the 'secure' flag) and now, the cookie is being stored. weeeeird.
Olivier Jacquet
06-Jul-2006 11:09
To respond to the UTF-8 encoding post:

"Although I wish it were different, UTF-8 encoded scripts start output at the php tag declaration."

This is because many editors put a BOM (Byte Order Marker) at the beginning of a UTF-8 file. These 2 bytes also get send to the browser.

To solve this you can either remove the bytes from the file with a binary editor or turn on output_buffering.

In relation to setting cookies:
If you want to set cookies that need to be remembered forever you can use the PHP_INT_MAX constant.

setcookie("cookieName", $cookieValue, PHP_INT_MAX);
mrvanes at gmail dot com
06-Jun-2006 10:22
I just discovered that IE6 (6.0.2800.1106 at least) seems to have difficulties accepting cookies when the subdomain part of the URL is longer than 10 characters. This applies to automatic sessionid's as well (that's how I found out). So: Setting a cookie on a site named http://examples.test.com/ will work, doing the same on http://my_examples.test.com will fail (or at least, failed for me).

Tested it by snooping the outgoing traffic from the client: the GET request lacks the COOKIE: header in the last case. A closer inspection revealed that the cookie was never accepted in the first place: when the subdomain is longer than 10 characters, no cookiefile is created in the temporary internet files directory. Temporary cookies (like the PHP sessionid) have the same problem.

Firefox and Konqueror have no problem with this.

[Editor's note:  Your comment is wrong. It is not the length of the hostname but the use of the underscore (_) char within it which makes IE (silently) freak out. The use of underscores in hostnames is an RFC violation and may very well not be supported at all by a browser, proxy or any http client.]
gareth at gw126 dot com
05-Jun-2006 10:38
You can use cookies to prevent a browser refresh repeating some action from a form post... (providing the client is cookie enabled!)

//Flag up repeat actions (like credit card transaction, etc)
if(count($_POST)>0) {
   $lastpost= isset($_COOKIE['lastpost']) ? $_COOKIE['lastpost'] : '';
   if($lastpost!=md5(serialize($_POST))) {
       setcookie('lastpost', md5(serialize($_POST)));
       $_POST['_REPEATED']=0;
   } else {
       $_POST['_REPEATED']=1;
   }
}

//At this point, if $_POST['_REPEATED']==1, then  the user
//has hit the refresh button; so don't do any actions that you don't
//want to repeat!

Hope that helps :)

Gareth
Ahmet Antmen ahmetantmen_at_msn_dot_com
19-May-2006 05:05
You can be sure about the cookie files contents weren't changed.

<?php

$Seperator
= '--';
$uniqueID = 'Ju?hG&F0yh9?=/6*GVfd-d8u6f86hp';
$Data = 'Ahmet '.md5('123456789');

setcookie('VerifyUser', $Data.$Seperator.md5($Data.$uniqueID));

if (
$_COOKIE) {
  
$Cut = explode($Seperator, $_COOKIE['VerifyUser']);
   if (
md5($Cut[0].$uniqueID) === $Cut[1]) {
      
$_COOKIE['VerifyUser'] = $Cut[0];
   } else {
       die(
'Cookie data is invalid!!!');
   }
}

echo
$_COOKIE['VerifyUser'];

?>

Create a unique id for your site and create a hash with md5($Data.$uniqueID). Attacker can understant that it must be re-hash after change cookie content.
But doesn't. Because cannot guess your unique id. Seperate your hash and data with seperator and send that cookie. Control that hash of returned value and your unique id's is same returned hash. Otherwise you have to stop attack. Sorry for my poor english!
matt at mattsoft dot net
05-May-2006 08:47
using time()-1 to delete (expire) a cookie only works if the client's clock is set exact. my testing showed some weird results with my clock set 1 second or so behind the server. 1 day or even just a few seconds ahead of the server's time and the cookie doesn't expire when it's suposed to.

my test:
setcookie('k',$k+1,time()-1);
echo $k;

setting the expire time to 0 makes it a browser session cookie, lasting forever, until the browser is closed. but setting the expire time to 1 is the lowest timestamp possible and is most likely to expire the cookie without any problems.

my fix:
setcookie('k',$k+1,1);
echo $k;

this is my theory. I'm not sure why no one else has thought of this problem or solution, and I'm still testing, but please email me your questions or comments.
Brian
11-Mar-2006 03:56
Firefox is following the real spec and does not decode '+' to space...in fact it further encodes them to '%2B' to store the cookie.  If you read a cookie using javascript and unescape it, all your spaces will be turned to '+'.
To fix this problem, use setrawcookie and rawurlencode:

setrawcookie('cookie_name', rawurlencode($value), time()+60*60*24*365);

The only change is that spaces will be encoded to '%20' instead of '+' and will now decode properly.
php at macros dot org
03-Mar-2006 09:19
I've just been fighting with an issue with setting cookie paths that contain the underscore [_] and dash [-] characters on IIS6.

This issue is a known "feature" that was added to IIS6 to help prevent cross-site scripting vunerabilities (See: http://tinyurl.com/n5hq9).

Basically, when IIS6 is asked to go to a URI that doesn't contain a trailing slash, it will "courtesy redirect" to the URI with a trailing slash, but in doing so will encode underscores and dashes into their ASCII codes (%5F and %2D respectively).

Now, while this normally wouldn't be an issue, when a cookie is set by PHP using a defined path the unencoded version is used for the path. PHP does not know that the path is encoded, as IIS6 decodes it before it's given to the script (I assume).

Example:
URI = http://somedomain.com/some_directory
IIS redirects = http://somedomain.com/some%5Fdirectory/
PHP sees: http://somedomain.com/some_directory/
PHP_SELF: /some_directory/script.php

So, setting the path: path="/some_directory/"
but browser sees: path="/some%5Fdirectory/" which are not equal, so therefore the paths aren't the same and the cookie isn't retrieved.

Normally this wouldn't be an issue: simply don't specify a path & the browser handles it for you; or don't use underscores or dashes in the URI; or specify the URI with the trailing slash. But when used to lock down sessions to a specific directory (which might have those characters) this becomes a big problem (default session path is /).

The solution is to also set a cookie using the session information (name, value & other parameters) for the encoded version of the path, then the same session will be used for either version of the path.

Cameron.
simon at go4 dot com dot au
27-Feb-2006 07:26
For some reason setting a cookie path without a domain does not appear to work, eg.

<?php
setcookie
('cookieName', 'cookieValue', time()+3600, '/');
?>

The path remains the default; 'The default value is the current directory that the cookie is being set in'.  So you cannot access the cookie from outside that sub-directory.

However if you set the cookie with a blank domain, eg.

<?php
setcookie
('cookieName', 'cookieValue', time()+3600, '/', false);
?>

Then the path override will work and you can access it from any path in the domain using the code in the above example ('/').

NOTE: setting the domain to false will cause it to be filled with the default value.
r dot namakshenas at gmail dot com
06-Feb-2006 04:38
if you are using Dreamweaver 8 and the file encoding is set to UTF and BOM has been selected to define Byte Order, a disturbing "\377" is added to the start of the page which causes failure for cookies, sessions and headers and is not displayed in Dreamweaver. You will need to edit the file again using vi or similar editors and delete "\377". It took me several working hours.
simplifiedwebsystems
27-Jan-2006 02:36
I needed to access COOKIE data before the next page load, so here's the solution:

Accessing PHP $_COOKIE value before new page load or page refresh:

<?PHP
//Set the cookie and set the $_COOKIE value
//This works because $_COOKIE is a variable
setcookie('key', 'value', time()+60*60*24*30);
if (!isset(
$_COOKIE['key'])) {
  
$_COOKIE['key'] = 'value';
}

//The $_COOKIE['key'] = 'value' will now be available on this intantiation for immediate use.
?>
useradd at hotmail dot com
14-Dec-2005 09:55
ok, I want to contribute with a simply calculator that uses cookies for saving the results... Sure you'll find so basic.

<?php
  
include("xmlvalido.php");
   if(isset(
$_COOKIE["ACUMULADO"])&&(!isset($_POST["CE"]))){
      
$valor = $_COOKIE["ACUMULADO"];
       if(
$_POST["op"]=="1"){
          
$valor_nuevo=$valor+$_POST["dat"];
       }
       if(
$_POST["op"]=="2"){
          
$valor_nuevo=$valor-$_POST["dat"];
           }
       if(
$_POST["op"]=="3"){
          
$valor_nuevo=$valor*$_POST["dat"];
           }
       if(
$_POST["op"]=="4"){
          
$valor_nuevo=$valor/$_POST["dat"];
           }
          
setcookie("ACUMULADO",$valor_nuevo);
       echo
abre_web("Mi calculadora");
       }else{
          
setcookie("ACUMULADO",0);
          
$valor_nuevo=0;
          
$valor=0;
           echo
abre_web("Mi calculadora");
           }
  
?>
    <br/>
   <p> Valor calculado hasta ahora: <?php
  
echo $valor_nuevo;?></p>
   <form action="calculadora.php" method="POST">
   <p><input type="radio" name="op" value="1">Sumar</p>
   <p><input type="radio" name="op" value="2">Restar</p>
   <p><input type="radio" name="op" value="3">Multiplicar</p>
   <p><input type="radio" name="op" value="4">Dividir</p>
   <p>Valor: <input type="text" name="dat"></p>
   <input type="submit" value="Calcular" name="Calcular">
   <input type="submit" value="CE" name="CE">
   </form><?php
  
echo cierra_web();

?>

Note I've created a file called "funciones.php" that provides my php all the headers for making valid xhtml webs! Here I'm posting it...

...xmlvalido.php

<?php
  
function abre_web($titulo)
   {
      
$salida=<<<TXT
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="sp" lang="sp">
<head>
<title>$titulo</title>
</head>
<body>
<p>
TXT;
   return
$salida;
   }
   function
cierra_web(){
      
$salida=<<<TXT
</p>
</body>
</html>
TXT;
   echo
$salida;
   }
   function
enlace($texto, $url){
      
$salida=<<<TXT
        <a href="$url">$texto</a>
TXT;
   return
$salida;
   }
?>
no at spam dot com
11-Dec-2005 05:47
To workaround the header issue with php/IIS I opened the php.ini file and enabled the following option.

; Output buffering allows you to send header lines (including cookies) even
; after you send body content, at the price of slowing PHP's output layer a
; bit.  You can enable output buffering during runtime by calling the output
; buffering functions.  You can also enable output buffering for all files by
; setting this directive to On.  If you wish to limit the size of the buffer
; to a certain size - you can use a maximum number of bytes instead of 'On', as
; a value for this directive (e.g., output_buffering=4096).
output_buffering = On

All cookies work fine now :)
marc at NOSPAM dot grootkoerkamp dot net
14-Oct-2005 03:59
If you want to secure your cookies with the HttpOnly attribute (only supported by IE6. see http://msdn.microsoft.com/workshop/author/dhtml/httponly_cookies.asp ) you could create your own setcookie function by using the following function:
<?php

function mgk_setcookie($sName,$sValue,$iExpire=false,
  
$sPath="",$sDomain="",$bSecure=false,$bHttpOnly=true) {
  
$sHeader = "Set-Cookie: $sName=$sValue";
   if (
$sPath) { // optional
      
$sHeader .= "; Path=$sPath";
   }
   if (
$iExpire !==false || $iExpire !== 0 ) {
  
// 0 check because of php setcookie function which
   // isn't entirely compattible with the rfc.
      
$sHeader .= "; Max-Age=$iExpire";
   }
   if (
$sPath) { // optional
      
$sHeader .= "; Path=$sPath";
   }
   if (
$sDomain) { // optional
      
$sHeader .= "; Domain=$sDomain";
   }
   if (
$bSecure) { // optional
      
$sHeader .= "; Secure";
   }
   if (
$bHttpOnly) { // IE6 only attribure
      
$sHeader .= "; HttpOnly";
   }
  
$sHeader .= "; Version=\"1\""; // not sure if i use the right version
  
header($sHeader);
}

?>
simon at ruderich dot com
02-Aug-2005 05:21
If you want to delete a session cookie, you can do it with this code:

<?php
  session_start
();

 
// many code

 
$sessionName = session_name();
 
$sessionCookie = session_get_cookie_params();

 
session_destroy();

 
setcookie($sessionName, false, $sessionCookie['lifetime'], $sessionCookie['path'], $sessionCookie['domain'], $sessionCookie['secure']);
 
?>

This works also well if the session cookie params or the session name were changed.
lharkleroad at gmail dot com
19-Jul-2005 06:12
In response to Snupha's note, one way to get the desired result on the server side, and therefore work around the user's local date and time, is to store your session information in a sessions table in your database.

For example:

1. Create a sessions table in your favorite db with a 32 char sessionid column, modifed and created datetime columns, and any other columns that you want to associate with the session such as a userid.

2. In your PHP script, create a variable containing the 32 char unique ID for your session, and store the result in a sessionid cookie that expires at the end of the browser session, and also insert it in a new row in your sessions table.

3. When your scripts check for the existance of the sessionid cookie, if a value is returned, they would check against the sessions table in your database to find the matching ID, updating the modified datetime for that record to keep the session active.  If the server time elapsed since the record was modified exceeds your timeout constraints, you can force the user to reauthenticate and issue them a new session ID if desired.

4. If you require a login to some part of your site, you can also update the session record with the userid to link the session to the user.

I decided to use this method on a recent project where both PHP and ColdFusion needed to access the same datasource for different applications.  The result is a language-independent session in which multiple web servers on the same domain (but potentially different sub-domains) can know when the user has logged in and allow them to continue the session on the other server.  Otherwise, using the sessions built in to PHP and ColdFusion would require separate logins when switching between applications programmed in the two languages.

Going a little further with this, you can then report stats on the sessions table for the average session length by comparing the created and modified columns, or get a session history for a particular user.

I hope this helps a few people.  Have fun!
p dot nowicki at hero dot mac dot edu dot pl
08-Jul-2005 12:03
It takes me some time to solve a problem with PHPSESSID propagation in URL's when --enable-trans-sid is on.
The problem was that even when user has cookies turned on, sometimes PHPSESSID has been shown in URL. I used subdomains on my page to make the URL's more user-friendly.
It turn out that the session cookie was automaticly set to main domain address with "www" (ex. "www.domain.top_domain"), when session.cookie_domain was set to (default value) "no value".
Adding

<?php
ini_set
("session.cookie_domain", ".domain.top_domain");
?>

helped, and now when session cookie is set, all URL's are clear from the nasty session id.
Right now it seems to be pretty logical, but sometimes you can easly forget about something like that.
James
23-Jun-2005 01:34
"If you cannot get a cookie to stick when using an expiration date..."

Also the session.cookie_lifetime parameter in php.ini seems to cause the problem. If this value is set to zero, the cookie will always expire when you close the browser even though you specify expiration time in set_cookie().  After setting session.cookie_lifetime to some nonzero value, my cookies stuck. :)
(Apache 1.3.33, PHP 5.0.4)
nahtan at email dot com
11-Jun-2005 07:35
I tried for an hour to figure out how to have a session cookie (one that deletes when the browser is closed) with a path.

If you specify 0 as the expire parameter, the cookie will be a session cookie. eg:

setcookie("test", "value", 0, "/somepath/");
Snupha
08-Jun-2005 04:24
If you cannot get a cookie to stick when using an expiration date - check that local computer's time and time zone!

I spent hours trouble-shooting my code when I realized that the computer time on a client's computer was 7 years into the future which automatically expired the cookie before it could be used! Go figure, I'm going to look now for a work around that I can do on the server-side.
terry at scribendi dot com
08-May-2005 10:07
A few comments have suggested using serialize() to set object or array data into a cookie.  There are a couple of reasons to be carefull with that technique:

Security: If the cookie is human readable, then it is also fairly easy for end users to play around with it.  Wrapping your cookie setting and getting in an encryption routine will prevent tampering, and make sure that your cookies don't make any sense to any client-side exploits or other sites they get sent to thanks to browser bugs.

Bulk: If you serialize even a fairly simple class, then you get a lot of data.  Large cookies will make browser requests fat and slow, and some browsers have a limit on cookie size, so think about what data you really need to persist, and create __sleep() and __wakeup() methods to package the data into the shortest possible form.  You can get better and faster results when you write your own __sleep() and __wakup() to implode() or pack() your data, than by using zlib compress() on the serialized object.
support at duggen dot net
21-Apr-2005 01:21
Addition to "Carl V"s note. Thanks for the note.

Here is a way to delete alle cookies and cookie arrays from your domain.

$cookiesSet = array_keys($_COOKIE);
for ($x = 0; $x < count($cookiesSet); $x++) {
   if (is_array($_COOKIE[$cookiesSet[$x]])) {
       $cookiesSetA = array_keys($_COOKIE[$cookiesSet[$x]]);
       for ($c = 0; $c < count($cookiesSetA); $c++) {
           $aCookie = $cookiesSet[$x].'['.$cookiesSetA[$c].']';
           setcookie($aCookie,"",time()-1);
       }
   }
   setcookie($cookiesSet[$x],"",time()-1);
}
scissorjammer at hotmail dot com
09-Apr-2005 03:18
As per Sijmen Ruwhof's comment:

It's not that the domain requires a dot as much as it requires a fully qualified domain name.  Setting it to false is a valid workaround, as it will default to whatever domain the page is being accessed at.  If a fully qualified domain name is unavailable, consider editing your hosts file on both the client and server to have a common domain name to use for the cookie.
Carl V
08-Apr-2005 11:29
If you want to delete all the cookies set by your domain, you may run the following:

<?php
$cookiesSet
= array_keys($_COOKIE);
for (
$x=0;$x<count($cookiesSet);$x++) setcookie($cookiesSet[$x],"",time()-1);
?>

Very useful when doing logout scripts and the cookie name may have changed (long story).
jonathan at jonathanopie dot com
23-Mar-2005 06:00
I've found that when using header("Location: <destination page>") type 302 redirect, that cookie information in the header will be silently dropped if the <destination page> path includes any non-alphanumeric (except the allowed "-" and ".") characters. That had been pointed out above, and is documented with Microsoft as by design...

http://support.microsoft.com/default.aspx?scid=kb;EN-US;316112

...but what is not made clear, is that the rule seems to include file and directory names too!

Also, I've had unpredictable results when the <destination page> filename begins with ANY character other than strictly alphanumeric characters, even the otherwise allowed "-" and "." characters; and/or if the <destination page> is in a different directory.
Alchaemist
15-Mar-2005 07:36
setcookie + header Location + IIS 5 = Trouble

It took me a long time to figure out what was causing a missing cookie in one system while it worked perfectly in another...

See this one: http://support.microsoft.com/kb/q176113/

In short, this WILL NEVER WORK IN IIS 5:
<?php
header
("Pragma: no-cache");
header('Location: http://www.example.com/');
setcookie('AA','1',0,'/');
setcookie('BB','2',time() + 24 * 3600,'/');
?>

You will ONLY get the Location Header, everything else will be "cut out" by IIS 5 CGI implementation.

Solutions:
1- Migrate to Apache/IIS6/Whatever
2- Use a Non Parsed Header Script (nph-*.php)
3- Try with header('Refresh: 0; $URL');

I hope this helps somebody not to spend hours knocking his/her head.

Alchaemist
dear_grommet at reverse dot liamtoh dot moc
05-Mar-2005 03:41
When using cookies to store PHP variables with serialize(),
I found that I also needed to use stripslashes().

Here is an example of code that does what the PHP web site
does - remembering your last search options at the top of the
page.

I would initialise my variable $cookie_data to whatever I wanted,
and I use an array, eg:

<?php
// user options held in persistant cookies

$cookie_data['hist_days'] = 15;
$cookie_data['test1'] = "a string test $ ; \" & ";
$cookie_data['test2'] = 0;

$cookie_data_expire = 60 * 60 * 24 * 60;
?>

And then this code, run at the start of the script, after the
session_start(), will store/restore this cookie data...

<?php
 
// restore user options

 
if (isset($_SESSION['cookie_data']))
  {
  
// from session data

  
$cookie_data = $_SESSION['cookie_data'];
  }
  else
  {
  
// from cookie

  
if (isset($_COOKIE['cookie_data']))
   {
    
$string = $_COOKIE['cookie_data'];
    
$string = stripslashes($string);
    
//$string = gzuncompress($string);
    
$cookie_data = unserialize($string);
   }
  }
?>

NB. I found that for such a small amount of data, the gzcompress()
made the string bigger.

The idea is that if the $_SESSION data is available, then it takes
precedence, but if not, then it will re-load the last values from a
cookie that was stored (for the last 60 days).

Then you need to get any user changes for any values and copy
them back into the $_SESSION data.
Then set both the $_SESSION and cookie data., eg:

<?php
 
// get new user options

 
$cookie_data['test2']++;
 
$_SESSION['cookie_data'] = $cookie_data;

 
// store in cookie

 
$string = serialize($cookie_data);
 
//$string = gzcompress($string, 9);
 
setcookie('cookie_data', $string, time()+$cookie_data_expire, '/');

 
// store in session

 
$_SESSION['cookie_data'] = $cookie_data;
?>
Danny Lin
20-Feb-2005 12:37
I have XP Home and IE6. I tried the example 3:

<?php
// set the cookies
setcookie("cookie[three]", "cookiethree");
setcookie("cookie[two]", "cookietwo");
setcookie("cookie[one]", "cookieone");

// after the page reloads, print them out
if (isset($_COOKIE['cookie'])) {
   foreach (
$_COOKIE['cookie'] as $name => $value) {
       echo
"$name : $value <br />\n";
   }
}
?>

And it worked. However, when I added expiration info, it stopped working. Then, I noticed that it might be related to the time() on my server (hosted in Texas). With the debugging code below, I found out I have to add two hours. (You will need to "fresh" the browser to get the cookies set.)

<?
for ($i = 0; $i < 10; $i++) {
  
$time[$i] = time();
  
setcookie("time_report[$i]",$time[$i], $time[$i]+3600*$i);
}

print_r($_COOKIE);
?>
sikon at gorodok dot net
09-Feb-2005 11:13
The documentation says, 'Consider explode() or serialize() to set one cookie with multiple names and values'. It means implode(), of course, since it is implode() that constructs a string from an array, and explode() splits a string into an array.
Jos Enrique Serrano Expsito
05-Feb-2005 04:36
For listing all the cookies readed in the client machine.- echo sHTTP_COOKIE_VARS();
<?php
  
function  sHTTP_COOKIE_VARS() {
     global 
$HTTP_COOKIE_VARS; // $HTTP_COOKIE_VARS  is not exist in a function
    
$sEcho = '';
    
$iCookies = 0;
     ForEach(
$HTTP_COOKIE_VARS As $vIndex  => $vValue  ) {
    
$iCookies++;
     if(
is_array( $vValue ) ) {
       foreach    (
$vValue    as $vIndex2 => $vValue2 ) {
      
$sEcho .= '$HTTP_COOKIE_VARS[\'<b>'.$vIndex."</b>']['".$vIndex2.'\'] == "'.
                
$vValue2.'"<br>';
       }
     } else {
      
$sEcho .= '$HTTP_COOKIE_VARS[\'<b>'.$vIndex.'</b>\'] == "'.
                
$vValue.'"<br>';
     }
     }
    
$sEcho .= $iCookies.' cookies listed.';
     return
$sEcho; ///
  
}
?>
do not spam M. Kristall at MKP
25-Jan-2005 02:18
The "bug" that prevents cookies from working properly with Windows XP and Internet Explorer 6 seems to be a more strict following of the cookie specifications. This part in particular:

Only hosts within the specified domain can set a cookie for a domain and domains must have at least two (2) or three (3) periods in them to prevent domains of the form: ".com", ".edu", and "va.us". Any domain that fails [sic] within one of the seven special top level domains listed below only require two periods. Any other domain requires at least three. The seven special top level domains are: "COM", "EDU", "NET", "ORG", "GOV", "MIL", and "INT".

localhost should not work. Neither should localhost.localdomain. Though, it may be a bug that 127.0.0.1 does not work, even despit the fact that 127.0.0.1 is not a domain name.
glillibridge at yahoo dot com
09-Jan-2005 03:27
mleer at sp dot nl 19-Dec-2002 02:50 had a very similar problem to my own.

My page is being called from a client url as part of a 100% frame so the url would look correct to the user (reason: PR). My cookies stopped working.

mleer suggested sending the following header:
header('P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"');

Unfortunately it did not work for me. I found the below header command at http://www.sitepoint.com/article/p3p-cookies-ie6/2

It worked for me.

header('P3P: CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA"');

Good luck.
Sijmen Ruwhof
03-Jan-2005 04:41
My experience with setting cookies, a must read for every developer that doenst want to spend his time debugging cookies..

- if your're using windows XP with Internet Explorer, having a local webserver running and you're setting a cookie in you script, don't set the domain name! Use false instead. Windows XP in combination with Internet Explorer (with Firefox your cookie will be set successful) will not set a cookie when the domain name points to localhost. Examples:

<?
setcookie
('test', 'value', false, '/', 'localhost', 0); // wil not work in WinXP with IE, but will work in WinXP with Firefox
?>

Someone noted already this bug in WinXP + IE, but said that the domainname should have a dot in it. That is totally untrue (i've tried a domainname with a dot in it that points to 127.0.0.1). I think that the problem persist that the domainname may not be pointing to 127.0.0.1 so the solution would be:

<?php
setcookie
('test', 'value', false, '/', false, 0); // works
?>

- be sure that the 4th argument (path) always ends with a slash '/'

- be sure that the expire time is false or a timestamp, 0 is not the value for a session cookie (false is the value).

-- Sijmen Ruwhof
christophe at publicityweb dot com
28-Dec-2004 01:46
About the IIS bug (cookies removed in CGI if 302 Redirect)
reported by shrockc (2003-12-23), here's the correct URL.

BUG: Set-Cookie Is Ignored in CGI When Combined With Location
http://support.microsoft.com/kb/q176113/
adam at metrovanlines dot com
24-Mar-2004 07:47
This limitation is mentiones in the <a href=http://wp.netscape.com/newsref/std/cookie_spec.html> Netscape HTTP Cookies Preliminary Specification </a> above but I thought it might save someone some time if it was posted here. (I wasted almost a day about it, Serves me right for not RTFMing ...)

There are limitations on the number of cookies that a client can store at any one time. This is a specification of the minimum number of cookies that a client should be prepared to receive and store.

   * 300 total cookies
   * 4 kilobytes per cookie, where the name and the OPAQUE_STRING combine to form the 4 kilobyte limit.
   * 20 cookies per server or domain. (note that completely specified hosts and domains are treated as separate entities and have a 20 cookie limitation for each, not combined)

Servers should not expect clients to be able to exceed these limits. When the 300 cookie limit or the 20 cookie per server limit is exceeded, clients should delete the least recently used cookie. When a cookie larger than 4 kilobytes is encountered the cookie should be trimmed to fit, but the name should remain intact as long as it is less than 4 kilobytes.
apex at xepa dot nl
25-Nov-2003 10:59
[Editor's note: ... or simply use sessions. You can't be sure that the visitor will use the same IP the next visit. Not even on the next request (thanks to proxy servers)]

Note on setting cookies allowing access to sites:

If you are not using something "personal" from the computer that you are sending the cookie too watch out.  Via javascript it is possible to steal cookies from other users.  Thus allowing the stealer to login to your site as another user that might not have access otherwise.  Try to add something like the user's ip in the cookie and allowing access from that ip only with the stored cookie data.
Jrg Aldinger
01-Oct-2003 07:13
When using your cookies on a webserver that is not on the standard port 80, you should NOT include the :[port] in the "Cookie domain" parameter, since this would not be recognized correctly.
I had the issue working on a project that runs on multiple servers (development, production, etc.). One of the servers is running on a different port (together with other websites that run on the same server but on different ports).
dave at fubra dot com
10-Sep-2003 03:36
There are several characters that dont work correctly if part of the cookie name, I in particular ran into problems using = as part of the cookie name ie/

setcookie('dE4fR=', $foo);

this was as a result of base64_encode() ing a variable to use as the name. The cookie was being set fine but the = is stored as a little square character in the cookie which prevent proper retreival.

The solution for me was to use md5() instead of base64_encode() but any other method which avoids the = sign should also solve the problem
thomas at proton dot mine dot nu
30-Jun-2003 07:07
Browsers like Opera and Mozilla are told to need the full page loaded to accept cookies (that's what bmatzelle at NOSPAM dot yahoo dot com said).
You can use cookies at reloading pages, however after sending out all headers call exit(); to stop the rest of the script, so the actual page content is not sent out.
soreman at abv dot bg
12-Apr-2003 06:26
If you experience problems on Microsoft Information Server (IIS) when setting a cookie via PHP and when PHP is running as a CGI binary it is not setting the cookie. After spending many hours on what the problem is here is what happens:

When you invoke setcookie and redirect to another page you will not have your cookie set, because it seems that IIS doesn't set the cookie unless you actually send some contents to the browser. Therefore if you want to set a cookie and then redirect to another page you will have to do the redirection via JavaScript and/or HTML if you want your script to work on IIS.
neil
21-Jan-2003 04:34
Some versions of IE6, when you drop a non-persistent cookie, handle this incorrectly and instead drop a persistent cookie.

This is not PHP's fault: ) Rather it is a bug in these versions of IE6. I am posting this because people may be programming session IDs and tokens in what they believe to be non-persistent cookies. Specifically, IE6 build 6.0.2800.1106CO (SP1) does this.

IE6, in this case, will make your application insecure. You should notify IE6 users to manually delete their cookies after their session is completed. Will they do this, no. At least you did your part to notify them of the risk.

We found this to be true after an ethical hack which condemned the use of SessionID's in query string, even if the entire session begins, happens, and ends, in https. M$ Proxy Server (here we go with M$ crappy security) writes these session ID's to it's proxy logs. Since Proxy Server is easily comprimised with numerous well known hacks, your sessions can be. I have seen the logs and seen the sessionid's in https.

Unfortunately, these EH fellows are forgetting that most user's systems are less secure and less patched than most ISP's and corporate proxy servers. We are simply moving the vulnerability to the client by using cookies. If it were up to me, I would leave sessionid in query string.

I was proven right in the case with this browser.

thx
Neil
mleer at sp dot nl
20-Dec-2002 06:50
P3P is a good idea. But.
IE 6 features an inadequate definition of third party cookies.
If your site is hosted on server A and your PHP stuff is coming in a framesetting from server B your setcookie-attempts will be blocked when default privacy settings are deployed. Your secondparty-cookie will be regarded as a thirdparty-cookie.

So what you do is not read the P3P-Internet Explorer 6-manual at MS but send a header like

header('P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"');

before doing the setcookie-thing. This will enable your cookie to survive any IE-6 privacy settings.

You won't do this if you're working for some doubleclick institution, because if you do, you... you...well... you are not a very nice person!
robert at ourwebhome dot com
06-Dec-2001 07:41
Internet Exploer 6 now requires sites that set cookies to have P3P policies.

From the Microsoft page:
"Internet Explorer 6 implements advanced cookie filtering that is based on the Platform for Privacy Preferences (P3P) specification. By default, Internet Explorer 6 blocks third-party cookies that do not have a compact policy (a condensed computer-readable privacy statement) or third-party cookies that have a compact policy which specifies that personally identifiable information is used without your implicit consent. First-party cookies that have a compact policy which specifies that personally identifiable information is used without implicit consent are downgraded (deleted when you close Internet Explorer). First-party cookies that do not have a compact policy are leashed (restricted so that they can only be read in the first-party context)."

See:
http://support.microsoft.com/default.aspx?scid=kb;EN-US;q260971&GSSNB=1

For more about P3P:
http://www.w3.org/P3P/