Slim 4 - Session
source link: https://odan.github.io/2021/01/15/slim4-session.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Daniel Opitz - Blog
Developer, Trainer, Open Source Contributor
Blog About me DonateSlim 4 - Session
15 Jan 2021
Table of contents
Requirements
- PHP 7.3+ or PHP 8.0+
- A Slim 4 application
Introduction
Since HTTP driven applications are stateless, sessions provide a way to store information about the user across multiple requests. The PHP ecosystem provides a variety of session components to handle sessions.
This time I want to show you how to install the odan/session
component.
Unlike many other PHP session components, this one is optimized for PSR-7 HTTP requests
and PSR-15 middleware handlers. A unique feature is the “lazy session start” support
to give the middleware complete control over the session start time.
Installation
composer require odan/session
Configuration
Insert the session settings into your configuration file, e.g. config/settings.php
;
// Session
$settings['session'] = [
'name' => 'webapp',
'cache_expire' => 0,
];
You can use all the standard PHP session configuration options.
Read more: Session Runtime Configuration
Container Setup
Add a DI container definition for SessionInterface:class
and SessionMiddleware:class
in config/container.php
.
Make sure you also have a definition for ResponseFactoryInterface::class
and App::class
.
<?php
use Odan\Session\PhpSession;
use Odan\Session\SessionInterface;
use Odan\Session\Middleware\SessionMiddleware;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Slim\App;
return [
// ...
SessionInterface::class => function (ContainerInterface $container) {
$settings = $container->get('settings');
$session = new PhpSession();
$session->setOptions((array)$settings['session']);
return $session;
},
SessionMiddleware::class => function (ContainerInterface $container) {
return new SessionMiddleware($container->get(SessionInterface::class));
},
// ...
App::class => function (ContainerInterface $container) {
AppFactory::setContainer($container);
return AppFactory::create();
},
ResponseFactoryInterface::class => function (ContainerInterface $container) {
return $container->get(App::class)->getResponseFactory();
},
];
Middleware
Now add the SessionMiddleware
, before the RoutingMiddleware
, into your Slim middleware stack.
This example registers the session middleware for all routes. It’s also possible to register middleware for a single rout and routing groups.
<?php
use Odan\Session\Middleware\SessionMiddleware;
use Slim\App;
return function (App $app) {
// ...
// Start the session
$app->add(SessionMiddleware::class); // <-- here
$app->addRoutingMiddleware();
// ...
};
Usage
To access the SessionInterface
implementation, we must first declare it in the constructor so that it can
be automatically injected by the DI Container.
I want to show a simple login/logout mechanism to demonstrate the session and flash message handling.
Create a new file src/Action/LoginSubmitAction.php
and copy/paste this content:
<?php
namespace App\Action;
use Odan\Session\SessionInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Routing\RouteContext;
final class LoginSubmitAction
{
/**
* @var SessionInterface
*/
private $session;
public function __construct(SessionInterface $session)
{
$this->session = $session;
}
public function __invoke(
ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
$data = (array)$request->getParsedBody();
$username = (string)($data['username'] ?? '');
$password = (string)($data['password'] ?? '');
// Pseudo example
// Check user credentials. You may use an application/domain service and the database here.
$user = null;
if($username === 'admin' && $password === 'secret') {
$user = 1;
}
// Clear all flash messages
$flash = $this->session->getFlash();
$flash->clear();
// Get RouteParser from request to generate the urls
$routeParser = RouteContext::fromRequest($request)->getRouteParser();
if ($user) {
// Login successfully
// Clears all session data and regenerate session ID
$this->session->destroy();
$this->session->start();
$this->session->regenerateId();
$this->session->set('user', $user);
$flash->add('success', 'Login successfully');
// Redirect to protected page
$url = $routeParser->urlFor('users-get');
} else {
$flash->add('error', 'Login failed!');
// Redirect back to the login page
$url = $routeParser->urlFor('login');
}
$response->withStatus(302)->withHeader('Location', $url);
}
}
Create a new file src/Action/LogoutAction.php
and copy/paste this content:
<?php
namespace App\Action;
use App\Responder\Responder;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Routing\RouteContext;
use Odan\Session\SessionInterface;
final class LogoutAction
{
/**
* @var SessionInterface
*/
private $session;
public function __construct(SessionInterface $session)
{
$this->session = $session;
}
public function __invoke(
ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
// Logout user
$this->session->destroy();
$routeParser = RouteContext::fromRequest($request)->getRouteParser();
$url = $routeParser->urlFor('logout');
return $response->withStatus(302)->withHeader('Location', $url);
}
}
To check the user session for each request you can add a middleware that redirects all “invalid” requests to the login page.
<?php
namespace App\Middleware;
use Odan\Session\SessionInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Routing\RouteContext;
final class UserAuthMiddleware implements MiddlewareInterface
{
/**
* @var ResponseFactoryInterface
*/
private $responseFactory;
/**
* @var SessionInterface
*/
private $session;
public function __construct(
ResponseFactoryInterface $responseFactory,
SessionInterface $session
) {
$this->responseFactory = $responseFactory;
$this->session = $session;
}
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface{
if ($this->session->get('user')) {
// User is logged in
return $handler->handle($request);
}
// User is not logged in. Redirect to login page.
$routeParser = RouteContext::fromRequest($request)->getRouteParser();
$url = $routeParser->urlFor('login');
return $this->responseFactory->createResponse()
->withStatus(302)
->withHeader('Location', $url);
}
}
You can add the UserAuthMiddleware::class
to individual routes and/or route groups you want to protect.
use App\Middleware\UserAuthMiddleware;
use Slim\Routing\RouteCollectorProxy;
// ...
// Password protected area
$app->group('/users', function (RouteCollectorProxy $group) {
// ...
})->add(UserAuthMiddleware::class);
Add the routes as follows:
$app->get('/login', \App\Action\LoginAction::class)->setName('login');
$app->post('/login', \App\Action\LoginSubmitAction::class);
$app->get('/logout', \App\Action\LogoutAction::class)->setName('logout');
Conclusion
As you can see, with a middleware-based session handler, it’s much easier to control the start time of the session and add cool middleware features as shown above.
Read more
© 2021 Daniel Opitz | Twitter
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK