从 PHP 响应中删除重复的“Set-Cookie”标头

WOU*_*nes 5 php cookies setcookie http-headers response-headers

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

<?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');
Run Code Online (Sandbox Code Playgroud)

我得到以下响应(通过 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
Run Code Online (Sandbox Code Playgroud)

问题是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
Run Code Online (Sandbox Code Playgroud)

虽然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
Run Code Online (Sandbox Code Playgroud)

如果我尝试删除标题,如:

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

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

Sᴀᴍ*_*ᴇᴌᴀ 5

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

当检测到多个具有相同名称的 cookie 时,我们可以header_remove()像您一样使用调用,然后将 cookie 设置为最终值。请参阅下面的示例以及此示例 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

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 -编码