LoginSignup
0
0

More than 3 years have passed since last update.

JavaScriptでPHPのsetcookie相当の関数を作成

Last updated at Posted at 2020-05-27

まずPHPのsetcookie関数の仕様を確認。
setcookie - PHPマニュアル

パラメータは
namevalueexpirespathdomainsecurehttponlyoptions

optionsは指定が特殊で、第8引数ということではなくexpirespathdomainsecurehttponlysamesiteのうち必要分を連想配列として第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()

setcookie.js
/**
 * 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()

getcookie.js
/**
 * 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);
0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0