v13.36.0, oltre a quella di Octane, e collega i metodi di registrazione di base del service container alla manutenzione a lungo termine di un pacchetto. Scegliere scoped() non basta a reimpostare tutti i riferimenti tra oggetti e tutto lo stato statico.
Scegliere il metodo di registrazione in base alla durata del servizio
Questa tabella confronta le risoluzioni normali. I percorsi speciali, come la risoluzione con parametri espliciti o il contextual binding, vanno verificati separatamente.
Anche con
bind(), se salvi il servizio ottenuto in una proprietà di un altro singleton, quel riferimento rimane. Il metodo di registrazione è una regola di creazione e riutilizzo del container, non una regola che obbliga a ricreare gli oggetti conservati da chi li usa.
scoped combina una registrazione condivisa con il tracciamento delle istanze da rimuovere
Il metodoContainer::scoped() esaminato esegue due operazioni.
- Registra la chiave in
scopedInstances. - Chiama
singleton()e la registra come binding condiviso.
instances usato dai singleton. forgetScopedInstances() esegue unset() sulle istanze salvate per le chiavi registrate. La logica di creazione del binding rimane, quindi la successiva risoluzione normale può creare un nuovo oggetto.
Questa operazione non reimposta le proprietà del vecchio oggetto e non riscrive i riferimenti conservati da altri oggetti. Un vecchio oggetto ancora referenziato può continuare a essere usato.
Il confine lo crea l’ambiente di esecuzione
Il container non monitora da solo l’inizio delle richieste HTTP o dei job.- Queue worker:
QueueServiceProviderpassa al Worker una callback che reimposta lo scope.Worker::daemon()la invoca nel ciclo prima di prelevare il job successivo e, all’interno della callback, esegueforgetScopedInstances()e la pulizia delle istanze risolte dalle Facade, tra le altre cose. - Octane: la configurazione predefinita ufficiale registra
FlushTemporaryContainerInstancessuOperationTerminated. Questo listener chiamaforgetScopedInstances(). - Comandi residenti o cicli personalizzati: ripetere semplicemente
make()non crea un confine. Il ciclo di vita deve essere progettato dall’infrastruttura di esecuzione.
Rendere scoped lo stato condiviso all’interno di un’elaborazione
Considera un pacchetto di spedizioni che vuole condividere l’identificativo della destinazione all’interno di una singola richiesta o di un singolo job. Invece di conservare direttamente la Request HTTP, salva solo il valore necessario.handle() del job dell’applicazione che usa il pacchetto, imposta un identificativo per cui sono già stati completati la validazione dell’input e il controllo dei permessi. Gli altri servizi che risolvono questo tipo nello stesso ciclo di vita possono fare riferimento allo stesso context.
scoped() non è una forma di persistenza su database o cache esterna. I valori da passare al job successivo vanno passati esplicitamente come dati del job.
Con Octane,
register() e boot() dei service provider vengono eseguiti all’avvio iniziale del worker. Non progettare il codice in modo da salvare nel context l’utente o la destinazione correnti in boot(). I valori specifici di ogni elaborazione vanno impostati dopo l’inizio dell’elaborazione.I riferimenti da un singleton a un servizio scoped non vengono aggiornati automaticamente
Con la registrazione seguente, rendere scoped solo il context non produce il comportamento atteso.DeliveryService salva il context in una proprietà nel costruttore, continua a conservare il primo context creato. Anche se il container rimuove il context salvato, la proprietà del DeliveryService già creato non cambia.
Scegli la soluzione in base alla responsabilità del servizio.
- Passare i valori necessari a ogni elaborazione come argomenti dei metodi. Se non serve condividere stato, è la soluzione più semplice.
- Rendere scoped anche il servizio che conserva il riferimento. Sia
DeliveryServicesia il context vengono ricreati per ogni elaborazione. - Passare un resolver che risolve al momento dell’uso. Se devi mantenere il singleton, recupera ogni volta il servizio necessario dal container corrente.
DeliveryService deve ricevere una Closure nel costruttore ed essere modificato in un’API che la invoca a ogni operazione. Invocare il resolver una sola volta nel costruttore e salvarne il risultato non risolve il problema. Si tratta di un esempio di progettazione che applica il container resolver della documentazione ufficiale di Octane a un context specifico del pacchetto.
Verificare anche lo stato che scoped non rimuove
scoped() non rimuove automaticamente lo stato seguente.
- Valori aggiunti a proprietà statiche proprie del pacchetto
- Riferimenti al context salvati in un altro singleton o in variabili globali
- “Utente corrente” o “destinazione corrente” conservati all’interno di un singleton
- Riferimenti conservati da Facade o livelli di cache personalizzati
Scrivere test che attraversano un confine nello stesso container
Se i test HTTP si limitano a creare ogni volta una nuova applicazione, possono sfuggire i problemi dovuti al riutilizzo dello stesso container in un processo residente. Per prima cosa, verifica con il solo container la condivisione all’interno della stessa elaborazione e la ricreazione dopo il confine. I test seguenti sono in formato PHPUnit. Il primo test, come i test ufficiali del Container, usaforgetScopedInstances() per verificare l’effetto del confine senza avviare l’intero Worker.
I test del solo container non verificano la sandbox di Octane né il percorso di esecuzione reale del queue worker. Servono anche test di integrazione che eseguano elaborazioni consecutive negli ambienti di esecuzione che dichiari di supportare. Nei deploy che modificano il metodo di registrazione o l’implementazione dei servizi, ricarica i processi residenti per applicare il nuovo codice.
Pagine correlate
Hook di risoluzione dei servizi nei pacchetti
Approfondisce afterResolving, il recupero delle istanze condivise e le differenze di configurazione alla nuova risoluzione.
Laravel Octane
Approfondisce la conservazione della Request e del container e le avvertenze operative sui processi residenti.
Fonti primarie consultate
- Documentazione ufficiale di Laravel: Binding scoped singletons
- Documentazione ufficiale di Laravel: Dependency injection and Octane
- Container: scoped(), singleton(), forgetScopedInstances(), resolve()
- ContainerTest: condivisione e ricreazione con scoped
- QueueServiceProvider: resetScope passato al Worker
- Worker: reset dello scope in daemon()
- Octane: FlushTemporaryContainerInstances
- Octane: listener predefiniti di OperationTerminated