Skip to content

Commit 9c6d475

Browse files
authored
Merge pull request #71 from alexmerlin/issue-59-v2
Removed `SessionIdentifierAwareInterface`
2 parents ddcbc3b + 8dc2dcc commit 9c6d475

13 files changed

+600
-61
lines changed

docs/book/v2/installation.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# This Is Only a Placeholder
2+
3+
The content of this page can be found under:
4+
5+
https://github.com/laminas/documentation-theme/blob/master/theme/pages/installation.html

docs/book/v2/intro.md

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# mezzio-session
2+
3+
Web applications often need to persist user state between requests, and the
4+
generally accepted way to do so is via _sessions_. While PHP provides its own
5+
session extension, it:
6+
7+
- uses global functions that affect global state.
8+
- relies on a superglobal for access to both read and write the session data.
9+
- incurs either filesystem or network I/O on every request, depending on the
10+
session storage handler.
11+
- can clobber the `Set-Cookie` header when other processes also set it.
12+
13+
Some projects, such as [psr-7-sessions/storageless](https://github.com/psr7-sessions/storageless),
14+
take a different approach, using [JSON Web Tokens](https://tools.ietf.org/html/rfc7519) (JWT).
15+
16+
The goals of mezzio-session are:
17+
18+
- to abstract the way users interact with session storage.
19+
- to abstract how sessions are persisted, to allow both standard ext-session,
20+
but also other paradigms such as JWT.
21+
- to provide session capabilities that "play nice" with
22+
[PSR-7](http://www.php-fig.org/psr/psr-7/) and middleware.
23+
24+
## Installation
25+
26+
Use [Composer](https://getcomposer.org) to install this package:
27+
28+
```bash
29+
$ composer require mezzio/mezzio-session
30+
```
31+
32+
However, the package is not immediately useful unless you have a persistence
33+
adapter. If you are okay with using ext-session, you can install the following
34+
package as well:
35+
36+
```bash
37+
$ composer require mezzio/mezzio-session-ext
38+
```
39+
40+
## Features
41+
42+
mezzio-session provides the following:
43+
44+
- Interfaces for:
45+
- session containers
46+
- session persistence
47+
- An implementation of the session container.
48+
- A "lazy-loading" implementation of the session container, to allow delaying
49+
any de/serialization and/or I/O processes until session data is requested;
50+
this implementation decorates a normal session container.
51+
- PSR-7 middleware that:
52+
- composes a session persistence implementation.
53+
- initializes the lazy-loading session container, using the session
54+
persistence implementation.
55+
- delegates to the next middleware, passing the session container into the
56+
request.
57+
- finalizes the session before returning the response.
58+
59+
Persistence implementations locate session information from the requests (e.g.,
60+
via a cookie) in order to initialize the session. On completion of the request,
61+
they examine the session container for changes and/or to see if it is empty, and
62+
provide data to the response so as to notify the client of the session (e.g.,
63+
via a `Set-Cookie` header).
64+
65+
Note that the goals of this package are solely focused on _session persistence_
66+
and _access to session data by middleware_. If you also need other features
67+
often related to session data, you may want to consider the following packages:
68+
69+
- [mezzio-flash](https://github.com/mezzio/mezzio-flash):
70+
provides flash message capabilities.
71+
- [mezzio-csrf](https://github.com/mezzio/mezzio-csrf):
72+
provides CSRF token generation, storage, and verification, using either a
73+
session container, or flash messages.

docs/book/v2/middleware.md

+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# Session Middleware
2+
3+
mezzio-session provides middleware consuming
4+
[PSR-7](http://www.php-fig.org/psr/psr-7/) HTTP message instances, via
5+
implementation of [PSR-15](https://www.php-fig.org/psr/psr-15/)
6+
interfaces.
7+
8+
This middleware composes a [persistence](persistence.md) instance, and uses that
9+
in order to generate a session container, which it pushes into the request it
10+
delegates to the next middleware. Once a response is returned, it uses the
11+
persistence instance to persist the session data and provide information back to
12+
the client.
13+
14+
The above two paragraphs are longer than the body of the middleware
15+
implementation:
16+
17+
```php
18+
namespace Mezzio\Session;
19+
20+
use Psr\Http\Message\ServerRequestInterface;
21+
use Psr\Http\Message\ResponseInterface;
22+
use Psr\Http\Server\MiddlewareInterface;
23+
use Psr\Http\Server\RequestHandlerInterface;
24+
25+
class SessionMiddleware implements MiddlewareInterface
26+
{
27+
public const SESSION_ATTRIBUTE = 'session';
28+
29+
private $persistence;
30+
31+
public function __construct(SessionPersistenceInterface $persistence)
32+
{
33+
$this->persistence = $persistence;
34+
}
35+
36+
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
37+
{
38+
$session = new LazySession($this->persistence, $request);
39+
$response = $handler->handle(
40+
$request
41+
->withAttribute(self::SESSION_ATTRIBUTE, $session)
42+
->withAttribute(SessionInterface::class, $session)
43+
);
44+
return $this->persistence->persistSession($session, $response);
45+
}
46+
}
47+
```
48+
49+
## Configuration
50+
51+
This package provides a factory for `Mezzio\Session\SessionMiddleware`
52+
via `Mezzio\Session\SessionMiddlewareFactory`; this factory is
53+
auto-wired if you are using Mezzio and the laminas-component-installer Composer
54+
plugin. If not, you will need to wire these into your application.
55+
56+
The factory depends on one service: `Mezzio\Session\SessionPersistenceInterface`.
57+
You will need to either wire in your persistence implementation of choice, or
58+
have the package providing it do so for you.
59+
60+
## Adding the middleware to your application
61+
62+
You may pipe this middleware anywhere in your application. If you want to have
63+
it available anywhere, pipe it early in your application, prior to any routing.
64+
As an example, within Mezzio, you could pipe it in the `config/pipeline.php`
65+
file:
66+
67+
```php
68+
$app->pipe(\Mezzio\Session\SessionMiddleware::class);
69+
$app->pipe(\Mezzio\Router\Middleware\RouteMiddleware::class);
70+
```
71+
72+
This will generally be an inexpensive operation; since the middleware uses a
73+
`LazySession` instance, unless your persistence implementation does any work in
74+
its constructor, the cost is just that of instantiating a few objects.
75+
76+
However, it's often useful to specifically include such middleware directly in
77+
the routed middleware pipelines, to ensure other developers are aware of its
78+
presence in that route's workflow.
79+
80+
Within Mezzio, you can do this when routing, in your `config/routes.php`
81+
file, or within a [delegator factory](https://docs.mezzio.dev/mezzio/cookbook/autowiring-routes-and-pipelines/#delegator-factories):
82+
83+
```php
84+
$app->post('/login', [
85+
\Mezzio\Session\SessionMiddleware::class,
86+
\User\Middleware\LoginHandler::class
87+
]);
88+
```
89+
90+
## Retrieving the session in your own middleware
91+
92+
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`.
93+
94+
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.
95+
96+
Furthermore, a static method exists to optionally retrieve a session when you cannot be sure the middleware has previously been piped: `SessionRetrieval::fromRequestOrNull($request)`
97+
98+
```php
99+
namespace My\NameSpace;
100+
101+
use Mezzio\Session\Exception\SessionNotInitializedException;
102+
use Mezzio\Session\RetrieveSession;
103+
use Psr\Http\Message\ResponseInterface;
104+
use Psr\Http\Message\ServerRequestInterface;
105+
use Psr\Http\Server\RequestHandlerInterface;
106+
107+
class MyRequestHandler implements RequestHandlerInterface {
108+
109+
// ...
110+
111+
public function handle(ServerRequestInterface $request) : ResponseInterface
112+
{
113+
try {
114+
$session = RetrieveSession::fromRequest($request);
115+
} catch (SessionNotInitializedException $error) {
116+
// Handle the uninitialized session:
117+
return $this->redirectToLogin();
118+
}
119+
120+
$value = $session->get('SomeKey');
121+
$this->templateRenderer->render('some:template', ['value' => $value]);
122+
}
123+
}
124+
125+
class AnotherRequestHandler implements RequestHandlerInterface {
126+
127+
// ...
128+
129+
public function handle(ServerRequestInterface $request) : ResponseInterface
130+
{
131+
$session = RetrieveSession::fromRequestOrNull($request);
132+
if (! $session) {
133+
// Handle the uninitialized session:
134+
return $this->redirectToLogin();
135+
}
136+
137+
$value = $session->get('SomeKey');
138+
$this->templateRenderer->render('some:template', ['value' => $value]);
139+
}
140+
}
141+
142+
```

docs/book/v2/persistence.md

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Session Persistence
2+
3+
Session persistence within mezzio-session refers to one or both of the
4+
following:
5+
6+
- Identifying session information provided by the client making the request.
7+
- Storing session data for access on subsequent requests.
8+
- Providing session information to the client making the request.
9+
10+
In some scenarios, such as usage of JSON Web Tokens (JWT), the serialized
11+
session data is provided _by_ the client, and provided _to_ the client directly,
12+
without any server-side storage whatsoever.
13+
14+
To describe these operations, we provide `Mezzio\Session\SessionPersistenceInterface`:
15+
16+
```php
17+
namespace Mezzio\Session;
18+
19+
use Psr\Http\Message\ResponseInterface;
20+
use Psr\Http\Message\ServerRequestInterface;
21+
22+
interface SessionPersistenceInterface
23+
{
24+
/**
25+
* Generate a session data instance based on the request.
26+
*/
27+
public function initializeSessionFromRequest(ServerRequestInterface $request) : SessionInterface;
28+
29+
/**
30+
* Persist the session data instance.
31+
*
32+
* Persists the session data, returning a response instance with any
33+
* artifacts required to return to the client.
34+
*/
35+
public function persistSession(SessionInterface $session, ResponseInterface $response) : ResponseInterface;
36+
}
37+
```
38+
39+
Session initialization pulls data from the request (a cookie, a header value,
40+
etc.) in order to produce a session container. Session persistence pulls data
41+
from the session container, does something with it, and then optionally provides
42+
a response containing session artifacts (a cookie, a header value, etc.).
43+
44+
For sessions to work, _you must provide a persistence implementation_. We
45+
provide one such implementation using PHP's session extension via the package
46+
[mezzio-session-ext](https://github.com/mezzio/mezzio-session-ext).
47+
48+
## Session identifiers
49+
50+
Typically, the session identifier will be retrieved from the request (usually
51+
via a cookie), and a new identifier created if none was discovered.
52+
53+
During persistence, if an existing session's contents have changed, or
54+
`regenerateId()` was called on the session, the persistence implementation
55+
becomes responsible for:
56+
57+
- Removing the original session.
58+
- Generating a new identifier for the session.
59+
60+
In all situations, it then needs to store the session data in such a way that a
61+
later lookup by the current identifier will retrieve that data.
62+
63+
Prior to version 1.1.0, persistence engines had two ways to determine what the
64+
original session identifier was when it came time to regenerate or persist a
65+
session:
66+
67+
- Store the identifier as a property of the persistence implementation.
68+
- Store the identifier in the session data under a "magic" key (e.g.,
69+
`__SESSION_ID__`).
70+
71+
The first approach is problematic when using mezzio-session in an async
72+
environment such as [Swoole](https://swoole.co.uk) or
73+
[ReactPHP](https://reactphp.org), as the same persistence instance may be used
74+
by several simultaneous requests. `Mezzio\Session\SessionInterface` defines a new
75+
`getId` method, implementations can thus store the
76+
identifier internally, and, when it comes time to store the session data,
77+
persistence implementations can query that method in order to retrieve the
78+
session identifier.
79+
80+
## Persistent sessions
81+
82+
- Since 1.2.0.
83+
84+
If your persistence implementation supports persistent sessions — for
85+
example, by setting an `Expires` or `Max-Age` cookie directive — then you
86+
can opt to globally set a default session duration, or allow developers to hint
87+
a desired session duration via the session container using
88+
`SessionContainerPersistenceInterface::persistSessionFor()`.
89+
90+
Implementations SHOULD honor the value of `SessionContainerPersistenceInterface::getSessionLifetime()`
91+
when persisting the session data. This could mean either or both of the
92+
following:
93+
94+
- Ensuring that the session data will not be purged until after the specified
95+
TTL value.
96+
- Setting an `Expires` or `Max-Age` cookie directive.
97+
98+
In each case, the persistence engine should query the `Session` instance for a
99+
TTL value:
100+
101+
```php
102+
$ttl = $session instanceof SessionContainerPersistenceInterface
103+
? $session->getSessionLifetime()
104+
: $defaultLifetime; // likely 0, to indicate automatic expiry
105+
```
106+
107+
`getSessionLifetime()` returns an `integer` value indicating the number of
108+
seconds the session should persist.

0 commit comments

Comments
 (0)