# ch03 — `setcookie()` と `$_COOKIE`

Source: https://php-school.pages.dev/topics/session-cookie/ch03-cookie-basic/
Site: AIと学ぶPHPの学校 (https://php-school.pages.dev)
Lesson: セッション・Cookie・ログイン
Level: L4

Reference (official): https://www.php.net/manual/ja/book.session.php

## 本文

# ch03 — `setcookie()` と `$_COOKIE`

## 概要

Session の裏側で使われている Cookie 自体を **直接** 扱う章。書き込みは **`setcookie('name', 'value')`**、読み込みは **`$_COOKIE['name']`**。Session を立てるほどでもない軽い情報 (ダークモード設定・言語選択など) を保つ用途で実務でも頻出する。

`setcookie()` は多くのオプションを取る。第 3 引数で **有効期限** (UNIX タイムスタンプ)、配列形式で渡せば **Path / Domain / Secure / HttpOnly** を細かく指定できる。とくに `HttpOnly` (JavaScript から読めない) と `Secure` (HTTPS 専用) は XSS や盗聴対策の必須属性で、Session Cookie には絶対に付けたい。

`setcookie()` で書いた値は **「ブラウザに『次から送ってね』と頼んでいるだけ」** で、自分の `$_COOKIE` には即時反映されない。`$_COOKIE` に乗ってくるのは **次のリクエスト** から。これも HTTP が 1 往復で完結することの帰結。CLI 採点ランナーでは、stdin から Cookie 文字列を読んで `$_COOKIE` を手で組み立てるスタブで疑似化する。

## 学習目標

- `setcookie('name', 'value')` でブラウザに Cookie を書き出せる
- `$_COOKIE['name']` でブラウザから送られてきた Cookie を読める
- 採点用に **stdin から Cookie 文字列を読んで `$_COOKIE` をスタブする** 書き方を知る

## 所要時間

スライド 5 分 + ドリル 2 問 = **25 分**

## ドリル

| no | 内容 |
|---|---|
| 01 | stdin から `name=太郎` を読み、`$_COOKIE['name']` を出力 |
| 02 | stdin の複数行から `key=value` を読み、`$_COOKIE` を組み立てて出力 |

## 本物の Cookie 動作で確認したい場合

```bash
cd topics/session-cookie/ch03-cookie-basic/drill/01-read-cookie/
php -S localhost:8000 answer.php
```

ブラウザの開発者ツール (Application → Cookies) で Cookie を手動で追加し、リロードして `$_COOKIE` 経由で読めることを確認する。

## スライド

---
marp: true
theme: dojo
paginate: true
size: 16:9
---

<!--
LLM_CONTEXT:
  Lesson 13 / Chapter 3
  目的: setcookie() / $_COOKIE / Cookie ヘッダの仕組み
  扱わない: session_start (ch02 で済) / login (ch04) / CSRF (ch05) / regenerate_id (ch06)
  読み上げ時間目安: 4 分半〜5 分
-->

# `setcookie()` と `$_COOKIE`

Lesson 13 / Chapter 3

---

## Cookie のやり取り (絵)

```
1. ブラウザがリクエスト
   GET / HTTP/1.1

2. サーバーがレスポンスに Set-Cookie ヘッダを乗せる
   HTTP/1.1 200 OK
   Set-Cookie: name=taro

3. ブラウザが Cookie を保存する

4. 次回以降のリクエストに自動で乗せて送る
   GET /page HTTP/1.1
   Cookie: name=taro
```

PHP からは:

- **書き出す**: `setcookie('name', 'taro')`
- **読む**: `$_COOKIE['name']` (連想配列)

---

## 最小のサンプル

```php
<?php
// 1. ブラウザに Cookie を書く (echo より前で呼ぶ)
setcookie('name', '太郎');

// 2. 次回以降、ブラウザが送ってきた Cookie を読む
$name = $_COOKIE['name'] ?? 'guest';
echo "こんにちは、{$name}さん\n";
```

- `setcookie()` は **`echo` の前** で呼ぶ (HTTP ヘッダは本文より前に送る必要がある)
- 初回アクセス時は `$_COOKIE` は空 → `??` で `guest` にフォールバック
- リロードすると Cookie が乗ってきて `太郎` が出る

---

## `setcookie()` のオプション (重要 3 つ)

```php
setcookie(
    'name',           // key
    '太郎',           // value
    [
        'expires'  => time() + 3600,   // 1 時間後に消える
        'httponly' => true,            // JS から読めない (XSS 対策)
        'samesite' => 'Lax',           // CSRF 対策
        'secure'   => true,            // HTTPS でのみ送る
    ],
);
```

| オプション | 役割 |
|---|---|
| `expires` | **有効期限**。未指定はブラウザ閉じるまで |
| `httponly` | JavaScript からアクセス禁止 (XSS で盗まれにくくする) |
| `samesite` | 他サイトからのリクエストに乗せない (**CSRF 対策**) |
| `secure` | HTTPS でのみ送信 |

→ 本番では `httponly + samesite=Lax + secure` が基本セット。

---

## 採点用スタブ: stdin から `$_COOKIE` を埋める

CLI 採点ランナーには **ブラウザが居ない** ので、`$_COOKIE` が自動で埋まらない。
web トピックの `$_GET` と同じ要領で **stdin から組み立てる**:

```php
<?php
// 採点用: stdin の 1 行目 (例 "name=太郎") を $_COOKIE に展開
$line = trim(fgets(STDIN) ?: '');
parse_str($line, $_COOKIE);

// 以降は本物の Web と同じ
$name = $_COOKIE['name'] ?? 'guest';
echo "こんにちは、{$name}さん\n";
```

- `parse_str("name=太郎", $_COOKIE)` で連想配列に分解
- 本物の Web では PHP が `$_COOKIE` を自動で埋めるので、このスタブは不要

---

## Cookie はブラウザに改ざんできる ← 必ず覚える

```php
// ❌ 危ない例
setcookie('user_id', '42');
$userId = $_COOKIE['user_id'];   // ユーザーが 1 や 999 に書き換えられる
```

- ブラウザの開発者ツールから **Cookie の値はいくらでも書き換えられる**
- ログイン状態や権限など、**改ざんされると困る情報を Cookie に直接置かない**
- 「ユーザーが好き勝手に値を入れる入力欄」と思って扱う

→ そういう情報は **Session** に入れて、Cookie には **Session ID (合言葉) だけ** 入れる (ch02 の話)。

---

## このチャプターでできるようになること

✅ `setcookie('key', 'value')` でブラウザに Cookie を書ける
✅ `$_COOKIE['key']` でブラウザから送られた Cookie を読める
✅ `httponly` / `samesite` / `secure` の役割を 1 行で言える
✅ Cookie はブラウザに改ざんされるため、秘密情報は入れないと判断できる

→ ドリルへ
