> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Driver di sessione personalizzati nei pacchetti

> Analizzare il SessionManager di Laravel 13 per comprendere il contratto di salvataggio di SessionHandlerInterface, la selezione esplicita del driver, TTL e serializzazione, gli Store conservati e i test.

Per fornire da un pacchetto una destinazione di salvataggio delle sessioni personalizzata, separa **la registrazione del salvataggio** dalla **selezione da parte dell'applicazione**. La semplice installazione non deve cambiare dove viene salvato lo stato di login: progetta il pacchetto in modo che l'utente possa attivarlo dopo aver valutato l'impatto sulle sessioni esistenti.

Questa pagina esamina i sorgenti ufficiali di Laravel Framework `v13.39.0`. Per le operazioni di base consulta [Sessioni](https://kawax.biz/it/session.md), per il caricamento automatico dei provider consulta [Package auto-discovery](https://kawax.biz/it/advanced/package-discovery.md).

## extend riceve un handler, non uno Store

Il callback di `Session::extend()` restituisce un **handler di salvataggio** che implementa la `SessionHandlerInterface` di PHP. Non è un'API che restituisce direttamente uno Store che implementa `Illuminate\Contracts\Session\Session` o un Repository di cache.

`SessionManager::callCustomCreator()` passa il valore restituito dal callback a `buildSession()`, che lo avvolge in uno `Store` o in un `EncryptedStore` a seconda di `session.encrypt`. In quel momento applica anche il nome del cookie e `session.serialization`.

```mermaid theme={null}
flowchart TD
    A["Registrare il nome del driver in boot()"] --> B["L'utente sceglie session.driver"]
    B --> C["Il primo driver() esegue il callback di creazione"]
    C --> D["Restituire una SessionHandlerInterface"]
    D --> E["SessionManager la avvolge in<br>uno Store o EncryptedStore"]
    E --> F["StartSession avvia la lettura"]
    F --> G["L'applicazione opera con put() e simili"]
    G --> H["Lo Store serializza e<br>passa a handler.write()"]
```

Nella normale elaborazione HTTP, il middleware `StartSession` gestisce l'avvio e il salvataggio della sessione. Per fornire un handler, il pacchetto non deve chiamare `session_start()` né `session_set_save_handler()`.

## Distinguere le parti del contratto di salvataggio

Per implementare un backend personalizzato, soddisfa i seguenti ruoli.

| Metodo | Responsabilità dell'handler |
| - | - |
| `open($savePath, $sessionName)` | Inizializzazione necessaria. Restituire `true` se non serve |
| `close()` | Chiusura necessaria. Restituire `true` se non serve |
| `read($sessionId)` | Restituire la stringa salvata. Restituire una stringa vuota se non salvata o scaduta |
| `write($sessionId, $data)` | Salvare la stringa ricevuta e restituire successo o fallimento |
| `destroy($sessionId)` | Eliminare i dati dell'ID indicato |
| `gc($lifetime)` | Eliminare i dati scaduti in base a una durata in secondi. Non necessario se lo storage scade automaticamente |

Poiché lo `Store` si occupa di serializzazione e ripristino, l'handler non deve applicare di nuovo `serialize()` o `json_encode()` all'array di sessione. Anche con la cifratura attiva, salva così com'è la stringa elaborata da `EncryptedStore`.

> [!WARNING]
>
> Distingui i dati non salvati da un guasto del backend. Lo `Store::readFromHandler()` esaminato tratta un risultato di lettura vuoto o `false` come una sessione vuota. Inoltre, `Store::save()` non controlla il valore restituito dal `write()` dell'handler.
>
> Nei pacchetti che devono sempre trattare i guasti come eccezioni, rendi esplicita la politica e testala: non sopprimere le eccezioni dello storage e, per i backend che restituiscono `false`, rilevalo nell'handler e convertilo in eccezione. Non è detto che Laravel ritenti automaticamente o segnali il mancato salvataggio.

## Registrare solo la creazione in boot

L'esempio minimo seguente riutilizza il `CacheBasedSessionHandler` integrato. Non implementa un nuovo protocollo di storage, ma mostra come registrare un driver personalizzato che usa uno store di cache con nome. `Acme\SessionBridge\` corrisponde a `src/` tramite il PSR-4 di Composer.

Prepara il provider in `src/SessionBridgeServiceProvider.php`.

```php theme={null}
<?php

namespace Acme\SessionBridge;

use Illuminate\Contracts\Container\Container;
use Illuminate\Session\CacheBasedSessionHandler;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;
use InvalidArgumentException;
use SessionHandlerInterface;

final class SessionBridgeServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Session::extend('acme-cache', function (Container $app): SessionHandlerInterface {
            $config = $app->make('config');
            $store = $config->get('session.store');
            $minutes = (int) $config->get('session.lifetime', 120);

            if (! is_string($store) || $store === '' || $minutes <= 0) {
                throw new InvalidArgumentException(
                    'acme-cache requires a named cache store and a positive session lifetime.'
                );
            }

            return new CacheBasedSessionHandler(
                $app->make('cache')->store($store),
                $minutes,
            );
        });
    }
}
```

`Session::extend()` ottiene il SessionManager e registra la creazione, ma a questo punto lo store di cache all'interno del callback non viene risolto. Lo storage viene creato solo quando il driver viene selezionato per la prima volta. Non eseguire verifiche di connessione né salvataggi di dati nel `boot()` del provider.

Se usi una tua implementazione di `SessionHandlerInterface`, sostituisci l'handler restituito dal callback. Arrivare fino a `new Store(...)` non corrisponde più al valore di ritorno atteso dal SessionManager.

* Carica il provider come un normale provider non differito.
* Completa la registrazione prima del primo utilizzo della sessione. Se un altro provider risolve il tuo driver prima, verifica anche l'ordine di caricamento.
* Usa un nome di driver specifico del pacchetto. Un `extend()` con lo stesso nome sovrascrive la creazione, e registrando il nome di un driver integrato si ottiene la precedenza sull'implementazione integrata.

## L'applicazione che usa il pacchetto sceglie in modo esplicito

L'utente modifica le chiavi corrispondenti del proprio `config/session.php` esistente. Le altre impostazioni restano invariate.

```php theme={null}
'driver' => 'acme-cache',
'store' => 'acme-sessions',
```

Per lo storage, aggiungi uno store con nome all'array `stores` esistente di `config/cache.php`. L'esempio seguente usa la connessione Redis `default`. [La configurazione della connessione Redis e il client necessario](https://kawax.biz/it/redis.md) vanno preparati dall'applicazione.

```php theme={null}
'acme-sessions' => [
    'driver' => 'redis',
    'connection' => 'default',
    'prefix' => 'acme-sessions:',
],
```

La connessione e il prefisso di questo esempio sono determinati dalla **configurazione dello store di cache**. Poiché non si passa per il `createRedisDriver()` del driver di sessione Redis integrato, aggiungere `session.connection` o `session.prefix` non viene recepito automaticamente da questo callback.

Questo callback non modifica né il Repository né la configurazione dello storage: usa lo store con nome così com'è. Anche lo store `array` di test condivide lo stesso spazio di salvataggio all'interno della stessa applicazione.

Il pacchetto non deve riscrivere incondizionatamente `session.driver`, il nome del cookie o il prefisso di cache globale dell'applicazione. Verifica con l'utente anche che lo spazio di salvataggio non sia condiviso con altri usi e che più server possano usare lo stesso storage.

> [!TIP]
>
> Se l'obiettivo è solo un normale salvataggio su Redis o database, parti dai driver di sessione integrati. Un driver personalizzato è un punto di estensione per i casi in cui servono un nuovo storage o regole di salvataggio aggiuntive.

## Mantenere l'unità del TTL e il formato di salvataggio

Il costruttore di `CacheBasedSessionHandler` riceve la durata di conservazione in **minuti**. Poiché `write()` passa ogni volta `$minutes * 60` al `put()` del Repository di cache, nell'esempio precedente il TTL si rinnova a ogni scrittura. `gc()` restituisce `0` e delega la scadenza alla cache.

La durata che `StartSession` passa al `gc($lifetime)` dell'handler è invece in **secondi**. Non implementare un salvataggio che usa direttamente il valore di `session.lifetime` come secondi.

Inoltre, le modifiche seguenti non si risolvono con un semplice cambio di nome nella configurazione.

| Modifica | Impatto sulle sessioni esistenti |
| - | - |
| Storage, connessione, prefisso delle chiavi | I vecchi dati non sono più visibili dal nuovo storage |
| `session.serialization` | I dati nel vecchio formato non si possono più ripristinare con il nuovo formato |
| `session.encrypt` | Lettura e scrittura in chiaro e cifrate non corrispondono più |
| `session.cookie` | Cambia la corrispondenza con il cookie dell'ID di sessione esistente inviato dal browser |

Se un aggiornamento del pacchetto modifica questi elementi, valuta istruzioni per una nuova autenticazione, l'eventuale necessità di una migrazione e i deploy in cui convivono la versione vecchia e quella nuova. Come in [Gestire la compatibilità tra versioni dei pacchetti](https://kawax.biz/it/advanced/package-versioning.md), includi formato di salvataggio e chiavi di configurazione nella specifica pubblica da mantenere.

## Uno Store già creato non viene sostituito solo cambiando la configurazione

`Manager`, la classe padre del SessionManager, conserva i driver creati per nome. Inoltre, `SessionServiceProvider` registra `session.store` come singleton.

| Operazione | Cosa succede | Cosa non succede |
| - | - | - |
| Modificare solo la configurazione | Le creazioni future possono leggere i nuovi valori | La modifica del TTL o della connessione dell'handler già creato |
| Registrare di nuovo `extend()` con lo stesso nome | Sostituzione del callback di creazione | L'eliminazione dello Store conservato con quel nome |
| `forgetDrivers()` | Eliminazione di tutti i driver conservati nel Manager | La sostituzione del `session.store` già risolto o dei riferimenti allo Store conservati altrove |

`forgetDrivers()` non è nemmeno un'API per eliminare i dati di sessione. Ricreare lo Store durante una richiesta in corso causa incongruenze con i dati già letti o con altri riferimenti.

Anche il CacheManager di questo esempio conserva gli store con nome. Se ne modifichi la configurazione di connessione, non basta risolvere di nuovo soltanto il SessionManager.

Nei deploy che usano la cache della configurazione, ricostruisci la cache e riavvia anche i worker di lunga durata perché carichino il nuovo codice e la nuova configurazione. Il riavvio dell'applicazione e la migrazione dei dati salvati sono attività distinte.

## Il salvataggio della sessione e il lock delle richieste sono separati

Implementare un handler personalizzato non basta a serializzare le richieste parallele sulla stessa sessione. Il blocco di `StartSession` usa i lock dello store di cache scelto con `session.block_store`, che non coincide necessariamente con lo storage della sessione stessa.

Se lo controlli con `Route::post(...)->block()` o simili, verifica separatamente uno store di cache che supporti i lock, la condivisione tra più server e i tempi di attesa e di mantenimento. Il pacchetto non deve attivare di sua iniziativa il blocco per l'intera applicazione.

## Testare fino al salvataggio e alla rilettura

Nei pacchetti che usano [Orchestra Testbench](https://kawax.biz/it/advanced/package-testing.md), carica il provider e prepara test che selezionino davvero il tuo driver. Ecco un esempio di `tests/SessionDriverTest.php`. La cache `array` di test non usa connessioni esterne.

```php theme={null}
<?php

namespace Acme\SessionBridge\Tests;

use Acme\SessionBridge\SessionBridgeServiceProvider;
use Illuminate\Session\CacheBasedSessionHandler;
use Illuminate\Session\Store;
use Orchestra\Testbench\TestCase;

final class SessionDriverTest extends TestCase
{
    protected function getPackageProviders($app): array
    {
        return [SessionBridgeServiceProvider::class];
    }

    protected function defineEnvironment($app): void
    {
        $app['config']->set('session.driver', 'acme-cache');
        $app['config']->set('session.store', 'session-test');
        $app['config']->set('session.lifetime', 2);
        $app['config']->set('session.cookie', 'acme_session');
        $app['config']->set('session.serialization', 'json');
        $app['config']->set('session.encrypt', false);
        $app['config']->set('cache.stores.session-test', ['driver' => 'array']);
    }

    public function test_it_wraps_the_registered_handler(): void
    {
        $store = $this->app->make('session')->driver();

        $this->assertInstanceOf(Store::class, $store);
        $this->assertInstanceOf(CacheBasedSessionHandler::class, $store->getHandler());
        $this->assertSame($store, $this->app->make('session.store'));
    }

    public function test_it_saves_and_reloads_the_payload(): void
    {
        $manager = $this->app->make('session');
        $first = $manager->driver();
        $first->start();
        $first->put('cart.quantity', 3);
        $first->save();

        $id = $first->getId();
        $payload = $first->getHandler()->read($id);
        $this->assertSame(3, json_decode($payload, true)['cart']['quantity']);

        $manager->forgetDrivers();
        $second = $manager->driver();
        $second->setId($id);
        $second->start();

        $this->assertNotSame($first, $second);
        $this->assertSame(3, $second->get('cart.quantity'));

        $second->invalidate();
        $this->assertSame('', $second->getHandler()->read($id));
    }

    public function test_forgetting_drivers_does_not_replace_the_store_binding(): void
    {
        $old = $this->app->make('session.store');
        $manager = $this->app->make('session');

        $this->app['config']->set('session.lifetime', 5);
        $manager->forgetDrivers();

        $this->assertNotSame($old, $manager->driver());
        $this->assertSame($old, $this->app->make('session.store'));
    }
}
```

Questo test è un **test isolato** che ricrea lo Store all'interno della stessa applicazione. Non verifica il middleware HTTP, l'andata e ritorno dei cookie, la persistenza verso un altro processo né la scadenza o i lock di un Redis reale. Non usare `array` come storage condiviso in produzione.

Prima di pubblicare un handler personalizzato, aggiungi anche i seguenti controlli.

* Un ID non salvato o scaduto restituisce una stringa vuota
* La stringa salvata viene ripristinata senza modifiche e non è più recuperabile dopo l'eliminazione
* L'unità e il rinnovo del TTL, e la scadenza sul backend reale
* I fallimenti di lettura, scrittura ed eliminazione producono i valori di ritorno o le eccezioni stabiliti
* L'andata e ritorno con `session.encrypt` attivo e i formati di serializzazione supportati
* Il solo caricamento del provider non accede allo storage e non sottrae la selezione ai driver integrati
* Se necessario, test HTTP che passano per il vero middleware `web` e i cookie, e il lock delle richieste parallele

## Pagine correlate

> [card] [Sessioni](https://kawax.biz/it/session.md)
>
> Lettura e scrittura della sessione, dati flash e rigenerazione dell'ID.

> [card] [Implementazione interna di Manager](https://kawax.biz/it/advanced/manager.md)
>
> Creazione e conservazione dei driver personalizzati e delega al driver predefinito.

> [card] [Store di cache personalizzati nei pacchetti](https://kawax.biz/it/advanced/package-cache-drivers.md)
>
> Suddivisione dei ruoli tra lo Store di cache usato come storage e il Repository, e contratto del TTL.

> [card] [Ciclo di vita dei servizi di un pacchetto](https://kawax.biz/it/advanced/package-service-lifetimes.md)
>
> Dipendenze conservate dai worker di lunga durata e isolamento dello stato.

## Fonti primarie

* [Documentazione ufficiale HTTP Session: driver personalizzati e blocco](https://github.com/laravel/docs/blob/625d1dfb61018e6bc50cc7ab67fca89ae018dfa2/session.md)
* [Documentazione ufficiale sullo sviluppo di pacchetti](https://github.com/laravel/docs/blob/625d1dfb61018e6bc50cc7ab67fca89ae018dfa2/packages.md)
* [SessionManager: creazione personalizzata e costruzione dello Store](https://github.com/laravel/framework/blob/v13.39.0/src/Illuminate/Session/SessionManager.php)
* [Store: lettura, serializzazione, salvataggio e rigenerazione dell'ID](https://github.com/laravel/framework/blob/v13.39.0/src/Illuminate/Session/Store.php)
* [CacheBasedSessionHandler: conversione del TTL e contratto di salvataggio](https://github.com/laravel/framework/blob/v13.39.0/src/Illuminate/Session/CacheBasedSessionHandler.php)
* [CacheManager: store con nome e configurazione Redis](https://github.com/laravel/framework/blob/v13.39.0/src/Illuminate/Cache/CacheManager.php)
* [Manager: extend, driver, forgetDrivers](https://github.com/laravel/framework/blob/v13.39.0/src/Illuminate/Support/Manager.php)
* [SessionServiceProvider: registrazione singleton del Manager e di session.store](https://github.com/laravel/framework/blob/v13.39.0/src/Illuminate/Session/SessionServiceProvider.php)
* [StartSession: salvataggio, GC e lock tramite cache](https://github.com/laravel/framework/blob/v13.39.0/src/Illuminate/Session/Middleware/StartSession.php)
* [CacheBasedSessionHandlerTest ufficiale: verifica di dati non salvati, TTL ed eliminazione](https://github.com/laravel/framework/blob/v13.39.0/tests/Session/CacheBasedSessionHandlerTest.php)


## Related topics

- [Store di cache personalizzati e contratti nei pacchetti](https://kawax.biz/it/advanced/package-cache-drivers.md)
- [Provider personalizzati](https://kawax.biz/it/packages/laravel-copilot-sdk/custom-providers.md)
- [Agenti personalizzati](https://kawax.biz/it/packages/laravel-copilot-sdk/custom-agents.md)
- [Hook di sessione](https://kawax.biz/it/packages/laravel-copilot-sdk/hooks.md)
- [Registrazione e test dei transport di posta nei pacchetti](https://kawax.biz/it/advanced/package-mail-transports.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.