【问题标题】:Remove duplicate "Set-Cookie" header from PHP response从 PHP 响应中删除重复的“Set-Cookie”标头
【发布时间】:2017-09-23 03:03:07
【问题描述】:

这是来自大型应用程序的示例脚本,但显示了我正在尝试执行的一般过程。如果我有以下脚本:

<?php
ob_start();

setcookie('test1', 'first');
setcookie('test1', 'second');
setcookie('test1', 'third');
setcookie('test2', 'keep');

//TODO remove duplicate test1 from headers

ob_end_clean();
die('end test');

我收到以下响应(通过 Fiddler 查看):

HTTP/1.1 200 OK
Date: Tue, 25 Apr 2017 21:54:45 GMT
Server: Apache/2.4.17 (Win32) OpenSSL/1.0.2d PHP/5.5.30
X-Powered-By: PHP/5.5.30
Set-Cookie: test1=first
Set-Cookie: test1=second
Set-Cookie: test1=third
Set-Cookie: test2=keep
Content-Length: 8
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html

end test

问题是 Set-Cookie: test1... 存在 3 次不同的时间,因此不必要地增加了标头大小。 (同样,这是一个简化的例子—— 实际上,我正在处理约 800 字节范围内的约 10 个重复 cookie。)

我可以写什么来代替 TODO 来完全摆脱标题或只显示一次吗?即以下是我的最终目标:

HTTP/1.1 200 OK
Date: Tue, 25 Apr 2017 21:54:45 GMT
Server: Apache/2.4.17 (Win32) OpenSSL/1.0.2d PHP/5.5.30
X-Powered-By: PHP/5.5.30
Set-Cookie: test1=third
Set-Cookie: test2=keep
Content-Length: 8
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html

end test

虽然Set-Cookie: test1=third 也不能存在,这很好,但Set-Cookie: test2=keep 需要保留。当我尝试setcookie('test1', '', 1); 删除 cookie 时,它​​会添加一个额外的标头以将其标记为过期:

Set-Cookie: test1=first
Set-Cookie: test1=second
Set-Cookie: test1=third
Set-Cookie: test2=keep
Set-Cookie: test1=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0

如果我尝试删除像这样的标题:

if (!headers_sent()) {
    foreach (headers_list() as $header) {
        if (stripos($header, 'Set-Cookie: test1') !== false) {
            header_remove('Set-Cookie');
        }
    }
}

当我只想删除 test1 时,它会删除所有 Set-Cookie 标头。

【问题讨论】:

  • 你能把标题存储在一个数组中吗?例如如果你有一个 View 类,它可能有一个(静态?)方法 SetCookie() ,它将键和值存储在关联数组中。然后在发送最终标头时,只需遍历该关联数组并调用 setcookie(),每个键只会调用一次...
  • 我希望!理想情况下就是这样设置的,但是当它到达我的脚本时,Set-Cookie: test1=... 已经运行了多次,所以我可以注入任何类型代码的地方是TODO

标签: php cookies http-headers setcookie response-headers


【解决方案1】:

正如您在最后一段代码中所建议的,headers_list() 函数可用于检查已发送的标头。使用它,每个 cookie 的最后一个值可以存储在关联数组中。可以使用explode()(以及trim())提取名称和值。

当检测到多个同名 cookie 时,我们可以像您一样使用 header_remove() 调用,但随后将 cookie 设置为最终值。请参阅下面的示例以及this example phpfiddle

if (!headers_sent()) {
    $cookiesSet = []; //associative array to store the last value for each cookie
    $rectifyCookies = false; //multiple values detected for same cookie name

    foreach (headers_list() as $header) {
        if (stripos($header, 'Set-Cookie:') !== false) {
            list($setCookie, $cookieValue) = explode(':', $header);
            list($cookieName, $cookieValue) = explode('=', trim($cookieValue));
            if (array_key_exists($cookieName, $cookiesSet)) {
                $rectifyCookies = true;
            }
            $cookiesSet[$cookieName] = $cookieValue;
        }
    }
    if ($rectifyCookies) {
        header_remove('Set-Cookie');
        foreach($cookiesSet as $cookieName => $cookieValue) {
            //might need to consider optional 3rd - 8th parameters 
            setcookie($cookieName, $cookieValue);
        }
    }
}

输出:

Cache-Control max-age=0, no-cache, no-store, must-revalidate
Connection keep-alive
内容编码 gzip
内容类型 text/html; charset=utf-8
日期 2017 年 4 月 26 日星期三 15:31:33 GMT
到期 1984 年 1 月 11 日星期三 05:00:00 GMTPragma 无缓存
服务器 nginx
Set-Cookie test1=third
test2=keep
Transfer-Encoding 分块
Vary Accept-Encoding

【讨论】:

  • 太棒了,这正是我要找的!
  • 这太棒了。我建议更改两件事以正确处理额外的 cookie 标头参数。 1) 更改 both 在 2 处爆炸以限制 例如:explode(':', $header, 2) 2) 使用 header() 函数而不是 setcookie()header("Set-Cookie: {$cookieName}={$cookieValue}", false); 这些更改将保持任何过期、Max-Age 等来自原始 Set-Cookie 标头的参数。
  • @Stenerson 好的,我可以将第三个参数添加到对explode() 的调用中,但是在阅读this answer 之后,没有看到太多不使用setcookie() 的理由(除非@987654327 @影响你)....
  • 请注意,此代码可以放入函数并注册到header-register_callback()
【解决方案2】:

我不明白为什么您认为您向我们展示的 cookie 删除代码会删除 test2 的 setcookie。

如果您的代码多次设置相同的 cookie,那么您需要更改代码以停止多次设置 cookie!其他任何方法都是草率的解决方法。

【讨论】:

  • 你的意思是header_remove('Set-Cookie');?它的定义方式 (php.net/manual/en/function.header-remove.php) 删除了所有 Set-Cookie 标头。因此,无论 cookie 名称如何,它都会将它们全部清除。我正在使用一个似乎多次添加 cookie 的框架,所以我只是想在返回响应之前清理它而不破坏核心。
  • 当然可以!撇开解决问题的错误方法不谈,您可以将 setcookie 标头解析为 name=>value 的数组,然后 header_remove('Set-Cookie') 然后再次遍历您的数组。但这会导致一个稍微不那么糟糕的解决方案,即首先填充数组而不调用 setcookie() 直到数据被编组(但它仍然是错误的解决方案)
猜你喜欢
  • 2019-03-21
  • 1970-01-01
  • 2016-03-05
  • 2014-02-26
  • 2016-05-22
  • 2021-10-21
  • 1970-01-01
  • 2012-06-24
  • 2019-08-01
相关资源
最近更新 更多