Salta ai contenuti

ADR-005 - Modello Middleware PSR-15

Adottare gestori di richieste HTTP PSR-15 (middleware) per una pipeline di elaborazione delle richieste migliorata.


Proposto - In fase di valutazione per la release XOOPS 4.0


XOOPS 2.5 utilizza un approccio monolitico di gestione delle richieste:

// Attuale: Elaborazione sequenziale
require_once 'mainfile.php';
// → Inizializzazione del kernel
// → Autenticazione utente
// → Caricamento moduli
// → Rendering pagina
// Tutto in un flusso, preoccupazioni miste
  1. Preoccupazioni Miste - Autenticazione, logging, routing tutti intrecciati
  2. Difficile da Testare - Difficile testare in unità i singoli step di elaborazione delle richieste
  3. Difficile da Estendere - I moduli possono solo agganciarsi via preload/events
  4. Scarsa Separazione - Logica di elaborazione delle richieste dispersa nel codebase
  5. Non Componibile - Non è facile concatenare o riordinare i step di elaborazione

PSR-15 definisce un’interfaccia standard per il middleware HTTP:

<?php
interface RequestHandlerInterface {
public function handle(ServerRequestInterface $request): ResponseInterface;
}
interface MiddlewareInterface {
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface;
}

Catena di Middleware:

Richiesta
[Logger] → registra richiesta
[Auth] → convalida sessione utente
[CORS] → controlla cross-origin
[Router] → invia a handler
[Handler] → genera risposta
Risposta

Implementare una pipeline di elaborazione delle richieste basata su middleware seguendo lo standard PSR-15.

graph TD
subgraph "Request Processing Pipeline"
A["HTTP Request<br/>(PSR-7 ServerRequest)"]
B["Middleware Stack<br/>(PSR-15)"]
C["Logger Middleware"]
D["Session Middleware"]
E["Auth Middleware"]
F["CORS Middleware"]
G["Router Middleware"]
H["Handler<br/>(Controller/Action)"]
I["Response<br/>(PSR-7 Response)"]
end
A --> B
B --> C
C --> D
D --> E
E --> F
F --> G
G --> H
H --> I

1. Middleware dell’Applicazione (Livello Centrale)

Sezione intitolata “1. Middleware dell’Applicazione (Livello Centrale)”
<?php
declare(strict_types=1);
namespace XoopsCore;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class SessionMiddleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
// 1. Recupera sessione (o avvia nuova)
$sessionId = $request->getCookieParams()['PHPSESSID'] ?? null;
$session = $this->sessionManager->load($sessionId);
// 2. Allega sessione alla richiesta
$request = $request->withAttribute('session', $session);
// 3. Passa al prossimo middleware
$response = $handler->handle($request);
// 4. Imposta cookie di sessione se necessario
if ($session->isModified()) {
$response = $response->withAddedHeader(
'Set-Cookie',
'PHPSESSID=' . $session->getId() . '; HttpOnly; SameSite=Strict'
);
}
return $response;
}
}
<?php
class AuthMiddleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
// Ottiene sessione dal middleware precedente
$session = $request->getAttribute('session');
// Autentica utente dalla sessione
$user = $this->authenticate($session);
// Allega utente alla richiesta
$request = $request->withAttribute('user', $user);
return $handler->handle($request);
}
private function authenticate(?Session $session): User
{
if ($session && $session->has('uid')) {
return $this->userRepository->findById($session->get('uid'));
}
return new AnonymousUser();
}
}
<?php
class AuthorizationMiddleware implements MiddlewareInterface
{
public function __construct(private AuthorizationChecker $checker)
{
}
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
$user = $request->getAttribute('user');
$route = $request->getAttribute('route');
// Controlla se l'utente ha autorizzazione per questa rotta
if (!$this->checker->isGranted($user, $route)) {
return new JsonResponse(
['error' => 'Unauthorized'],
403
);
}
return $handler->handle($request);
}
}
<?php
// I moduli possono fornire il loro proprio middleware
class PublisherAccessMiddleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
$user = $request->getAttribute('user');
// Controllo accesso specifico del modulo
if (!$user->hasPermission('publisher_view')) {
return new HtmlResponse('Access denied', 403);
}
return $handler->handle($request);
}
}
<?php
// bootstrap.php - Configurazione applicazione
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Xoops\Core\Middleware\{
LoggerMiddleware,
SessionMiddleware,
AuthMiddleware,
CorsMiddleware,
ErrorHandlingMiddleware
};
// Crea pipeline middleware
$middlewareStack = [
// 1. Gestione errori (più esterna)
new ErrorHandlingMiddleware(),
// 2. Logging
new LoggerMiddleware($logger),
// 3. Gestione CORS
new CorsMiddleware($corsConfig),
// 4. Gestione sessione
new SessionMiddleware($sessionManager),
// 5. Autenticazione
new AuthMiddleware($userRepository),
// 6. Autorizzazione
new AuthorizationMiddleware($authChecker),
// 7. Routing e dispatching
new RoutingMiddleware($router),
// 8. Middleware modulo (dinamico)
...$this->loadModuleMiddleware(),
];
// Elabora richiesta attraverso stack middleware
$request = ServerRequestFactory::fromGlobals();
$dispatcher = new MiddlewareDispatcher($middlewareStack);
$response = $dispatcher->dispatch($request);
// Invia risposta
http_response_code($response->getStatusCode());
foreach ($response->getHeaders() as $name => $values) {
foreach ($values as $value) {
header("$name: $value", false);
}
}
echo $response->getBody();

I moduli possono fornire middleware:

<?php
// Modulo Publisher - xoops_version.php
$modversion['middleware'] = [
'PublisherAccessMiddleware' => true, // Caricamento automatico
'PublisherLogMiddleware' => true,
];
// O personalizzato:
$modversion['middleware_factory'] = function() {
return [
new PublisherCacheMiddleware(),
new PublisherPermissionMiddleware(),
];
};

  1. Separazione delle Preoccupazioni - Ogni middleware gestisce una responsabilità
  2. Testabilità - Facile testare in unità singoli componenti middleware
  3. Componibilità - Il middleware può essere miscelato e riordinato
  4. Conforme ai Standard - Utilizza standard PSR-15 e PSR-7
  5. Estensibilità - I moduli possono facilmente aggiungere middleware personalizzato
  6. Debug - Flusso di richiesta chiaro attraverso la pipeline
  7. Performance - Possibilità di ottimizzare specifici livelli middleware
  8. Interoperabilità - Può usare middleware PSR-15 di terze parti
  1. Curva di Apprendimento - Gli sviluppatori devono comprendere PSR-15
  2. Overhead Performance - Più chiamate di funzione nella pipeline
  3. Complessità - Più parti mobili rispetto all’approccio monolitico
  4. Sforzo di Migrazione - Richiede refactoring del codice esistente
  5. Dipendenze - Richiede libreria HTTP PSR-7
RischioGravitàMitigazione
Catene middleware complesseMediaDocumentazione chiara, esempi
Degradazione performanceMediaBenchmark, ottimizzare hot path
Utilizzo scorretto dagli sviluppatoriMediaCode review, guida best practices
Cambios critici alla migrazioneAltaPeriodo deprecazione, helper
Problemi ordine middlewareMediaGrafo dipendenze chiaro

  • Implementare wrapper messaggio HTTP PSR-7
  • Creare MiddlewareDispatcher
  • Implementare middleware centrale (sessione, auth)
  • Aggiornare kernel per usare middleware
  • Migrare funzionalità esistenti a middleware
  • Aggiungere supporto middleware modulo
  • Creare utility di test middleware
  • Scrivere documentazione completa
  • Fornire livello compatibilità per codice precedente
  • Aiutare moduli ad aggiornare a nuovo middleware
  • Ottimizzazione performance
  • Audit di sicurezza
  • Release XOOPS 4.0 con middleware
  • Deprecare sistema preload/hook precedente
  • Feedback comunità e aggiornamenti

  • Tutta la funzionalità centrale migrata a middleware
  • Copertura test 90%+ per middleware
  • Documentazione completa con esempi
  • Performance entro 10% della versione precedente
  • Moduli utilizzano con successo nuovo sistema middleware
  • Tasso adozione comunità >80%

  • Mantenere middleware focalizzato (responsabilità singola)
  • Usare immutabilità (creare nuova richiesta/risposta)
  • Gestire errori con grazia
  • Documentare dipendenze
  • Aggiungere type hints
  • Scrivere test per middleware
  • Usare interfacce standard PSR-15
  • Non modificare oggetti richiesta/risposta condivisi
  • Non accedere direttamente a globali
  • Non creare dipendenze sull’ordine middleware
  • Non catturare tutte le eccezioni
  • Non mescolare logica affari con middleware
  • Non far fare troppo al middleware

<?php
// Esempio: middleware rate limiting
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class RateLimitMiddleware implements MiddlewareInterface
{
public function __construct(
private RateLimiter $limiter,
private int $limit = 100,
private int $window = 3600
) {
}
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
$user = $request->getAttribute('user');
$identifier = $user->getId() ?? $request->getClientIp();
// Controlla rate limit
$remaining = $this->limiter->check($identifier, $this->limit, $this->window);
if ($remaining < 0) {
return new JsonResponse(
['error' => 'Rate limit exceeded'],
429
);
}
// Aggiungi header rate limit
$response = $handler->handle($request);
return $response
->withAddedHeader('X-RateLimit-Limit', (string)$this->limit)
->withAddedHeader('X-RateLimit-Remaining', (string)$remaining);
}
}

  • ADR-001: Architettura Modulare - Fondazione
  • ADR-004: Sistema di Sicurezza - Utilizza middleware per auth
  • ADR-006: Autenticazione Doppio Fattore - Può essere middleware


VersioneDataCambiamenti
1.0.02024-01-28Proposta iniziale

#xoops #adr #psr-15 #middleware #architecture #psr-7