|
|
|
Sessions and Cookies
|
|
|
|
====================
|
|
|
|
|
|
|
|
Sessions and cookies allow data to be persisted across multiple user requests. In plain PHP you may access them
|
|
|
|
through the global variables `$_SESSION` and `$_COOKIE`, respectively. Yii encapsulates sessions and cookies as objects
|
|
|
|
and thus allows you to access them in an object-oriented fashion with additional useful enhancements.
|
|
|
|
|
|
|
|
|
|
|
|
## Sessions <span id="sessions"></span>
|
|
|
|
|
|
|
|
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]],
|
|
|
|
by default.
|
|
|
|
|
|
|
|
|
|
|
|
### Opening and Closing Sessions <span id="opening-closing-sessions"></span>
|
|
|
|
|
|
|
|
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();
|
|
|
|
```
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
|
|
### Accessing Session Data <span id="access-session-data"></span>
|
|
|
|
|
|
|
|
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
|
|
|
|
directly modifying an array element. For example,
|
|
|
|
|
|
|
|
```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;
|
|
|
|
|
|
|
|
// get the whole array first, modify it and then save it back
|
|
|
|
$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;
|
|
|
|
|
|
|
|
// store array data by keys with a common prefix
|
|
|
|
$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.
|
|
|
|
|
|
|
|
|
|
|
|
### Custom Session Storage <span id="custom-session-storage"></span>
|
|
|
|
|
|
|
|
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).
|
|
|
|
* [[yii\redis\Session]]: stores session data using [redis](http://redis.io/) as the storage medium.
|
|
|
|
* [[yii\mongodb\Session]]: stores session data in a [MongoDB](http://www.mongodb.org/).
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
> Note: If you want to access session data via `$_SESSION` while using custom session storage, you must make
|
|
|
|
sure that the session has already been started by [[yii\web\Session::open()]]. This is because custom session storage
|
|
|
|
handlers are registered within this method.
|
|
|
|
|
|
|
|
To learn how to configure and use these component classes, please refer to their API documentation. Below is
|
|
|
|
an example showing how to configure [[yii\web\DbSession]] in the application configuration to use a database table
|
|
|
|
for session storage:
|
|
|
|
|
|
|
|
```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
|
|
|
|
)
|
|
|
|
```
|
|
|
|
|
|
|
|
where 'BLOB' refers to the BLOB-type of your preferred DBMS. Below are the BLOB types that can be used for some popular DBMS:
|
|
|
|
|
|
|
|
- MySQL: LONGBLOB
|
|
|
|
- PostgreSQL: BYTEA
|
|
|
|
- MSSQL: BLOB
|
|
|
|
|
|
|
|
> Note: According to the php.ini setting of `session.hash_function`, you may need to adjust
|
|
|
|
the length of the `id` column. For example, if `session.hash_function=sha256`, you should use a
|
|
|
|
length 64 instead of 40.
|
|
|
|
|
|
|
|
|
|
|
|
### Flash Data <span id="flash-data"></span>
|
|
|
|
|
|
|
|
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.');
|
|
|
|
|
|
|
|
// 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');
|
|
|
|
```
|
|
|
|
|
|
|
|
Like regular session data, you can store arbitrary data as flash data.
|
|
|
|
|
|
|
|
When you call [[yii\web\Session::setFlash()]], it will overwrite any existing flash data that has the same name.
|
|
|
|
To append new flash data to an existing message of the same name, you may call [[yii\web\Session::addFlash()]] instead.
|
|
|
|
For example:
|
|
|
|
|
|
|
|
```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
|
|
|
|
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
|
|
|
|
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 <span id="cookies"></span>
|
|
|
|
|
|
|
|
Yii represents each cookie as an object of [[yii\web\Cookie]]. Both [[yii\web\Request]] and [[yii\web\Response]]
|
|
|
|
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
|
|
|
|
be sent to the user.
|
|
|
|
|
|
|
|
|
|
|
|
### Reading Cookies <span id="reading-cookies"></span>
|
|
|
|
|
|
|
|
You can get the cookies in the current request using the following code:
|
|
|
|
|
|
|
|
```php
|
|
|
|
// get the cookie collection (yii\web\CookieCollection) from the "request" component
|
|
|
|
$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 <span id="sending-cookies"></span>
|
|
|
|
|
|
|
|
You can send cookies to end users using the following code:
|
|
|
|
|
|
|
|
```php
|
|
|
|
// get the cookie collection (yii\web\CookieCollection) from the "response" component
|
|
|
|
$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',
|
|
|
|
]));
|
|
|
|
|
|
|
|
// remove a cookie
|
|
|
|
$cookies->remove('language');
|
|
|
|
// equivalent to the following
|
|
|
|
unset($cookies['language']);
|
|
|
|
```
|
|
|
|
|
|
|
|
Besides the [[yii\web\Cookie::name|name]], [[yii\web\Cookie::value|value]] properties shown in the above
|
|
|
|
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
|
|
|
|
properties as needed to prepare a cookie and then add it to the response's cookie collection.
|
|
|
|
|
|
|
|
> 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
|
|
|
|
the [httpOnly wiki article](https://www.owasp.org/index.php/HttpOnly) for more details.
|
|
|
|
|
|
|
|
|
|
|
|
### Cookie Validation <span id="cookie-validation"></span>
|
|
|
|
|
|
|
|
When you are reading and sending cookies through the `request` and `response` components as shown in the last
|
|
|
|
two subsections, you enjoy the added security of cookie validation which protects cookies from being modified
|
|
|
|
on the client side. This is achieved by signing each cookie with a hash string, which allows the application to
|
|
|
|
tell if a cookie has been modified on the client side. If so, the cookie will NOT be accessible through the
|
|
|
|
[[yii\web\Request::cookies|cookie collection]] of the `request` component.
|
|
|
|
|
|
|
|
> 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.
|
|
|
|
|
|
|
|
Cookie validation is enabled by default. You can disable it by setting the [[yii\web\Request::enableCookieValidation]]
|
|
|
|
property to be false, although we strongly recommend you do not do so.
|
|
|
|
|
|
|
|
> Note: Cookies that are directly read/sent via `$_COOKIE` and `setcookie()` will NOT be validated.
|
|
|
|
|
|
|
|
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.
|
|
|
|
It should only be known to people you trust. Do not store it in the version control system.
|