从 PHP 响应中删除重复的 "Set-Cookie" header

Remove duplicate "Set-Cookie" header from PHP response

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

<?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 个不同的时间,因此不必要地增加 header 大小。 (同样,这是一个简化的例子 - 实际上,我正在处理大约 800 字节范围内的大约 10 个重复 cookie。)

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

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 时,它​​会添加额外的 header 以将其标记为已过期:

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

如果我尝试删除 header,例如:

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

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

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

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

正如您在最后一段代码中所建议的那样,headers_list() function could be used to check what headers have been sent. Using that, the last values for each cookie could be stored in an associative array. The names and values can be extracted using explode() (along with trim())。

当检测到多个同名cookie时,我们可以使用header_remove() call like you had, but then set the cookies to the final values. See the example below, as well as 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
Content-Encoding gzip
Content-Type text/html; charset=utf-8
Date Wed, 26 Apr 2017 15:31:33 GMT
Expires Wed, 11 Jan 1984 05:00:00 GMT
Pragma no-cache
Server nginx
Set-Cookie test1=third
                     test2=keep
Transfer-Encoding chunked
Vary Accept-Encoding