Init
This commit is contained in:
751
output/12.x/session.md
Normal file
751
output/12.x/session.md
Normal file
@@ -0,0 +1,751 @@
|
||||
# HTTP Session
|
||||
|
||||
* Introduction
|
||||
* Configuration
|
||||
* Driver Prerequisites
|
||||
* Interacting With the Session
|
||||
* Retrieving Data
|
||||
* Storing Data
|
||||
* Flash Data
|
||||
* Deleting Data
|
||||
* Regenerating the Session ID
|
||||
* Session Blocking
|
||||
* Adding Custom Session Drivers
|
||||
* Implementing the Driver
|
||||
* Registering the Driver
|
||||
|
||||
## Introduction
|
||||
|
||||
Since HTTP driven applications are stateless, sessions provide a way to store
|
||||
information about the user across multiple requests. That user information is
|
||||
typically placed in a persistent store / backend that can be accessed from
|
||||
subsequent requests.
|
||||
|
||||
Laravel ships with a variety of session backends that are accessed through an
|
||||
expressive, unified API. Support for popular backends such as
|
||||
[Memcached](https://memcached.org), [Redis](https://redis.io), and databases
|
||||
is included.
|
||||
|
||||
### Configuration
|
||||
|
||||
Your application's session configuration file is stored at
|
||||
`config/session.php`. Be sure to review the options available to you in this
|
||||
file. By default, Laravel is configured to use the `database` session driver.
|
||||
|
||||
The session `driver` configuration option defines where session data will be
|
||||
stored for each request. Laravel includes a variety of drivers:
|
||||
|
||||
* `file` \- sessions are stored in `storage/framework/sessions`.
|
||||
* `cookie` \- sessions are stored in secure, encrypted cookies.
|
||||
* `database` \- sessions are stored in a relational database.
|
||||
* `memcached` / `redis` \- sessions are stored in one of these fast, cache-based stores.
|
||||
* `dynamodb` \- sessions are stored in AWS DynamoDB.
|
||||
* `array` \- sessions are stored in a PHP array and will not be persisted.
|
||||
|
||||
The array driver is primarily used during [testing](/docs/12.x/testing) and
|
||||
prevents the data stored in the session from being persisted.
|
||||
|
||||
### Driver Prerequisites
|
||||
|
||||
#### Database
|
||||
|
||||
When using the `database` session driver, you will need to ensure that you
|
||||
have a database table to contain the session data. Typically, this is included
|
||||
in Laravel's default `0001_01_01_000000_create_users_table.php` [database
|
||||
migration](/docs/12.x/migrations); however, if for any reason you do not have
|
||||
a `sessions` table, you may use the `make:session-table` Artisan command to
|
||||
generate this migration:
|
||||
|
||||
|
||||
|
||||
1php artisan make:session-table
|
||||
|
||||
2
|
||||
|
||||
3php artisan migrate
|
||||
|
||||
|
||||
php artisan make:session-table
|
||||
|
||||
php artisan migrate
|
||||
|
||||
#### Redis
|
||||
|
||||
Before using Redis sessions with Laravel, you will need to either install the
|
||||
PhpRedis PHP extension via PECL or install the `predis/predis` package (~1.0)
|
||||
via Composer. For more information on configuring Redis, consult Laravel's
|
||||
[Redis documentation](/docs/12.x/redis#configuration).
|
||||
|
||||
The `SESSION_CONNECTION` environment variable, or the `connection` option in
|
||||
the `session.php` configuration file, may be used to specify which Redis
|
||||
connection is used for session storage.
|
||||
|
||||
## Interacting With the Session
|
||||
|
||||
### Retrieving Data
|
||||
|
||||
There are two primary ways of working with session data in Laravel: the global
|
||||
`session` helper and via a `Request` instance. First, let's look at accessing
|
||||
the session via a `Request` instance, which can be type-hinted on a route
|
||||
closure or controller method. Remember, controller method dependencies are
|
||||
automatically injected via the Laravel [service
|
||||
container](/docs/12.x/container):
|
||||
|
||||
|
||||
|
||||
1<?php
|
||||
|
||||
2
|
||||
|
||||
3namespace App\Http\Controllers;
|
||||
|
||||
4
|
||||
|
||||
5use Illuminate\Http\Request;
|
||||
|
||||
6use Illuminate\View\View;
|
||||
|
||||
7
|
||||
|
||||
8class UserController extends Controller
|
||||
|
||||
9{
|
||||
|
||||
10 /**
|
||||
|
||||
11 * Show the profile for the given user.
|
||||
|
||||
12 */
|
||||
|
||||
13 public function show(Request $request, string $id): View
|
||||
|
||||
14 {
|
||||
|
||||
15 $value = $request->session()->get('key');
|
||||
|
||||
16
|
||||
|
||||
17 // ...
|
||||
|
||||
18
|
||||
|
||||
19 $user = $this->users->find($id);
|
||||
|
||||
20
|
||||
|
||||
21 return view('user.profile', ['user' => $user]);
|
||||
|
||||
22 }
|
||||
|
||||
23}
|
||||
|
||||
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show the profile for the given user.
|
||||
*/
|
||||
public function show(Request $request, string $id): View
|
||||
{
|
||||
$value = $request->session()->get('key');
|
||||
|
||||
// ...
|
||||
|
||||
$user = $this->users->find($id);
|
||||
|
||||
return view('user.profile', ['user' => $user]);
|
||||
}
|
||||
}
|
||||
|
||||
When you retrieve an item from the session, you may also pass a default value
|
||||
as the second argument to the `get` method. This default value will be
|
||||
returned if the specified key does not exist in the session. If you pass a
|
||||
closure as the default value to the `get` method and the requested key does
|
||||
not exist, the closure will be executed and its result returned:
|
||||
|
||||
|
||||
|
||||
1$value = $request->session()->get('key', 'default');
|
||||
|
||||
2
|
||||
|
||||
3$value = $request->session()->get('key', function () {
|
||||
|
||||
4 return 'default';
|
||||
|
||||
5});
|
||||
|
||||
|
||||
$value = $request->session()->get('key', 'default');
|
||||
|
||||
$value = $request->session()->get('key', function () {
|
||||
return 'default';
|
||||
});
|
||||
|
||||
#### The Global Session Helper
|
||||
|
||||
You may also use the global `session` PHP function to retrieve and store data
|
||||
in the session. When the `session` helper is called with a single, string
|
||||
argument, it will return the value of that session key. When the helper is
|
||||
called with an array of key / value pairs, those values will be stored in the
|
||||
session:
|
||||
|
||||
|
||||
|
||||
1Route::get('/home', function () {
|
||||
|
||||
2 // Retrieve a piece of data from the session...
|
||||
|
||||
3 $value = session('key');
|
||||
|
||||
4
|
||||
|
||||
5 // Specifying a default value...
|
||||
|
||||
6 $value = session('key', 'default');
|
||||
|
||||
7
|
||||
|
||||
8 // Store a piece of data in the session...
|
||||
|
||||
9 session(['key' => 'value']);
|
||||
|
||||
10});
|
||||
|
||||
|
||||
Route::get('/home', function () {
|
||||
// Retrieve a piece of data from the session...
|
||||
$value = session('key');
|
||||
|
||||
// Specifying a default value...
|
||||
$value = session('key', 'default');
|
||||
|
||||
// Store a piece of data in the session...
|
||||
session(['key' => 'value']);
|
||||
});
|
||||
|
||||
There is little practical difference between using the session via an HTTP
|
||||
request instance versus using the global `session` helper. Both methods are
|
||||
[testable](/docs/12.x/testing) via the `assertSessionHas` method which is
|
||||
available in all of your test cases.
|
||||
|
||||
#### Retrieving All Session Data
|
||||
|
||||
If you would like to retrieve all the data in the session, you may use the
|
||||
`all` method:
|
||||
|
||||
|
||||
|
||||
1$data = $request->session()->all();
|
||||
|
||||
|
||||
$data = $request->session()->all();
|
||||
|
||||
#### Retrieving a Portion of the Session Data
|
||||
|
||||
The `only` and `except` methods may be used to retrieve a subset of the
|
||||
session data:
|
||||
|
||||
|
||||
|
||||
1$data = $request->session()->only(['username', 'email']);
|
||||
|
||||
2
|
||||
|
||||
3$data = $request->session()->except(['username', 'email']);
|
||||
|
||||
|
||||
$data = $request->session()->only(['username', 'email']);
|
||||
|
||||
$data = $request->session()->except(['username', 'email']);
|
||||
|
||||
#### Determining if an Item Exists in the Session
|
||||
|
||||
To determine if an item is present in the session, you may use the `has`
|
||||
method. The `has` method returns `true` if the item is present and is not
|
||||
`null`:
|
||||
|
||||
|
||||
|
||||
1if ($request->session()->has('users')) {
|
||||
|
||||
2 // ...
|
||||
|
||||
3}
|
||||
|
||||
|
||||
if ($request->session()->has('users')) {
|
||||
// ...
|
||||
}
|
||||
|
||||
To determine if an item is present in the session, even if its value is
|
||||
`null`, you may use the `exists` method:
|
||||
|
||||
|
||||
|
||||
1if ($request->session()->exists('users')) {
|
||||
|
||||
2 // ...
|
||||
|
||||
3}
|
||||
|
||||
|
||||
if ($request->session()->exists('users')) {
|
||||
// ...
|
||||
}
|
||||
|
||||
To determine if an item is not present in the session, you may use the
|
||||
`missing` method. The `missing` method returns `true` if the item is not
|
||||
present:
|
||||
|
||||
|
||||
|
||||
1if ($request->session()->missing('users')) {
|
||||
|
||||
2 // ...
|
||||
|
||||
3}
|
||||
|
||||
|
||||
if ($request->session()->missing('users')) {
|
||||
// ...
|
||||
}
|
||||
|
||||
### Storing Data
|
||||
|
||||
To store data in the session, you will typically use the request instance's
|
||||
`put` method or the global `session` helper:
|
||||
|
||||
|
||||
|
||||
1// Via a request instance...
|
||||
|
||||
2$request->session()->put('key', 'value');
|
||||
|
||||
3
|
||||
|
||||
4// Via the global "session" helper...
|
||||
|
||||
5session(['key' => 'value']);
|
||||
|
||||
|
||||
// Via a request instance...
|
||||
$request->session()->put('key', 'value');
|
||||
|
||||
// Via the global "session" helper...
|
||||
session(['key' => 'value']);
|
||||
|
||||
#### Pushing to Array Session Values
|
||||
|
||||
The `push` method may be used to push a new value onto a session value that is
|
||||
an array. For example, if the `user.teams` key contains an array of team
|
||||
names, you may push a new value onto the array like so:
|
||||
|
||||
|
||||
|
||||
1$request->session()->push('user.teams', 'developers');
|
||||
|
||||
|
||||
$request->session()->push('user.teams', 'developers');
|
||||
|
||||
#### Retrieving and Deleting an Item
|
||||
|
||||
The `pull` method will retrieve and delete an item from the session in a
|
||||
single statement:
|
||||
|
||||
|
||||
|
||||
1$value = $request->session()->pull('key', 'default');
|
||||
|
||||
|
||||
$value = $request->session()->pull('key', 'default');
|
||||
|
||||
#### Incrementing and Decrementing Session Values
|
||||
|
||||
If your session data contains an integer you wish to increment or decrement,
|
||||
you may use the `increment` and `decrement` methods:
|
||||
|
||||
|
||||
|
||||
1$request->session()->increment('count');
|
||||
|
||||
2
|
||||
|
||||
3$request->session()->increment('count', $incrementBy = 2);
|
||||
|
||||
4
|
||||
|
||||
5$request->session()->decrement('count');
|
||||
|
||||
6
|
||||
|
||||
7$request->session()->decrement('count', $decrementBy = 2);
|
||||
|
||||
|
||||
$request->session()->increment('count');
|
||||
|
||||
$request->session()->increment('count', $incrementBy = 2);
|
||||
|
||||
$request->session()->decrement('count');
|
||||
|
||||
$request->session()->decrement('count', $decrementBy = 2);
|
||||
|
||||
### Flash Data
|
||||
|
||||
Sometimes you may wish to store items in the session for the next request. You
|
||||
may do so using the `flash` method. Data stored in the session using this
|
||||
method will be available immediately and during the subsequent HTTP request.
|
||||
After the subsequent HTTP request, the flashed data will be deleted. Flash
|
||||
data is primarily useful for short-lived status messages:
|
||||
|
||||
|
||||
|
||||
1$request->session()->flash('status', 'Task was successful!');
|
||||
|
||||
|
||||
$request->session()->flash('status', 'Task was successful!');
|
||||
|
||||
If you need to persist your flash data for several requests, you may use the
|
||||
`reflash` method, which will keep all of the flash data for an additional
|
||||
request. If you only need to keep specific flash data, you may use the `keep`
|
||||
method:
|
||||
|
||||
|
||||
|
||||
1$request->session()->reflash();
|
||||
|
||||
2
|
||||
|
||||
3$request->session()->keep(['username', 'email']);
|
||||
|
||||
|
||||
$request->session()->reflash();
|
||||
|
||||
$request->session()->keep(['username', 'email']);
|
||||
|
||||
To persist your flash data only for the current request, you may use the `now`
|
||||
method:
|
||||
|
||||
|
||||
|
||||
1$request->session()->now('status', 'Task was successful!');
|
||||
|
||||
|
||||
$request->session()->now('status', 'Task was successful!');
|
||||
|
||||
### Deleting Data
|
||||
|
||||
The `forget` method will remove a piece of data from the session. If you would
|
||||
like to remove all data from the session, you may use the `flush` method:
|
||||
|
||||
|
||||
|
||||
1// Forget a single key...
|
||||
|
||||
2$request->session()->forget('name');
|
||||
|
||||
3
|
||||
|
||||
4// Forget multiple keys...
|
||||
|
||||
5$request->session()->forget(['name', 'status']);
|
||||
|
||||
6
|
||||
|
||||
7$request->session()->flush();
|
||||
|
||||
|
||||
// Forget a single key...
|
||||
$request->session()->forget('name');
|
||||
|
||||
// Forget multiple keys...
|
||||
$request->session()->forget(['name', 'status']);
|
||||
|
||||
$request->session()->flush();
|
||||
|
||||
### Regenerating the Session ID
|
||||
|
||||
Regenerating the session ID is often done in order to prevent malicious users
|
||||
from exploiting a [session fixation](https://owasp.org/www-
|
||||
community/attacks/Session_fixation) attack on your application.
|
||||
|
||||
Laravel automatically regenerates the session ID during authentication if you
|
||||
are using one of the Laravel [application starter kits](/docs/12.x/starter-
|
||||
kits) or [Laravel Fortify](/docs/12.x/fortify); however, if you need to
|
||||
manually regenerate the session ID, you may use the `regenerate` method:
|
||||
|
||||
|
||||
|
||||
1$request->session()->regenerate();
|
||||
|
||||
|
||||
$request->session()->regenerate();
|
||||
|
||||
If you need to regenerate the session ID and remove all data from the session
|
||||
in a single statement, you may use the `invalidate` method:
|
||||
|
||||
|
||||
|
||||
1$request->session()->invalidate();
|
||||
|
||||
|
||||
$request->session()->invalidate();
|
||||
|
||||
## Session Blocking
|
||||
|
||||
To utilize session blocking, your application must be using a cache driver
|
||||
that supports [atomic locks](/docs/12.x/cache#atomic-locks). Currently, those
|
||||
cache drivers include the `memcached`, `dynamodb`, `redis`, `mongodb`
|
||||
(included in the official `mongodb/laravel-mongodb` package), `database`,
|
||||
`file`, and `array` drivers. In addition, you may not use the `cookie` session
|
||||
driver.
|
||||
|
||||
By default, Laravel allows requests using the same session to execute
|
||||
concurrently. So, for example, if you use a JavaScript HTTP library to make
|
||||
two HTTP requests to your application, they will both execute at the same
|
||||
time. For many applications, this is not a problem; however, session data loss
|
||||
can occur in a small subset of applications that make concurrent requests to
|
||||
two different application endpoints which both write data to the session.
|
||||
|
||||
To mitigate this, Laravel provides functionality that allows you to limit
|
||||
concurrent requests for a given session. To get started, you may simply chain
|
||||
the `block` method onto your route definition. In this example, an incoming
|
||||
request to the `/profile` endpoint would acquire a session lock. While this
|
||||
lock is being held, any incoming requests to the `/profile` or `/order`
|
||||
endpoints which share the same session ID will wait for the first request to
|
||||
finish executing before continuing their execution:
|
||||
|
||||
|
||||
|
||||
1Route::post('/profile', function () {
|
||||
|
||||
2 // ...
|
||||
|
||||
3})->block($lockSeconds = 10, $waitSeconds = 10);
|
||||
|
||||
4
|
||||
|
||||
5Route::post('/order', function () {
|
||||
|
||||
6 // ...
|
||||
|
||||
7})->block($lockSeconds = 10, $waitSeconds = 10);
|
||||
|
||||
|
||||
Route::post('/profile', function () {
|
||||
// ...
|
||||
})->block($lockSeconds = 10, $waitSeconds = 10);
|
||||
|
||||
Route::post('/order', function () {
|
||||
// ...
|
||||
})->block($lockSeconds = 10, $waitSeconds = 10);
|
||||
|
||||
The `block` method accepts two optional arguments. The first argument accepted
|
||||
by the `block` method is the maximum number of seconds the session lock should
|
||||
be held for before it is released. Of course, if the request finishes
|
||||
executing before this time the lock will be released earlier.
|
||||
|
||||
The second argument accepted by the `block` method is the number of seconds a
|
||||
request should wait while attempting to obtain a session lock. An
|
||||
`Illuminate\Contracts\Cache\LockTimeoutException` will be thrown if the
|
||||
request is unable to obtain a session lock within the given number of seconds.
|
||||
|
||||
If neither of these arguments is passed, the lock will be obtained for a
|
||||
maximum of 10 seconds and requests will wait a maximum of 10 seconds while
|
||||
attempting to obtain a lock:
|
||||
|
||||
|
||||
|
||||
1Route::post('/profile', function () {
|
||||
|
||||
2 // ...
|
||||
|
||||
3})->block();
|
||||
|
||||
|
||||
Route::post('/profile', function () {
|
||||
// ...
|
||||
})->block();
|
||||
|
||||
## Adding Custom Session Drivers
|
||||
|
||||
### Implementing the Driver
|
||||
|
||||
If none of the existing session drivers fit your application's needs, Laravel
|
||||
makes it possible to write your own session handler. Your custom session
|
||||
driver should implement PHP's built-in `SessionHandlerInterface`. This
|
||||
interface contains just a few simple methods. A stubbed MongoDB implementation
|
||||
looks like the following:
|
||||
|
||||
|
||||
|
||||
1<?php
|
||||
|
||||
2
|
||||
|
||||
3namespace App\Extensions;
|
||||
|
||||
4
|
||||
|
||||
5class MongoSessionHandler implements \SessionHandlerInterface
|
||||
|
||||
6{
|
||||
|
||||
7 public function open($savePath, $sessionName) {}
|
||||
|
||||
8 public function close() {}
|
||||
|
||||
9 public function read($sessionId) {}
|
||||
|
||||
10 public function write($sessionId, $data) {}
|
||||
|
||||
11 public function destroy($sessionId) {}
|
||||
|
||||
12 public function gc($lifetime) {}
|
||||
|
||||
13}
|
||||
|
||||
|
||||
<?php
|
||||
|
||||
namespace App\Extensions;
|
||||
|
||||
class MongoSessionHandler implements \SessionHandlerInterface
|
||||
{
|
||||
public function open($savePath, $sessionName) {}
|
||||
public function close() {}
|
||||
public function read($sessionId) {}
|
||||
public function write($sessionId, $data) {}
|
||||
public function destroy($sessionId) {}
|
||||
public function gc($lifetime) {}
|
||||
}
|
||||
|
||||
Since Laravel does not include a default directory to house your extensions.
|
||||
You are free to place them anywhere you like. In this example, we have created
|
||||
an `Extensions` directory to house the `MongoSessionHandler`.
|
||||
|
||||
Since the purpose of these methods is not readily understandable, here is an
|
||||
overview of the purpose of each method:
|
||||
|
||||
* The `open` method would typically be used in file based session store systems. Since Laravel ships with a `file` session driver, you will rarely need to put anything in this method. You can simply leave this method empty.
|
||||
* The `close` method, like the `open` method, can also usually be disregarded. For most drivers, it is not needed.
|
||||
* The `read` method should return the string version of the session data associated with the given `$sessionId`. There is no need to do any serialization or other encoding when retrieving or storing session data in your driver, as Laravel will perform the serialization for you.
|
||||
* The `write` method should write the given `$data` string associated with the `$sessionId` to some persistent storage system, such as MongoDB or another storage system of your choice. Again, you should not perform any serialization - Laravel will have already handled that for you.
|
||||
* The `destroy` method should remove the data associated with the `$sessionId` from persistent storage.
|
||||
* The `gc` method should destroy all session data that is older than the given `$lifetime`, which is a UNIX timestamp. For self-expiring systems like Memcached and Redis, this method may be left empty.
|
||||
|
||||
### Registering the Driver
|
||||
|
||||
Once your driver has been implemented, you are ready to register it with
|
||||
Laravel. To add additional drivers to Laravel's session backend, you may use
|
||||
the `extend` method provided by the `Session` [facade](/docs/12.x/facades).
|
||||
You should call the `extend` method from the `boot` method of a [service
|
||||
provider](/docs/12.x/providers). You may do this from the existing
|
||||
`App\Providers\AppServiceProvider` or create an entirely new provider:
|
||||
|
||||
|
||||
|
||||
1<?php
|
||||
|
||||
2
|
||||
|
||||
3namespace App\Providers;
|
||||
|
||||
4
|
||||
|
||||
5use App\Extensions\MongoSessionHandler;
|
||||
|
||||
6use Illuminate\Contracts\Foundation\Application;
|
||||
|
||||
7use Illuminate\Support\Facades\Session;
|
||||
|
||||
8use Illuminate\Support\ServiceProvider;
|
||||
|
||||
9
|
||||
|
||||
10class SessionServiceProvider extends ServiceProvider
|
||||
|
||||
11{
|
||||
|
||||
12 /**
|
||||
|
||||
13 * Register any application services.
|
||||
|
||||
14 */
|
||||
|
||||
15 public function register(): void
|
||||
|
||||
16 {
|
||||
|
||||
17 // ...
|
||||
|
||||
18 }
|
||||
|
||||
19
|
||||
|
||||
20 /**
|
||||
|
||||
21 * Bootstrap any application services.
|
||||
|
||||
22 */
|
||||
|
||||
23 public function boot(): void
|
||||
|
||||
24 {
|
||||
|
||||
25 Session::extend('mongo', function (Application $app) {
|
||||
|
||||
26 // Return an implementation of SessionHandlerInterface...
|
||||
|
||||
27 return new MongoSessionHandler;
|
||||
|
||||
28 });
|
||||
|
||||
29 }
|
||||
|
||||
30}
|
||||
|
||||
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Extensions\MongoSessionHandler;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class SessionServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
Session::extend('mongo', function (Application $app) {
|
||||
// Return an implementation of SessionHandlerInterface...
|
||||
return new MongoSessionHandler;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Once the session driver has been registered, you may specify the `mongo`
|
||||
driver as your application's session driver using the `SESSION_DRIVER`
|
||||
environment variable or within the application's `config/session.php`
|
||||
configuration file.
|
||||
|
||||
Reference in New Issue
Block a user