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

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


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

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


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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

91
// 次のコードも動く
92 93 94
echo $session['captcha']['lifetime'];
```

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

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

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

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

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

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

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


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

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

129 130 131 132
* [[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/) に保存する。
133

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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


228
## クッキー <a name="cookies"></a>
229

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


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

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

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

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

247
// "language" というクッキーの値を取得する別の方法。
248 249 250 251
if (($cookie = $cookies->get('language')) !== null) {
    $language = $cookie->value;
}

252
// $cookies を配列のように使うことも出来る。
253 254 255 256
if (isset($cookies['language'])) {
    $language = $cookies['language']->value;
}

257
// "language" というクッキーが在るかどうかチェックする。
258 259 260 261 262
if ($cookies->has('language')) ...
if (isset($cookies['language'])) ...
```


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

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

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

271
// 送信されるレスポンスに新しいクッキーを追加する。
272 273 274 275 276
$cookies->add(new \yii\web\Cookie([
    'name' => 'language',
    'value' => 'zh-CN',
]));

277
// クッキーを削除する。
278
$cookies->remove('language');
279
// 次のようにしても同じ。
280 281 282
unset($cookies['language']);
```

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

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

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

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

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

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

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

308 309
クッキー検証を使用する場合は、前述のハッシュ文字列を生成するために使用される [[yii\web\Request::cookieValidationKey]] を指定しなければなりません。
アプリケーションのコンフィギュレーションで `request` コンポーネントを構成することによって、そうすることが出来ます。
310 311 312 313 314

```php
return [
    'components' => [
        'request' => [
315
            'cookieValidationKey' => 'ここに秘密のキーを書く',
316 317 318 319 320
        ],
    ],
];
```

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