runtime-sessions-cookies.md 16.5 KB
Newer Older
1
セッションとクッキー
2 3
====================

4 5 6
セッションとクッキーは、データが複数のユーザリクエストを超えて持続することを可能にします。
素の PHP では、それぞれ、グローバル変数 `$_SESSION``$_COOKIE` によってアクセスすることが出来ます。
Yii はセッションとクッキーをオブジェクトとしてカプセル化し、オブジェクト指向の流儀でアクセスできるようにするとともに、有用な機能強化を追加しています。
7 8


9
## セッション <a name="sessions"></a>
10

11
[リクエスト](runtime-requests.md)[レスポンス](runtime-responses.md) と同じように、既定では [[yii\web\Session]] のインスタンスである `session` [アプリケーションコンポーネント] によって、セッションにアクセスすることが出来ます。
12 13


14
### セッションのオープンとクローズ <a name="opening-closing-sessions"></a>
15

16
セッションのオープンとクローズは、次のようにして出来ます。
17 18 19 20

```php
$session = Yii::$app->session;

21
// セッションが既に開かれているかチェックする
22 23
if ($session->isActive) ...

24
// セッションを開く
25 26
$session->open();

27
// セッションを閉じる
28 29
$session->close();

30
// セッションに登録されている全てのデータを破壊する
31 32 33
$session->destroy();
```

34 35
エラーを発生させずに [[yii\web\Session::open()|open()]] と [[yii\web\Session::close()|close()]] を複数回呼び出すことが出来ます。
内部的には、これらのメソッドは、セッションが既に開かれているかどうかを最初にチェックします。
36 37


38
### セッションデータにアクセスする <a name="access-session-data"></a>
39

40
セッションに保存されているデータにアクセスするためには、次のようにすることが出来ます。
41 42 43 44

```php
$session = Yii::$app->session;

45
// セッション変数を取得する。次の三つの用法は同義。
46 47 48 49
$language = $session->get('language');
$language = $session['language'];
$language = isset($_SESSION['language']) ? $_SESSION['language'] : null;

50
// セッション変数を設定する。次の三つの用法は同義。
51 52 53 54
$session->set('language', 'en-US');
$session['language'] = 'en-US';
$_SESSION['language'] = 'en-US';

55
// セッション変数を削除する。次の三つの用法は同義。
56 57 58 59
$session->remove('language');
unset($session['language']);
unset($_SESSION['language']);

60
// セッション変数が存在するかどうかをチェックする。次の三つの用法は同義。
61 62 63 64
if ($session->has('language')) ...
if (isset($session['language'])) ...
if (isset($_SESSION['language'])) ...

65
// 全てのセッション変数をたどる。次の二つの用法は同義。
66 67 68 69
foreach ($session as $name => $value) ...
foreach ($_SESSION as $name => $value) ...
```

70 71
> Info|情報: セッションデータに `session` コンポーネントによってアクセスする場合は、まだ開かれていないときは、自動的にセッションが開かれます。
  これに対して `$_SESSION` によってセッションデータにアクセスする場合は、`session_start()` を明示的に呼び出すことが必要になります。
72

73
配列であるセッションデータを扱う場合、`session` コンポーネントには、配列の要素を直接修正することができない、という制約があります。例えば、
74 75 76 77

```php
$session = Yii::$app->session;

78
// 次のコードは動かない
79 80 81
$session['captcha']['number'] = 5;
$session['captcha']['lifetime'] = 3600;

82
// 次のコードは動く
83 84 85 86 87
$session['captcha'] = [
    'number' => 5,
    'lifetime' => 3600,
];

88
// 次のコードも動く
89 90 91
echo $session['captcha']['lifetime'];
```

92
次の回避策のどれかを使ってこの問題を解決することが出来ます。
93 94 95 96

```php
$session = Yii::$app->session;

97
// $_SESSION を直接使う (既に Yii::$app->session->open() が呼び出されていることを確認)
98 99 100
$_SESSION['captcha']['number'] = 5;
$_SESSION['captcha']['lifetime'] = 3600;

101
// 配列全体を取得し、修正して、保存しなおす
102 103 104 105 106
$captcha = $session['captcha'];
$captcha['number'] = 5;
$captcha['lifetime'] = 3600;
$session['captcha'] = $captcha;

107
// 配列の代わりに ArrayObject を使う
108 109 110 111 112
$session['captcha'] = new \ArrayObject;
...
$session['captcha']['number'] = 5;
$session['captcha']['lifetime'] = 3600;

113
// 共通の接頭辞を持つキーを使って配列データを保存する
114 115 116 117
$session['captcha.number'] = 5;
$session['captcha.lifetime'] = 3600;
```

118 119
パフォーマンスとコードの可読性を高めるためには、最後の回避策を推奨します。
すなわち、配列を一つのセッション変数として保存する代りに、配列の個々の要素を他の要素と同じキー接頭辞を共有する一つのセッション変数として保存することです。
120 121


122
### カスタムセッションストレージ <a name="custom-session-storage"></a>
123

124 125
既定の [[yii\web\Session]] クラスはセッションデータをサーバ上のファイルとして保存します。
Yii は、また、さまざまなセッションストレージを実装する下記のクラスをも提供しています。
126

127 128 129 130
* [[yii\web\DbSession]]: セッションデータをデータベーステーブルを使って保存する。
* [[yii\web\CacheSession]]: セッションデータを、構成された [キャッシュコンポーネント](caching-data.md#cache-components) の力を借りて、キャッシュを使って保存する。
* [[yii\redis\Session]]: セッションデータを [redis](http://redis.io/) をストレージ媒体として使って保存する。
* [[yii\mongodb\Session]]: セッションデータを [MongoDB](http://www.mongodb.org/) に保存する。
131

132 133
これらのセッションクラスは全て一連の同じ API メソッドをサポートします。
その結果として、セッションを使用するアプリケーションコードを修正することなしに、セッションストレージクラスを切り替えることが出来ます。
134

135
> Note|注意: カスタムセッションストレージを使っているときに `$_SESSION` を通じてセッションデータにアクセスしたい場合は、セッションが [[yii\web\Session::open()]] によって既に開始されていることを確認しなければなりません。
136
  これは、カスタムセッションストレージのハンドラが、このメソッドの中で登録されるからです。
137

138
これらのコンポーネントクラスの構成方法と使用方法については、それらの API ドキュメントを参照してください。
139
下記の例は、アプリケーションの構成情報において、データベーステーブルをセッションストレージとして使うために [[yii\web\DbSession]] を構成する方法を示すものです。
140 141 142 143 144 145

```php
return [
    'components' => [
        'session' => [
            'class' => 'yii\web\DbSession',
146 147
            // 'db' => 'mydb',  // DB 接続のアプリケーションコンポーネント ID。デフォルトは 'db'。
            // 'sessionTable' => 'my_session', // セッションテーブル名。デフォルトは 'session'。
148 149 150 151 152
        ],
    ],
];
```

153
セッションデータを保存するために、次のようなデータベーステーブルを作成することも必要です。
154 155 156 157 158 159 160 161 162 163

```sql
CREATE TABLE session
(
    id CHAR(40) NOT NULL PRIMARY KEY,
    expire INTEGER,
    data BLOB
)
```

164
ここで 'BLOB' はあなたが選んだ DBMS の BLOB 型を指します。下記は人気のあるいくつかの DBMS で使用できる BLOB 型です。
165 166 167 168 169

- MySQL: LONGBLOB
- PostgreSQL: BYTEA
- MSSQL: BLOB

170 171
> Note|注意: php.ini の `session.hash_function` の設定によっては、`id` カラムの長さを修正する必要があるかも知れません。
  例えば、`session.hash_function=sha256` である場合は、40 の代りに 64 の長さを使わなければなりません。
172 173


174
### フラッシュデータ <a name="flash-data"></a>
175

176 177
フラッシュデータは特殊な種類のセッションデータで、あるリクエストの中で設定されると、次のリクエストの間においてのみ読み出すことが出来て、その後は自動的に削除されるものです。
フラッシュデータが最もよく使われるのは、エンドユーザに一度だけ表示されるべきメッセージ、例えば、ユーザのフォーム送信が成功した後に表示される確認メッセージなどを実装するときです。
178

179
`session` アプリケーションコンポーネントによって、フラッシュデータを設定し、アクセスすることが出来ます。例えば、
180 181 182 183

```php
$session = Yii::$app->session;

184 185 186
// リクエスト #1
// "postDeleted" という名前のフラッシュメッセージを設定する
$session->setFlash('postDeleted', '投稿の削除に成功しました。');
187

188 189
// リクエスト #2
// "postDeleted" という名前のフラッシュメッセージを表示する
190 191
echo $session->getFlash('postDeleted');

192 193
// リクエスト #3
// フラッシュメッセージは自動的に削除されるので、$result は false になる
194 195 196
$result = $session->hasFlash('postDeleted');
```

197
通常のセッションデータと同様に、任意のデータをフラッシュデータとして保存することが出来ます。
198

199 200 201
[[yii\web\Session::setFlash()]] を呼び出すと、同じ名前の既存のフラッシュデータは上書きされます。
同じ名前の既存のメッセージに新しいフラッシュデータを追加するためには、代りに [[yii\web\Session::addFlash()]] を使うことが出来ます。
例えば、
202 203 204 205

```php
$session = Yii::$app->session;

206 207 208 209 210
// リクエスト #1
// "alerts" という名前の下にフラッシュメッセージを追加する
$session->addFlash('alerts', '投稿の削除に成功しました。');
$session->addFlash('alerts', '友達の追加に成功しました。');
$session->addFlash('alerts', 'あなたのレベルが上りました。');
211

212 213
// リクエスト #2
// $alerts は "alerts" という名前の下にあるフラッシュメッセージの配列となる
214 215 216
$alerts = $session->getFlash('alerts');
```

217 218
> Note|注意: 同じ名前のフラッシュデータに対して、[[yii\web\Session::setFlash()]] と [[yii\web\Session::addFlash()]] を一緒に使わないようにしてください。
  これは、後者のメソッドが、同じ名前のフラッシュデータを追加できるように、フラッシュデータを自動的に配列に変換するからです。
219
  その結果、[[yii\web\Session::getFlash()]] を呼び出したとき、この二つのメソッドの呼び出し順によって、あるときは配列を受け取り、あるときは文字列を受け取るということになってしまいます。
220 221


222
## クッキー <a name="cookies"></a>
223

224 225 226
Yii は個々のクッキーを [[yii\web\Cookie]] のオブジェクトとして表します。
[[yii\web\Request]] と [[yii\web\Response]] は、ともに、`cookies` という名前のプロパティによって、クッキーのコレクションを保持します。
後者のクッキーコレクションはリクエストの中で送信されたクッキーを表し、一方、後者のクッキーコレクションは、ユーザに送信されることになるクッキーを表します。
227 228


229
### クッキーを読み出す <a name="reading-cookies"></a>
230

231
現在のリクエストに含まれるクッキーは、下記のコードを使って取得することが出来ます。
232 233

```php
234
// "request" コンポーネントからクッキーコレクション (yii\web\CookieCollection) を取得する。
235 236
$cookies = Yii::$app->request->cookies;

237
// "language" というクッキーの値を取得する。クッキーが存在しない場合は、デフォルト値として "en" を返す。
238 239
$language = $cookies->getValue('language', 'en');

240
// "language" というクッキーの値を取得する別の方法。
241 242 243 244
if (($cookie = $cookies->get('language')) !== null) {
    $language = $cookie->value;
}

245
// $cookies を配列のように使うことも出来る。
246 247 248 249
if (isset($cookies['language'])) {
    $language = $cookies['language']->value;
}

250
// "language" というクッキーが在るかどうかチェックする。
251 252 253 254 255
if ($cookies->has('language')) ...
if (isset($cookies['language'])) ...
```


256
### クッキーを送信する <a name="sending-cookies"></a>
257

258
下記のコードを使って、クッキーをエンドユーザに送信することが出来ます。
259 260

```php
261
// "response" コンポーネントからクッキーコレクション (yii\web\CookieCollection) を取得する。
262 263
$cookies = Yii::$app->response->cookies;

264
// 送信されるレスポンスに新しいクッキーを追加する。
265 266 267 268 269
$cookies->add(new \yii\web\Cookie([
    'name' => 'language',
    'value' => 'zh-CN',
]));

270
// クッキーを削除する。
271
$cookies->remove('language');
272
// 次のようにしても同じ。
273 274 275
unset($cookies['language']);
```

276 277
[[yii\web\Cookie]] クラスは、上記の例で示されている [[yii\web\Cookie::name|name]] と [[yii\web\Cookie::value|value]] のプロパティ以外にも、[[yii\web\Cookie::domain|domain]]
[[yii\web\Cookie::expire|expire]] など、他のプロパティを定義して、利用可能なクッキー情報の全てを完全に表しています。
278
クッキーを準備するときに必要に応じてこれらのプロパティを構成してから、レスポンスのクッキーコレクションに追加することが出来ます。
279

280 281 282
> Note|注意: セキュリティを向上させるために、[[yii\web\Cookie::httpOnly]] のデフォルト値は true に設定されています。
これは、クライアントサイドスクリプトが保護されたクッキーにアクセスする危険を軽減するものです (ブラウザがサポートしていれば)。
詳細については、[httpOnly wiki article](https://www.owasp.org/index.php/HttpOnly) を読んでください。
283

284
### クッキー検証 <a name="cookie-validation"></a>
285

286 287 288 289
最後の二つの項で示されているように、`request``response` のコンポーネントを通じてクッキーを読んだり送信したりする場合には、クッキーがクライアントサイドで修正されるのを防止するクッキー検証という追加のセキュリティを享受することが出来ます。
これは、個々のクッキーにハッシュ文字列をサインとして追加することによって達成されます。
アプリケーションは、サインを見て、クッキーがクライアントサイドで修正されたかどうかを知ることが出来ます。
もし、修正されていれば、そのクッキーは `request` コンポーネントの [[yii\web\Request::cookies|クッキーコレクション]] からはアクセスすることが出来なくなります。
290

291 292 293
> Note|注意: クッキー検証はクッキーの値が修正されるのを防止するだけです。
  クッキーが検証に失敗した場合でも、`$_COOKIE` を通じてそれにアクセスすることは引き続いて可能です。
  これは、サードパーティのライブラリが、クッキー検証を含まない、ライブラリ自体の方法でクッキーを操作し得るためです。
294

295 296
クッキー検証はデフォルトで有効になっています。
[[yii\web\Request::enableCookieValidation]] プロパティを false に設定することによって無効にすることが出来ますが、無効にしないことを強く推奨します。
297

298
> Note|注意: `$_COOKIE` と `setcookie()` によって直接に 読み出し/送信 されるクッキーは検証されません。
299

300
クッキー検証を使用する場合は、前述のハッシュ文字列を生成するために使用される [[yii\web\Request::cookieValidationKey]] を指定しなければなりません。
301
アプリケーションの構成情報で `request` コンポーネントを構成することによって、そうすることが出来ます。
302 303 304 305 306

```php
return [
    'components' => [
        'request' => [
307
            'cookieValidationKey' => 'ここに秘密のキーを書く',
308 309 310 311 312
        ],
    ],
];
```

313 314
> Info|情報: [[yii\web\Request::cookieValidationKey|cookieValidationKey]] は、あなたのアプリケーションにとって、決定的に重要なものです。
  これは信頼する人にだけ教えるべきものです。バージョンコントロールシステムに保存してはいけません。