Salta ai contenuti

Trigger di azione e listener di eventi

NextPDF emette eventi del ciclo di vita tramite un sistema compatibile con PSR-14 in NextPDF\Event. Registrare un listener con una priorità per reagire alla creazione di un documento, all’aggiunta di una pagina, al caricamento dei font, alla firma, alla cifratura, alla scrittura o all’output. Interrompere la catena quando necessario.

Terminal window
composer require nextpdf/core:^3

Il sistema di eventi è composto da due elementi pubblici. ListenerProvider associa una classe di evento a un elenco ordinato di callable listener. EventDispatcher percorre tale elenco e richiama ogni listener in ordine di priorità. Entrambe le classi sono final; conviene quindi estenderne il comportamento tramite composizione, non tramite sottoclasse.

Entrambe le classi sono conformi a PSR-14 tramite duck typing. EventDispatcher::dispatch() corrisponde alla firma PSR-14 dispatch() e restituisce l’evento dopo l’esecuzione di tutti i listener. ListenerProvider::getListenersForEvent() corrisponde alla firma del provider PSR-14. NextPDF non richiede il pacchetto PSR-14, ma se il progetto lo include, le interfacce risultano comunque allineate.

Due comportamenti sono rilevanti per gli autori di estensioni:

  • Ascolto con carattere jolly. Per risolvere i listener, il provider percorre le classi padre dell’evento e le interfacce implementate. Associare un listener alla classe base AbstractEvent per osservare ogni evento del ciclo di vita. Associare un listener a un’interfaccia per intercettare una famiglia di eventi.
  • Priorità e propagazione. La priorità più alta viene eseguita per prima. Le priorità uguali mantengono l’ordine di registrazione. Ogni evento che estende AbstractEvent è interrompibile. Un listener può richiamare stopPropagation(). Il dispatcher salta quindi i listener restanti.

Il dispatcher dispone di un percorso rapido a costo zero. Quando nessun listener è associato a una classe di evento o a una classe padre, dispatch() restituisce immediatamente dopo un solo controllo hasListeners().

EventoNamespaceEmesso quandoStabilità
DocumentCreatedEventNextPDF\Event\DocumentUn documento completa la costruzionesperimentale
PageAddedEventNextPDF\Event\DocumentUna pagina viene completamente inizializzatasperimentale
ContentRenderedEventNextPDF\Event\ContentIl contenuto viene sottoposto a rendering su una paginasperimentale
FontLoadedEventNextPDF\Event\ContentUna famiglia e uno stile di font vengono caricati per la prima voltasperimentale
SignatureAppliedEventNextPDF\Event\SecurityI byte della firma vengono incorporatisperimentale
EncryptionAppliedEventNextPDF\Event\SecurityLa cifratura viene configuratasperimentale
PdfSerializedEventNextPDF\Event\WriterLa serializzazione viene completatasperimentale
DocumentOutputEventNextPDF\Event\DocumentPrima della consegna dell’outputsperimentale

Il dispatcher, il provider, l’interfaccia marcatore e la classe base sono stable (a partire dalla 3.0.0). I payload degli eventi sono experimental. I loro argomenti del costruttore e le proprietà readonly possono cambiare in una release minore. Una patch release aggiunge soltanto elementi. Collegarsi ai nomi delle proprietà del payload tenendo conto di questo aspetto.

NextPDF\Event\ListenerProvider (stable, final):

MetodoRestituisceScopo
addListener(string $eventClass, callable $listener, int $priority = 0)voidRegistra un listener; la priorità più alta viene eseguita per prima. Genera InvalidConfigException in presenza di una classe vuota.
getListenersForEvent(EventInterface $event)list<callable>Risolve i listener, incluse le registrazioni di classi padre e interfacce.
hasListeners(string $eventClass)boolControllo a overhead zero lungo l’intera gerarchia di classi.
getListenerCount(string $eventClass)intConta solo le registrazioni dirette.
clearListeners()voidReimposta il provider.

NextPDF\Event\EventDispatcher (stable, final):

MetodoRestituisceScopo
dispatch(EventInterface $event)EventInterfaceRichiama i listener in ordine di priorità; rispetta l’interruzione della propagazione; restituisce l’evento.
getListenerProvider()ListenerProviderRestituisce il provider per aggiungere listener in fase di runtime.

I documenti che emettono eventi utilizzano NextPDF\Event\EventAwareDocumentTrait. Il relativo metodo setEventDispatcher() collega un dispatcher a un singolo documento. In assenza di un dispatcher, ciascun helper di dispatch non esegue alcuna operazione.

<?php
declare(strict_types=1);
use NextPDF\Event\EventDispatcher;
use NextPDF\Event\ListenerProvider;
use NextPDF\Event\Security\SignatureAppliedEvent;
$listeners = new ListenerProvider();
$listeners->addListener(
SignatureAppliedEvent::class,
static function (SignatureAppliedEvent $event): void {
\error_log("Signed at level {$event->signatureLevel} by {$event->signerName}");
},
priority: 100,
);
$dispatcher = new EventDispatcher($listeners);

Questo listener di audit è pensato per la produzione. Utilizza una priorità elevata per essere eseguito per primo, registra i dati in forma strutturata e aggiunge un listener catch-all sulla classe base per completezza.

<?php
declare(strict_types=1);
use NextPDF\Event\AbstractEvent;
use NextPDF\Event\EventDispatcher;
use NextPDF\Event\ListenerProvider;
use NextPDF\Event\Security\EncryptionAppliedEvent;
use NextPDF\Event\Security\SignatureAppliedEvent;
use Psr\Log\LoggerInterface;
final class SecurityAuditSubscriber
{
public function __construct(private readonly LoggerInterface $logger) {}
public function register(ListenerProvider $listeners): EventDispatcher
{
$listeners->addListener(
SignatureAppliedEvent::class,
function (SignatureAppliedEvent $event): void {
$this->logger->info('signature.applied', [
'level' => $event->signatureLevel,
'signer' => $event->signerName,
]);
},
priority: 1000,
);
$listeners->addListener(
EncryptionAppliedEvent::class,
function (EncryptionAppliedEvent $event): void {
$this->logger->info('encryption.applied', [
'algorithm' => $event->algorithm,
]);
},
priority: 1000,
);
// Catch-all: observe every lifecycle event for trace completeness.
$listeners->addListener(
AbstractEvent::class,
fn (AbstractEvent $event): mixed
=> $this->logger->debug('lifecycle', ['event' => $event->getEventName()]),
priority: -1000,
);
return new EventDispatcher($listeners);
}
}
  • Classi final. EventDispatcher e ListenerProvider sono final. Usare la composizione, non le sottoclassi.
  • Una classe di evento vuota genera un’eccezione. addListener('', ...) genera InvalidConfigException. Passare sempre una costante class-string.
  • Costo del carattere jolly. Un listener su AbstractEvent viene attivato per ogni evento. Assegnare ai listener catch-all una priorità bassa. Mantenerli leggeri.
  • Mutazione dell’output. DocumentOutputEvent trasporta i byte del PDF. Il motore li rilegge dopo il dispatch. La modifica di tali byte offre un ampio controllo e comporta un rischio reale. Un offset di byte errato danneggia il PDF e può invalidare una firma. È preferibile limitarsi all’osservazione, a meno di assumersi la responsabilità del risultato in termini di determinismo e firme.
  • Nessun dispatcher, nessun evento. Un documento privo di un dispatcher impostato tramite EventAwareDocumentTrait non emette alcun evento. È il percorso a costo zero previsto. Non si tratta di un errore di configurazione.

Il percorso rapido consiste in un unico controllo hasListeners() sulla catena delle classi padre. In assenza di listener, il dispatch è praticamente gratuito. Il provider memorizza nella cache l’elenco ordinato dei listener per ciascuna classe di evento. Svuota la cache solo in caso di modifica. Mantenere i listener non bloccanti. Vengono eseguiti inline nel percorso di rendering.

SignatureAppliedEvent ed EncryptionAppliedEvent sono i punti di ancoraggio per l’audit. Registrare listener ad alta priorità per tracciare la firma e la cifratura in un archivio a prova di manomissione. Non interrompere la catena su un evento di sicurezza, a meno che non si intenda disattivare i listener successivi. L’interruzione può disattivare silenziosamente gli hook di audit eseguiti in seguito.

In questa pagina non viene formulata alcuna dichiarazione normativa oltre alla compatibilità con PSR-14, che è basata esclusivamente su duck typing e non richiede il pacchetto PSR-14.

NextPDF Enterprise include listener verificati per gli eventi di firma e cifratura che alimentano un registro di audit a prova di manomissione. Il contratto dei listener è l’API pubblica degli eventi; di conseguenza, i listener personalizzati coesistono con quelli di Enterprise sullo stesso provider.

Il glossario definisce i termini event listener, event dispatcher, listener provider e stoppable event utilizzati in questa pagina. Per le definizioni canoniche, consultare il glossario pubblicato.