まずPHPのsetcookie関数の仕様を確認。
setcookie - PHPマニュアル
パラメータは
name、value、expires、path、domain、secure、httponly、options
optionsは指定が特殊で、第8引数ということではなくexpires、path、domain、secure、httponly、samesiteのうち必要分を連想配列として第3引数に渡すということのよう。
パラメータの内容についてはこちらも合わせて参照。
HTTP Cookie - MDN
Document.cookie - MDN
挙動確認##
以下、PHPのsetcookie関数を実行しながらレスポンスヘッダに実際に出力されるSet-Cookie:フィールドの内容を確認。
パラメータ全省略####
setcookie();
Warning: setcookie() expects at least 1 parameter, 0 given
nameのみ####
setcookie('name');
Set-Cookie: name=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0
valueには'deleted'が付加され、expiresに'Thu, 01-Jan-1970 00:00:01 GMT'、Max-Ageに'0'が固定で指定された。
実質的に、Cookie名 'test' の削除動作。
value####
setcookie('name', 'value');
Set-Cookie: name=value
expires、Max-Ageは付かない。
セッションクッキー(ブラウザを閉じた時点で破棄)扱い。
setcookie('name', '');
setcookie('name', NULL);
空文字列やNULLはsetcookie('name'); の時と同じ
setcookie('name', []);
Warning: setcookie() expects parameter 2 to be string, array given
expires####
setcookie('name', 'value', time());
Set-Cookie: name=value; expires=Wed, 27-May-2020 06:33:16 GMT; Max-Age=0
setcookie('name', 'value', time() - 86400);
Set-Cookie: name=value; expires=Tue, 26-May-2020 06:35:14 GMT; Max-Age=0
expiresに実行日時以前の値を指定すると、Max-Ageも0になる。
削除と同等。
setcookie('name', 'value', time() + 86400);
Set-Cookie: name=value; expires=Thu, 28-May-2020 06:36:54 GMT; Max-Age=86400
expiresに実行時以降の値を指定すると、expiresとMax-Age両方で有効期限が設定される。
expiresは有効期限日時、Max-Ageは保持秒数。
path####
setcookie('name', 'value', time() + 86400, '');
Set-Cookie: name=value; expires=Thu, 28-May-2020 06:51:43 GMT; Max-Age=86400
setcookie('name', 'value', time() + 86400, '/');
Set-Cookie: name=value; expires=Thu, 28-May-2020 06:52:26 GMT; Max-Age=86400; path=/
setcookie('name', 'value', time() + 86400, '/test/test');
Set-Cookie: name=value; expires=Thu, 28-May-2020 06:53:45 GMT; Max-Age=86400; path=/test/test
setcookie('name', 'value', time() + 86400, 0);
Set-Cookie: name=value; expires=Thu, 28-May-2020 06:54:25 GMT; Max-Age=86400; path=0
pathパラメータに指定した値が空文字列以外であればそのままSet-Cookieフィールドにpathパラメータとして出力される様子。
domain####
setcookie('name', 'value', time() + 86400, '', '');
Set-Cookie: name=value; expires=Thu, 28-May-2020 06:58:37 GMT; Max-Age=86400
setcookie('name', 'value', time() + 86400, '', 'testdomain.com');
Set-Cookie: name=value; expires=Thu, 28-May-2020 06:59:28 GMT; Max-Age=86400; domain=testdomain.com
setcookie('name', 'value', time() + 86400, '', 'localhost');
Set-Cookie: name=value; expires=Thu, 28-May-2020 07:00:10 GMT; Max-Age=86400; domain=localhost
setcookie('name', 'value', time() + 86400, '', '127.0.0.1');
Set-Cookie: name=value; expires=Thu, 28-May-2020 07:00:54 GMT; Max-Age=86400; domain=127.0.0.1
空文字列以外であればそのままdomainパラメータとして出力。
secure####
setcookie('name', 'value', time() + 86400, '', '', '');
Set-Cookie: name=value; expires=Thu, 28-May-2020 07:05:54 GMT; Max-Age=86400
setcookie('name', 'value', time() + 86400, '', '', 0);
Set-Cookie: name=value; expires=Thu, 28-May-2020 07:06:13 GMT; Max-Age=86400
setcookie('name', 'value', time() + 86400, '', '', 1);
Set-Cookie: name=value; expires=Thu, 28-May-2020 07:06:42 GMT; Max-Age=86400; secure
setcookie('name', 'value', time() + 86400, '', '', false);
Set-Cookie: name=value; expires=Thu, 28-May-2020 07:08:27 GMT; Max-Age=86400
setcookie('name', 'value', time() + 86400, '', '', true);
Set-Cookie: name=value; expires=Thu, 28-May-2020 07:08:02 GMT; Max-Age=86400; secure
パラメータがtrueであればsecureが付く。
httponly####
setcookie('name', 'value', time() + 86400, '', '', '', '');
Set-Cookie: name=value; expires=Thu, 28-May-2020 07:11:39 GMT; Max-Age=86400
setcookie('name', 'value', time() + 86400, '', '', '', 0);
Set-Cookie: name=value; expires=Thu, 28-May-2020 07:11:54 GMT; Max-Age=86400
setcookie('name', 'value', time() + 86400, '', '', '', 1);
Set-Cookie: name=value; expires=Thu, 28-May-2020 07:12:19 GMT; Max-Age=86400; HttpOnly
setcookie('name', 'value', time() + 86400, '', '', '', false);
Set-Cookie: name=value; expires=Thu, 28-May-2020 07:12:43 GMT; Max-Age=86400
setcookie('name', 'value', time() + 86400, '', '', '', true);
Set-Cookie: name=value; expires=Thu, 28-May-2020 07:13:04 GMT; Max-Age=86400; HttpOnly
パラメータがtrueであればHttpOnlyが付く。
HttpOnlyはレスポンスヘッダのSet-Cookie:フィールドで保存指示を受けた場合のみ有効とする指定なのでJavaScriptのdocument.cookieで指定しても無効になるかと思いますが、一応確認。
options####
setcookie('name', 'value', []);
Set-Cookie: name=value
setcookie('name', 'value', ['expires' => time() + 86400]);
Set-Cookie: name=value; expires=Thu, 28-May-2020 07:17:50 GMT; Max-Age=86400
setcookie('name', 'value', ['Expires' => time() + 86400]);
Set-Cookie: name=value; expires=Thu, 28-May-2020 07:18:23 GMT; Max-Age=86400
setcookie('name', 'value', ['expires-' => time() + 86400]);
Warning: setcookie(): Unrecognized key 'expires-' found in the options array
Warning: setcookie(): No valid options were found in the given array
キー名の大文字小文字は区別しない様子。
無効なキー名を渡すとWarningが出る。
setcookie('name', 'value', ['expires' => time() + 86400, 'name' => 'name2']);
Set-Cookie: name=value; expires=Thu, 28-May-2020 07:22:59 GMT; Max-Age=86400
Warning: setcookie(): Unrecognized key 'name' found in the options array
optionsパラメータでnameパラメータの上書きはできなかった。
setcookie('name', 'value', ['expires' => time() + 86400, 'max-age' => 3600]);
Set-Cookie: name=value; expires=Thu, 28-May-2020 07:25:08 GMT; Max-Age=86400
Warning: setcookie(): Unrecognized key 'max-age' found in the options array
optionsパラメータでもmax-ageの指定はできなかった。
setcookie関数においては常にexpiresでの指定になり、内部的には同じ意味になるmax-ageパラメータを付加する動作みたい。
setcookie('name', 'value', ['expires' => time() + 86400, 'path' => '/']);
Set-Cookie: name=value; expires=Thu, 28-May-2020 07:26:59 GMT; Max-Age=86400; path=/
setcookie('name', 'value', ['path' => '/', 'expires' => time() + 86400]);
Set-Cookie: name=value; expires=Thu, 28-May-2020 07:27:31 GMT; Max-Age=86400; path=/
optionsパラメータ内の項目の順序を変えてもSet-Cookie:フィールド上の項目の並びはとくに変わらなかった。
setcookie('name', 'value', ['expires' => time() + 86400], '/');
Warning: setcookie(): Cannot pass arguments after the options array
第3引数にoptionsとして連想配列を渡した時はそれ以降の引数を渡すとWarningが出る。
setcookie('name', 'value', [
'expires' => time() + 86400,
'samesite' => 'Lax',
]);
Set-Cookie: name=value; expires=Thu, 28-May-2020 12:11:05 GMT; Max-Age=86400; SameSite=Lax
setcookie('name', 'value', [
'expires' => time() + 86400,
'samesite' => 'aaaaaaaa',
]);
Set-Cookie: name=value; expires=Thu, 28-May-2020 12:12:00 GMT; Max-Age=86400; SameSite=aaaaaaaa
setcookie('name', 'value', [
'expires' => time() + 86400,
'samesite' => '',
]);
Set-Cookie: name=value; expires=Thu, 28-May-2020 12:12:41 GMT; Max-Age=86400
setcookie('name', 'マルチバイト文字', [
'expires' => time() + 86400,
'samesite' => 'マルチバイト文字',
]);
Set-Cookie: name=%E3%83%9E%E3%83%AB%E3%83%81%E3%83%90%E3%82%A4%E3%83%88%E6%96%87%E5%AD%97; expires=Thu, 28-May-2020 12:32:12 GMT; Max-Age=86400; SameSite=マルチバイト文字
samesiteオプションはとりあえず有効な値(None,Lax,Strict)でなくても何か指定されていればそのまま出力する様子。
JavaScriptで作成した互換関数##
setcookie()####
/**
* PHPのsetcookie()関数相当のCookie発行処理
* サーバ側からのレスポンスヘッダのSet-Cookie:での発行指示とは経路が異なるため
* 反映タイミングなど差異が出る可能性もあり
*/
function setcookie(name, value, expires, path, domain, secure, httpOnly) {
let sameSite = undefined;
// name未指定なら終了
if(name === undefined) return;
// URIエンコードはせず無効な文字が含まれる場合は終了
if(name.match(/[=,;\0-\x20]/)) return;
// 第3引数が連想配列だった場合はoptions指定扱いとする
if(typeof expires === 'object') {
// path以降の引数が指定されていたら終了
for(let tmp of [path, domain, secure, httpOnly]) {
if(typeof tmp !== 'undefined') return;
}
let options = expires;
expires = undefined;
// optionsの設定を各パラメータの変数へ代入
for(let tmp in options) {
switch(tmp.toUpperCase()) {
case 'EXPIRES':
expires = options[tmp];
break;
case 'PATH':
path = options[tmp];
break;
case 'DOMAIN':
domain = options[tmp];
break;
case 'SECURE':
secure = options[tmp];
break;
case 'HTTPONLY':
httpOnly = options[tmp];
break;
case 'SAMESITE':
sameSite = options[tmp];
break;
default:
// 想定外のキー名の場合はとりあえず何もしない
break;
}
}
}
for(let tmp of [name, value, expires, path, domain, secure, httpOnly]) {
if(typeof tmp === 'object') return;
}
// name=valueのペア作成 valueはURIエンコードが必要
let cookie = [
name + '=' + (value === undefined ? 'deleted' : encodeURIComponent(value)),
];
// expires, max-age
let maxAge = 0;
if(expires !== undefined) {
maxAge = expires - Math.floor(Date.now() / 1000);
}
if(value === undefined || (expires !== undefined && expires > 0)) {
cookie.push(
'expires=' + new Date(value === undefined ? 1000 : Date.now() + 1000 * maxAge).toUTCString(),
'Max-Age=' + (maxAge > 0 ? maxAge : '0')
);
}
// その他のパラメータは指定されていればほぼそのまま渡す
if(path !== undefined && path !== '') cookie.push('path=' + path);
if(domain !== undefined && domain !== '') cookie.push('domain=' + domain);
if(secure !== undefined && Boolean(secure) === true) cookie.push('secure');
if(httpOnly !== undefined && Boolean(httpOnly) === true) cookie.push('HttpOnly');
if(sameSite !== undefined && sameSite !== '') cookie.push('SameSite=' + sameSite);
// 各項目をセパレータで繋いでdocument.cookieへ渡す
cookie = cookie.join('; ');
document.cookie = cookie;
}
使用例
// PHPのtime()相当
const time = () => Math.floor(Date.now() / 1000);
// PHPのsetcookie()相当
setcookie('test1', 'value1', time() + 3600, '', '', true);
setcookie('test2', 'value2', {'expires':time() + 3600, 'path':'/'});
let options = [];
options['expires'] = time() + 3600;
options['samesite'] = 'Lax';
setcookie('test3', 'value3', options);
getcookie()####
/**
* Cookie取得
* PHPの$_COOKIE変数相当の連想配列としてパース
*/
function getcookie() {
let r = {};
for(let cookie of document.cookie.split(/;\s+?/)) {
if(cookie === '') continue;
let tmp = cookie.split('=');
if(tmp[1] !== undefined) tmp[1] = decodeURIComponent(tmp[1]);
if(r[tmp[0]] === undefined && tmp[1] !== undefined) r[tmp[0]] = tmp[1];
}
return r;
}
使用例
let $_COOKIE = getcookie();
console.log($_COOKIE);