我的Cookie为什么没有设置?

31

我有以下的 PHP 函数:

function validateUser($username){
    session_regenerate_id (); 
    $_SESSION['valid'] = 1;
    $_SESSION['username'] = $username;
    setcookie('username2',$username,time()+60*60*24*365);
    header("Location: ../new.php");
}

然后我获取cookie:

echo $_COOKIE['username2']; exit();

(我只是为了调试目的加入了exit())

唯一的问题是它输出为空白。有任何想法吗?

更新:这是函数的调用方式:

    if(mysql_num_rows($queryreg) != 0){
    $row = mysql_fetch_array($queryreg,MYSQL_ASSOC);
    $hash = hash('sha256', $row['salt'] . hash('sha256', $password));
    if($hash == $row['password']) {
        if($row['confirm'] == 1){
            if(isset($remember)){
                setcookie('username',$username,time()+60*60*24*365);
                setcookie('password',$password,time()+60*60*24*365);
            } else {
                setcookie('username','',time()-3600);
                setcookie('password','',time()-3600);
            }
            validateUser($username);

为了节省一些空间,我没有包含所有的if()语句。


@AlienWebguy 是的,在 new.php 上。 - Jason
@Sayem 是的,$username 包含输入的用户名。 - Jason
@AgentConundrum $username 有一个值,而 print_r($_COOKIE); 没有显示正确的 cookie 痕迹。 - Jason
问题已解决,我必须将其更改为 setcookie('username2',$ username,time()+60 * 60 * 24 * 365,'/');,因为 cookie 必须在所有路径上都可用,而不仅仅是当前路径。 - Jason
问题类似,但是这个帖子中的回答更加深入并涵盖了更多可能出现的问题。因此,我不认为这两个帖子是重复的。 - Lizardx
显示剩余10条评论
11个回答

55

尝试添加路径=/,这样cookie将在整个站点而不仅仅是当前目录下工作(我曾经被这个问题困扰过)

示例

setcookie('password',$password,time()+60*60*24*365, '/'); 

同时确保 cookie 是第一个被输出的内容,如 PHP 手册所建议的(我以前也因此被困扰过)

与其他头信息一样,cookie 必须在脚本输出任何内容之前发送(这是协议限制)。


2
如果您想在输出一些文本后添加cookie,则请在脚本开头添加ob_start();。 - Hritik
你真棒!自从何时开始使用, '/');这种东西了? - Mr Heelis
谢谢您提到的内容,如果输出已经发送到浏览器(headers_sent() == true),那么cookie将不会设置,也不会出现错误!如果PHP在输出已经发送时使用setcookie()至少给出一个警告就好了!然而,显然,如果输出已经发送,则setcookie返回false,因此我应该检查输出:“如果在调用此函数之前存在输出,则setcookie将失败并返回false。如果setcookie成功运行,则它将返回true。这并不表示用户是否接受了cookie。” - user6096790

31

为什么您会遇到这个问题

这个问题源于setcookie()函数不会立即设置cookie,而是发送头信息让浏览器设置cookie。这意味着,在当前页面加载期间,setcookie()不会生成任何$_COOKIE。

当浏览器稍后请求页面时,它会在头信息中发送cookie,因此PHP可以将其作为$_COOKIE获取。

简单的、老旧的解决方案

关于解决方案,显然有一个明显的:

setcookie('username',$username,time()+60*60*24*365);
// 'Force' the cookie to exists
$_COOKIE['username'] = $username;

更好的解决方案

我创建了一个名为Cookie的类,它解决了setcookie()和$_COOKIE所共有的问题:

// Class that abstracts both the $_COOKIE and setcookie()
class Cookie
  {
  // The array that stores the cookie
  protected $data = array();

  // Expiration time from now
  protected $expire;
  // Domain for the website
  protected $domain;

  // Default expiration is 28 days (28 * 3600 * 24 = 2419200).
  // Parameters:
  //   $cookie: $_COOKIE variable
  //   $expire: expiration time for the cookie in seconds
  //   $domain: domain for the application `example.com`, `test.com`
  public function __construct($cookie, $expire = 2419200, $domain = null)
    {
    // Set up the data of this cookie
    $this->data = $cookie;

    $this->expire = $expire;

    if ($domain)
      $this->domain = $domain;
    else
      {
      $this->domain = 
        isset($_SERVER['HTTP_X_FORWARDED_HOST']) ?
        $_SERVER['HTTP_X_FORWARDED_HOST'] :
        isset($_SERVER['HTTP_HOST']) ?
          $_SERVER['HTTP_HOST'] :
          $_SERVER['SERVER_NAME'];
      }
    }

  public function __get($name)
    {
    return (isset($this->data[$name])) ?
      $this->data[$name] :
      "";
    }

  public function __set($name, $value = null)
    {
    // Check whether the headers are already sent or not
    if (headers_sent())
      throw new Exception("Can't change cookie " . $name . " after sending headers.");

    // Delete the cookie
    if (!$value)
      {
      setcookie($name, null, time() - 10, '/', '.' . $this->domain, false, true);
      unset($this->data[$name]);
      unset($_COOKIE[$name]);
      }

    else
      {
      // Set the actual cookie
      setcookie($name, $value, time() + $this->expire, '/', $this->domain, false, true);
      $this->data[$name] = $value;
      $_COOKIE[$name] = $value;
      }
    }
  }

然后你可以像这样使用它:
$Cookie = new Cookie($_COOKIE);
$User = $Cookie->user;
$LastVisit = $Cookie->last;
$Cookie->last = time();

当然,你必须传递它。这比使用全局变量好得多。

我也有自己的创建cookie类。然而,一个关键的区别是,如果头部已经被发送,我不会抛出异常,而是简单地打印一些设置/更新cookie的javascript代码,然后更新$_COOKIE变量。 - lilHar
嗨Francisco,谢谢你的解释。有一个问题,'user'和'last'属性在哪里定义的?看起来它们被假设为类内的属性? - undefined
这是10年前的东西...请阅读这里关于__get的内容以理解代码: https://www.php.net/manual/en/language.oop5.overloading.php#object.get - undefined

8

这是setcookie的一般语法

setcookie(name,value,expire,path,domain,secure); 

查看第三个参数,如果您没有设置它,脚本将使用当前工作目录。因此,如果您在 a.com/b/setcookie.php 中设置 cookie 而没有设置路径,则该 cookie 将不可用于 a.com/checkcookie.php。您正在做的是在子文件夹中设置 cookie,然后重定向到父文件夹,请查看 ../,在那里它不可用,因此出现了问题。如何避免这种情况?正常的程序是提供一个路径,即 /,在您的情况下,将其作为第四个参数提供。您的 cookie 的第五个参数将使其安全。http://www.php.net/setcookie 有更多解释。这应该解决您的问题。将域路径设置为 domain.com,将使 cookie 可用于 domain.com 下的所有内容,但不包括 something.domain.com。将域值设置为 .domain.com,请注意前面的点号,将使其在 anything.domain.com 上可用。希望对您有所帮助!

2
'/' 挽救了这一切。我没有收到任何错误信息,只是没有保存 cookie,如果没有在字符串末尾加上 setcookie('threadcookie', $v_id,time()+3600, '/'); 的话。 谢谢! - user886618

5
我想再添加一个可能导致cookie设置或随机功能行为的原因。
对于一些程序员来说,以下情况可能适用于他们,即由于错误使用header_remove()而出现似乎难以捉摸的cookie设置问题。
如果在调用header_remove()之前尝试设置cookie,则永远不会创建cookie,因为你已经立即销毁了用于创建cookie的header,然后才将其缓冲到客户端。
当您调整cookie时,您可能会发现cookie突然无法正常工作,因此您需要了解有关标头的竞争条件:
1.第一次运行时,你设置cookie,并且根本没有调用header_remove()。
2.在第二次运行时,你调用header_remove()。
你会发现你的cookie现在总是被设置,无论条件(2)和它被调用的次数,因为(1)至少发生了一次。
cookie将保持设置状态,直到过期、被覆盖或unset()。
修改标头(如cookie值)并在最终调用header_remove()之前,您将无法设置新值,因为它们将在响应被缓冲到用户之前被清除。
你需要在header_remove()之后设置cookie和任何其他标头。
使用header_remove()清理所有先前设置的标头,以便为最终输出设置新标头。
这种情况的一个示例可以如下:
使用header_remove()来更改RESTFUL API的HTTP响应代码层次结构,其中您正在使用axios和拦截器。
1.您的应用程序首先设置400+ header错误,如果应用程序在执行任何点时出错,应用程序会出现错误。
2.当达到最终所需的执行点并且期望有效的响应时,将标头修改为200。
在这种情况下,您可能希望保留所有其他先前设置的标头,但清除HTTP状态(400?)码,以便为最终响应设置新的(200?)码。
如果您在删除先前设置的标头之前尝试重新设置标头以更改状态代码,则将收到“Headers already sent”错误。
您可以使用header_remove来删除特定的头信息,以下是逐步取消状态码并设置新状态码的方法:

     // Set a default status code
     http_response_code(500);

     // Boot Logic runs - if fails here 500 is returned

     // Authentication Logic - If unauthorized ?
     header_remove('HTTP/1.0'); // Clear previous 500
     http_response_code(401); // Set new status code

     // else ?

     // Return Data Logic - Success
     header_remove('HTTP/1.0'); // Clear previous 500
     http_response_code(200) // Set new status code


4
这需要您在任何输出之前(包括和标签以及任何空格)调用此函数。
这是结构的样子。
<?php
$cookie_name = "user";
$cookie_value = "Ahmed Moftah";
setcookie($cookie_name, $cookie_value, time() + (86400 * 30), "/"); // 86400 = 1 day
?>
<?php
if(!isset($_COOKIE[$cookie_name])) {
    echo "Cookie named '" . $cookie_name . "' is not set!";
} else {
    echo "Cookie '" . $cookie_name . "' is set!<br>";
    echo "Value is: " . $_COOKIE[$cookie_name];
}
?>
<html>
<body>



</body>
</html>

0

在同一请求中,您无法获取 $_COOKIE,但可以获取 $_SESSION。

所以想法是

  • 在会话和 cookie 中都设置 $username。
  • 第一次从会话中获取用户名(如果是同一请求)
  • 取消会话
  • 始终从下一个请求的 $_COOKIE 中获取 cookie。
// Set Cookie
setcookie('username',$username,time()+60*60*24*365);
$_SESSION['username'] = $username;

//GET Cookies

if(!$_COOKIE['username']){
   $username = $_SESSION['username'];
   unset($_SESSION['username']);
}else{
   $username = $_COOKIE['username'];
}

0
虽然这个问题很老了,但是我遇到了同样的问题,而且我的情况完全不同。请看这里。

enter image description here

花了很长时间才弄清楚为什么我的cookie在浏览器中没有设置成功。

P.S - 我复制了一个带有https的虚拟机,但是我尝试执行复制的虚拟机时没有设置SSL,并且由于服务器设置为安全模式,cookie无法正常工作。要么需要使用https,要么需要更改服务器上的突出显示设置以使用http。


1
请用文字解释一下你的截图所描绘的内容。 - undefined

0

你可能已经发送了头部,从而使设置cookie的头部变得不可能。这可能是一个简单的解决方案,在你的页面开头加上ob_start()


0

当会话 cookie 被禁用时会出现此问题。

您可以导航到您的 php.ini 文件(根据服务器不同而有所变化。Ubuntu 20.04 的默认路径为 /etc/php/{X.x}/{apache2|[others]}/php.ini),并确保 session.use_cookies=1

重新启动您的服务器,然后尝试设置 cookie。它们应该立即可用。


0

这可能是缓存问题。尝试关闭浏览器,使用本地文件路径打开一个新的浏览器。我曾经遇到过同样的问题,我的页面被缓存了,所以Cookie无法工作,即使我可以输入新代码并在页面上看到更改。非常奇怪...清除缓存可能有帮助,请先尝试。然后尝试一个新的浏览器,然后尝试转到localhost:8080索引并点击刷新以查看上一页的修改时间。

如果这不能解决问题,请尝试重新启动LAAMP或XAAMP或您正在使用的任何其他软件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接