-
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #71 from alexmerlin/issue-59-v2
Removed `SessionIdentifierAwareInterface`
- Loading branch information
Showing
13 changed files
with
600 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# This Is Only a Placeholder | ||
|
||
The content of this page can be found under: | ||
|
||
https://github.com/laminas/documentation-theme/blob/master/theme/pages/installation.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# mezzio-session | ||
|
||
Web applications often need to persist user state between requests, and the | ||
generally accepted way to do so is via _sessions_. While PHP provides its own | ||
session extension, it: | ||
|
||
- uses global functions that affect global state. | ||
- relies on a superglobal for access to both read and write the session data. | ||
- incurs either filesystem or network I/O on every request, depending on the | ||
session storage handler. | ||
- can clobber the `Set-Cookie` header when other processes also set it. | ||
|
||
Some projects, such as [psr-7-sessions/storageless](https://github.com/psr7-sessions/storageless), | ||
take a different approach, using [JSON Web Tokens](https://tools.ietf.org/html/rfc7519) (JWT). | ||
|
||
The goals of mezzio-session are: | ||
|
||
- to abstract the way users interact with session storage. | ||
- to abstract how sessions are persisted, to allow both standard ext-session, | ||
but also other paradigms such as JWT. | ||
- to provide session capabilities that "play nice" with | ||
[PSR-7](http://www.php-fig.org/psr/psr-7/) and middleware. | ||
|
||
## Installation | ||
|
||
Use [Composer](https://getcomposer.org) to install this package: | ||
|
||
```bash | ||
$ composer require mezzio/mezzio-session | ||
``` | ||
|
||
However, the package is not immediately useful unless you have a persistence | ||
adapter. If you are okay with using ext-session, you can install the following | ||
package as well: | ||
|
||
```bash | ||
$ composer require mezzio/mezzio-session-ext | ||
``` | ||
|
||
## Features | ||
|
||
mezzio-session provides the following: | ||
|
||
- Interfaces for: | ||
- session containers | ||
- session persistence | ||
- An implementation of the session container. | ||
- A "lazy-loading" implementation of the session container, to allow delaying | ||
any de/serialization and/or I/O processes until session data is requested; | ||
this implementation decorates a normal session container. | ||
- PSR-7 middleware that: | ||
- composes a session persistence implementation. | ||
- initializes the lazy-loading session container, using the session | ||
persistence implementation. | ||
- delegates to the next middleware, passing the session container into the | ||
request. | ||
- finalizes the session before returning the response. | ||
|
||
Persistence implementations locate session information from the requests (e.g., | ||
via a cookie) in order to initialize the session. On completion of the request, | ||
they examine the session container for changes and/or to see if it is empty, and | ||
provide data to the response so as to notify the client of the session (e.g., | ||
via a `Set-Cookie` header). | ||
|
||
Note that the goals of this package are solely focused on _session persistence_ | ||
and _access to session data by middleware_. If you also need other features | ||
often related to session data, you may want to consider the following packages: | ||
|
||
- [mezzio-flash](https://github.com/mezzio/mezzio-flash): | ||
provides flash message capabilities. | ||
- [mezzio-csrf](https://github.com/mezzio/mezzio-csrf): | ||
provides CSRF token generation, storage, and verification, using either a | ||
session container, or flash messages. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
# Session Middleware | ||
|
||
mezzio-session provides middleware consuming | ||
[PSR-7](http://www.php-fig.org/psr/psr-7/) HTTP message instances, via | ||
implementation of [PSR-15](https://www.php-fig.org/psr/psr-15/) | ||
interfaces. | ||
|
||
This middleware composes a [persistence](persistence.md) instance, and uses that | ||
in order to generate a session container, which it pushes into the request it | ||
delegates to the next middleware. Once a response is returned, it uses the | ||
persistence instance to persist the session data and provide information back to | ||
the client. | ||
|
||
The above two paragraphs are longer than the body of the middleware | ||
implementation: | ||
|
||
```php | ||
namespace Mezzio\Session; | ||
|
||
use Psr\Http\Message\ServerRequestInterface; | ||
use Psr\Http\Message\ResponseInterface; | ||
use Psr\Http\Server\MiddlewareInterface; | ||
use Psr\Http\Server\RequestHandlerInterface; | ||
|
||
class SessionMiddleware implements MiddlewareInterface | ||
{ | ||
public const SESSION_ATTRIBUTE = 'session'; | ||
|
||
private $persistence; | ||
|
||
public function __construct(SessionPersistenceInterface $persistence) | ||
{ | ||
$this->persistence = $persistence; | ||
} | ||
|
||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface | ||
{ | ||
$session = new LazySession($this->persistence, $request); | ||
$response = $handler->handle( | ||
$request | ||
->withAttribute(self::SESSION_ATTRIBUTE, $session) | ||
->withAttribute(SessionInterface::class, $session) | ||
); | ||
return $this->persistence->persistSession($session, $response); | ||
} | ||
} | ||
``` | ||
|
||
## Configuration | ||
|
||
This package provides a factory for `Mezzio\Session\SessionMiddleware` | ||
via `Mezzio\Session\SessionMiddlewareFactory`; this factory is | ||
auto-wired if you are using Mezzio and the laminas-component-installer Composer | ||
plugin. If not, you will need to wire these into your application. | ||
|
||
The factory depends on one service: `Mezzio\Session\SessionPersistenceInterface`. | ||
You will need to either wire in your persistence implementation of choice, or | ||
have the package providing it do so for you. | ||
|
||
## Adding the middleware to your application | ||
|
||
You may pipe this middleware anywhere in your application. If you want to have | ||
it available anywhere, pipe it early in your application, prior to any routing. | ||
As an example, within Mezzio, you could pipe it in the `config/pipeline.php` | ||
file: | ||
|
||
```php | ||
$app->pipe(\Mezzio\Session\SessionMiddleware::class); | ||
$app->pipe(\Mezzio\Router\Middleware\RouteMiddleware::class); | ||
``` | ||
|
||
This will generally be an inexpensive operation; since the middleware uses a | ||
`LazySession` instance, unless your persistence implementation does any work in | ||
its constructor, the cost is just that of instantiating a few objects. | ||
|
||
However, it's often useful to specifically include such middleware directly in | ||
the routed middleware pipelines, to ensure other developers are aware of its | ||
presence in that route's workflow. | ||
|
||
Within Mezzio, you can do this when routing, in your `config/routes.php` | ||
file, or within a [delegator factory](https://docs.mezzio.dev/mezzio/cookbook/autowiring-routes-and-pipelines/#delegator-factories): | ||
|
||
```php | ||
$app->post('/login', [ | ||
\Mezzio\Session\SessionMiddleware::class, | ||
\User\Middleware\LoginHandler::class | ||
]); | ||
``` | ||
|
||
## Retrieving the session in your own middleware | ||
|
||
Whilst it is trivial to retrieve the initialised session from the request with `$session = $request->getAttribute(SessionInterface::class);`, static analysers cannot automatically infer the value assigned to `$session`. | ||
|
||
To provide a convenient and type safe way to retrieve the session from the current request without manually asserting its type, `SessionRetrieval::fromRequest($request)` can be called so that you can use the request without further assertions. | ||
|
||
Furthermore, a static method exists to optionally retrieve a session when you cannot be sure the middleware has previously been piped: `SessionRetrieval::fromRequestOrNull($request)` | ||
|
||
```php | ||
namespace My\NameSpace; | ||
|
||
use Mezzio\Session\Exception\SessionNotInitializedException; | ||
use Mezzio\Session\RetrieveSession; | ||
use Psr\Http\Message\ResponseInterface; | ||
use Psr\Http\Message\ServerRequestInterface; | ||
use Psr\Http\Server\RequestHandlerInterface; | ||
|
||
class MyRequestHandler implements RequestHandlerInterface { | ||
|
||
// ... | ||
|
||
public function handle(ServerRequestInterface $request) : ResponseInterface | ||
{ | ||
try { | ||
$session = RetrieveSession::fromRequest($request); | ||
} catch (SessionNotInitializedException $error) { | ||
// Handle the uninitialized session: | ||
return $this->redirectToLogin(); | ||
} | ||
|
||
$value = $session->get('SomeKey'); | ||
$this->templateRenderer->render('some:template', ['value' => $value]); | ||
} | ||
} | ||
|
||
class AnotherRequestHandler implements RequestHandlerInterface { | ||
|
||
// ... | ||
|
||
public function handle(ServerRequestInterface $request) : ResponseInterface | ||
{ | ||
$session = RetrieveSession::fromRequestOrNull($request); | ||
if (! $session) { | ||
// Handle the uninitialized session: | ||
return $this->redirectToLogin(); | ||
} | ||
|
||
$value = $session->get('SomeKey'); | ||
$this->templateRenderer->render('some:template', ['value' => $value]); | ||
} | ||
} | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
# Session Persistence | ||
|
||
Session persistence within mezzio-session refers to one or both of the | ||
following: | ||
|
||
- Identifying session information provided by the client making the request. | ||
- Storing session data for access on subsequent requests. | ||
- Providing session information to the client making the request. | ||
|
||
In some scenarios, such as usage of JSON Web Tokens (JWT), the serialized | ||
session data is provided _by_ the client, and provided _to_ the client directly, | ||
without any server-side storage whatsoever. | ||
|
||
To describe these operations, we provide `Mezzio\Session\SessionPersistenceInterface`: | ||
|
||
```php | ||
namespace Mezzio\Session; | ||
|
||
use Psr\Http\Message\ResponseInterface; | ||
use Psr\Http\Message\ServerRequestInterface; | ||
|
||
interface SessionPersistenceInterface | ||
{ | ||
/** | ||
* Generate a session data instance based on the request. | ||
*/ | ||
public function initializeSessionFromRequest(ServerRequestInterface $request) : SessionInterface; | ||
|
||
/** | ||
* Persist the session data instance. | ||
* | ||
* Persists the session data, returning a response instance with any | ||
* artifacts required to return to the client. | ||
*/ | ||
public function persistSession(SessionInterface $session, ResponseInterface $response) : ResponseInterface; | ||
} | ||
``` | ||
|
||
Session initialization pulls data from the request (a cookie, a header value, | ||
etc.) in order to produce a session container. Session persistence pulls data | ||
from the session container, does something with it, and then optionally provides | ||
a response containing session artifacts (a cookie, a header value, etc.). | ||
|
||
For sessions to work, _you must provide a persistence implementation_. We | ||
provide one such implementation using PHP's session extension via the package | ||
[mezzio-session-ext](https://github.com/mezzio/mezzio-session-ext). | ||
|
||
## Session identifiers | ||
|
||
Typically, the session identifier will be retrieved from the request (usually | ||
via a cookie), and a new identifier created if none was discovered. | ||
|
||
During persistence, if an existing session's contents have changed, or | ||
`regenerateId()` was called on the session, the persistence implementation | ||
becomes responsible for: | ||
|
||
- Removing the original session. | ||
- Generating a new identifier for the session. | ||
|
||
In all situations, it then needs to store the session data in such a way that a | ||
later lookup by the current identifier will retrieve that data. | ||
|
||
Prior to version 1.1.0, persistence engines had two ways to determine what the | ||
original session identifier was when it came time to regenerate or persist a | ||
session: | ||
|
||
- Store the identifier as a property of the persistence implementation. | ||
- Store the identifier in the session data under a "magic" key (e.g., | ||
`__SESSION_ID__`). | ||
|
||
The first approach is problematic when using mezzio-session in an async | ||
environment such as [Swoole](https://swoole.co.uk) or | ||
[ReactPHP](https://reactphp.org), as the same persistence instance may be used | ||
by several simultaneous requests. `Mezzio\Session\SessionInterface` defines a new | ||
`getId` method, implementations can thus store the | ||
identifier internally, and, when it comes time to store the session data, | ||
persistence implementations can query that method in order to retrieve the | ||
session identifier. | ||
|
||
## Persistent sessions | ||
|
||
- Since 1.2.0. | ||
|
||
If your persistence implementation supports persistent sessions — for | ||
example, by setting an `Expires` or `Max-Age` cookie directive — then you | ||
can opt to globally set a default session duration, or allow developers to hint | ||
a desired session duration via the session container using | ||
`SessionContainerPersistenceInterface::persistSessionFor()`. | ||
|
||
Implementations SHOULD honor the value of `SessionContainerPersistenceInterface::getSessionLifetime()` | ||
when persisting the session data. This could mean either or both of the | ||
following: | ||
|
||
- Ensuring that the session data will not be purged until after the specified | ||
TTL value. | ||
- Setting an `Expires` or `Max-Age` cookie directive. | ||
|
||
In each case, the persistence engine should query the `Session` instance for a | ||
TTL value: | ||
|
||
```php | ||
$ttl = $session instanceof SessionContainerPersistenceInterface | ||
? $session->getSessionLifetime() | ||
: $defaultLifetime; // likely 0, to indicate automatic expiry | ||
``` | ||
|
||
`getSessionLifetime()` returns an `integer` value indicating the number of | ||
seconds the session should persist. |
Oops, something went wrong.