runtime-sessions-cookies.md 12.4 KB
Newer Older
1 2 3
Sessions and Cookies
====================

4
Sessions and cookies allow data to be persisted across multiple user requests. In plain PHP you may access them
5
through the global variables `$_SESSION` and `$_COOKIE`, respectively. Yii encapsulates sessions and cookies as objects
6
and thus allows you to access them in an object-oriented fashion with additional useful enhancements.
7 8 9 10 11 12


## Sessions <a name="sessions"></a>

Like [requests](runtime-requests.md) and [responses](runtime-responses.md), you can get access to sessions via
the `session` [application component](structure-application-components.md) which is an instance of [[yii\web\Session]],
13
by default.
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35


### Opening and Closing Sessions <a name="opening-closing-sessions"></a>

To open and close a session, you can do the following:

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

// check if a session is already open
if ($session->isActive) ...

// open a session
$session->open();

// close a session
$session->close();

// destroys all data registered to a session.
$session->destroy();
```

36 37
You can call [[yii\web\Session::open()|open()]] and [[yii\web\Session::close()|close()]] multiple times
without causing errors; internally the methods will first check if the session is already open.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76


### Accessing Session Data <a name="access-session-data"></a>

To access the data stored in session, you can do the following:

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

// get a session variable. The following usages are equivalent:
$language = $session->get('language');
$language = $session['language'];
$language = isset($_SESSION['language']) ? $_SESSION['language'] : null;

// set a session variable. The following usages are equivalent:
$session->set('language', 'en-US');
$session['language'] = 'en-US';
$_SESSION['language'] = 'en-US';

// remove a session variable. The following usages are equivalent:
$session->remove('language');
unset($session['language']);
unset($_SESSION['language']);

// check if a session variable exists. The following usages are equivalent:
if ($session->has('language')) ...
if (isset($session['language'])) ...
if (isset($_SESSION['language'])) ...

// traverse all session variables. The following usages are equivalent:
foreach ($session as $name => $value) ...
foreach ($_SESSION as $name => $value) ...
```

> Info: When you access session data through the `session` component, a session will be automatically opened
if it has not been done so before. This is different from accessing session data through `$_SESSION`, which requires
an explicit call of `session_start()`.

When working with session data that are arrays, the `session` component has a limitation which prevents you from
77
directly modifying an array element. For example,
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104

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

// the following code will NOT work
$session['captcha']['number'] = 5;
$session['captcha']['lifetime'] = 3600;

// the following code works:
$session['captcha'] = [
    'number' => 5,
    'lifetime' => 3600,
];

// the following code also works:
echo $session['captcha']['lifetime'];
```

You can use one of the following workarounds to solve this problem:

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

// directly use $_SESSION (make sure Yii::$app->session->open() has been called)
$_SESSION['captcha']['number'] = 5;
$_SESSION['captcha']['lifetime'] = 3600;

105
// get the whole array first, modify it and then save it back
106 107 108 109 110 111 112 113 114 115 116
$captcha = $session['captcha'];
$captcha['number'] = 5;
$captcha['lifetime'] = 3600;
$session['captcha'] = $captcha;

// use ArrayObject instead of array
$session['captcha'] = new \ArrayObject;
...
$session['captcha']['number'] = 5;
$session['captcha']['lifetime'] = 3600;

117
// store array data by keys with a common prefix
118 119 120 121 122 123 124
$session['captcha.number'] = 5;
$session['captcha.lifetime'] = 3600;
```

For better performance and code readability, we recommend the last workaround. That is, instead of storing
an array as a single session variable, you store each array element as a session variable which shares the same
key prefix with other array elements.
125

126 127 128 129 130 131 132 133

### Custom Session Storage <a name="custom-session-storage"></a>

The default [[yii\web\Session]] class stores session data as files on the server. Yii also provides the following
session classes implementing different session storage:

* [[yii\web\DbSession]]: stores session data in a database table.
* [[yii\web\CacheSession]]: stores session data in a cache with the help of a configured [cache component](caching-data.md#cache-components).
pana1990 committed
134
* [[yii\redis\Session]]: stores session data using [redis](http://redis.io/) as the storage medium.
135 136
* [[yii\mongodb\Session]]: stores session data in a [MongoDB](http://www.mongodb.org/).

137 138
All these session classes support the same set of API methods. As a result, you can switch to a different
session storage class without the need to modify your application code that uses sessions.
139 140

> Note: If you want to access session data via `$_SESSION` while using custom session storage, you must make
141
  sure that the session has already been started by [[yii\web\Session::open()]]. This is because custom session storage
142 143 144
  handlers are registered within this method.

To learn how to configure and use these component classes, please refer to their API documentation. Below is
145 146
an example showing how to configure [[yii\web\DbSession]] in the application configuration to use a database table
for session storage:
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170

```php
return [
    'components' => [
        'session' => [
            'class' => 'yii\web\DbSession',
            // 'db' => 'mydb',  // the application component ID of the DB connection. Defaults to 'db'.
            // 'sessionTable' => 'my_session', // session table name. Defaults to 'session'.
        ],
    ],
];
```

You also need to create the following database table to store session data:

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

171
where 'BLOB' refers to the BLOB-type of your preferred DBMS. Below are the BLOB types that can be used for some popular DBMS:
172 173 174 175 176

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

Qiang Xue committed
177
> Note: According to the php.ini setting of `session.hash_function`, you may need to adjust
178
  the length of the `id` column. For example, if `session.hash_function=sha256`, you should use a
Qiang Xue committed
179
  length 64 instead of 40.
180

181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196

### Flash Data <a name="flash-data"></a>

Flash data is a special kind of session data which, once set in one request, will only be available during
the next request and will be automatically deleted afterwards. Flash data is most commonly used to implement
messages that should only be displayed to end users once, such as a confirmation message displayed after
a user successfully submits a form.

You can set and access flash data through the `session` application component. For example,

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

// Request #1
// set a flash message named as "postDeleted"
$session->setFlash('postDeleted', 'You have successfully deleted your post.');
197

198 199 200 201 202 203 204 205 206
// Request #2
// display the flash message named "postDeleted"
echo $session->getFlash('postDeleted');

// Request #3
// $result will be false since the flash message was automatically deleted
$result = $session->hasFlash('postDeleted');
```

207 208
Like regular session data, you can store arbitrary data as flash data.

209
When you call [[yii\web\Session::setFlash()]], it will overwrite any existing flash data that has the same name.
210
To append new flash data to an existing message of the same name, you may call [[yii\web\Session::addFlash()]] instead.
211
For example:
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227

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

// Request #1
// add a few flash messages under the name of "alerts"
$session->addFlash('alerts', 'You have successfully deleted your post.');
$session->addFlash('alerts', 'You have successfully added a new friend.');
$session->addFlash('alerts', 'You are promoted.');

// Request #2
// $alerts is an array of the flash messages under the name of "alerts"
$alerts = $session->getFlash('alerts');
```

> Note: Try not to use [[yii\web\Session::setFlash()]] together with [[yii\web\Session::addFlash()]] for flash data
228 229
  of the same name. This is because the latter method will automatically turn the flash data into an array so that it
  can append new flash data of the same name. As a result, when you call [[yii\web\Session::getFlash()]], you may
230 231 232 233 234 235 236
  find sometimes you are getting an array while sometimes you are getting a string, depending on the order of
  the invocation of these two methods.


## Cookies <a name="cookies"></a>

Yii represents each cookie as an object of [[yii\web\Cookie]]. Both [[yii\web\Request]] and [[yii\web\Response]]
237 238
maintain a collection of cookies via the property named `cookies`. The cookie collection in the former represents
the cookies submitted in a request, while the cookie collection in the latter represents the cookies that are to
239 240 241 242 243 244 245 246
be sent to the user.


### Reading Cookies <a name="reading-cookies"></a>

You can get the cookies in the current request using the following code:

```php
247
// get the cookie collection (yii\web\CookieCollection) from the "request" component
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
$cookies = Yii::$app->request->cookies;

// get the "language" cookie value. If the cookie does not exist, return "en" as the default value.
$language = $cookies->getValue('language', 'en');

// an alternative way of getting the "language" cookie value
if (($cookie = $cookies->get('language')) !== null) {
    $language = $cookie->value;
}

// you may also use $cookies like an array
if (isset($cookies['language'])) {
    $language = $cookies['language']->value;
}

// check if there is a "language" cookie
if ($cookies->has('language')) ...
if (isset($cookies['language'])) ...
```


### Sending Cookies <a name="sending-cookies"></a>

You can send cookies to end users using the following code:

```php
274
// get the cookie collection (yii\web\CookieCollection) from the "response" component
275 276 277 278 279 280
$cookies = Yii::$app->response->cookies;

// add a new cookie to the response to be sent
$cookies->add(new \yii\web\Cookie([
    'name' => 'language',
    'value' => 'zh-CN',
281
]));
282 283

// remove a cookie
284
$cookies->remove('language');
285 286 287 288 289
// equivalent to the following
unset($cookies['language']);
```

Besides the [[yii\web\Cookie::name|name]], [[yii\web\Cookie::value|value]] properties shown in the above
290 291
examples, the [[yii\web\Cookie]] class also defines other properties to fully represent all available cookie 
information, such as [[yii\web\Cookie::domain|domain]], [[yii\web\Cookie::expire|expire]]. You may configure these
292 293
properties as needed to prepare a cookie and then add it to the response's cookie collection.

294 295
> Note: For better security, the default value of [[yii\web\Cookie::httpOnly]] is set to true. This helps mitigate
the risk of a client side script accessing the protected cookie (if the browser supports it). You may read
296
the [httpOnly wiki article](https://www.owasp.org/index.php/HttpOnly) for more details.
Qiang Xue committed
297

298 299 300

### Cookie Validation <a name="cookie-validation"></a>

301
When you are reading and sending cookies through the `request` and `response` components as shown in the last
302
two subsections, you enjoy the added security of cookie validation which protects cookies from being modified
Qiang Xue committed
303
on the client side. This is achieved by signing each cookie with a hash string, which allows the application to
304
tell if a cookie has been modified on the client side. If so, the cookie will NOT be accessible through the
305
[[yii\web\Request::cookies|cookie collection]] of the `request` component.
Qiang Xue committed
306

Qiang Xue committed
307 308 309
> Note: Cookie validation only protects cookie values from being modified. If a cookie fails the validation, 
you may still access it through `$_COOKIE`. This is because third-party libraries may manipulate cookies 
in their own way, which does not involve cookie validation.
310

311
Cookie validation is enabled by default. You can disable it by setting the [[yii\web\Request::enableCookieValidation]]
Qiang Xue committed
312
property to be false, although we strongly recommend you do not do so.
313

314
> Note: Cookies that are directly read/sent via `$_COOKIE` and `setcookie()` will NOT be validated.
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329

When using cookie validation, you must specify a [[yii\web\Request::cookieValidationKey]] that will be used to generate
the aforementioned hash strings. You can do so by configuring the `request` component in the application configuration:

```php
return [
    'components' => [
        'request' => [
            'cookieValidationKey' => 'fill in a secret key here',
        ],
    ],
];
```

> Info: [[yii\web\Request::cookieValidationKey|cookieValidationKey]] is critical to your application's security.
330
  It should only be known to people you trust. Do not store it in the version control system.