Event: tassonomia PSR-14 degli eventi del ciclo di vita
In breve
Sezione intitolata “In breve”Il modulo Event distribuisce eventi del ciclo di vita tipizzati in ogni fase della generazione del PDF. I listener possono osservare o trasformare un documento senza modificare gli interni del motore. Il dispatcher segue il modello PSR-14, quindi gli strumenti PSR-14 esistenti rimangono compatibili.
Installazione
Sezione intitolata “Installazione”composer require nextpdf/core:^3Il modulo Event è incluso nel pacchetto core. Non ha dipendenze aggiuntive: i tipi EventInterface e StoppableEventInterface rispecchiano i contratti PSR-14 senza richiedere il pacchetto psr/event-dispatcher.
Panoramica concettuale
Sezione intitolata “Panoramica concettuale”Il modulo è composto da tre parti: un dispatcher, un provider di listener e un insieme fisso di classi di eventi del ciclo di vita.
EventDispatcher riceve un’istanza di EventInterface, richiede al ListenerProvider i listener corrispondenti e li richiama in ordine di priorità. Il metodo dispatch() restituisce lo stesso oggetto evento. In questo modo un listener può leggere lo stato che il motore ha inserito nell’evento. Può inoltre riscrivere lo stato che il motore leggerà in seguito. Questa è la forma del dispatcher PSR-14.
ListenerProvider associa una classe di evento a un elenco di callable in ordine di priorità. La registrazione è circoscritta all’istanza. Non esiste alcuno stato statico. Un processo worker può mantenere le proprie istanze di provider. Il provider percorre inoltre l’albero della classe di evento e le sue interfacce. In questo modo un listener su AbstractEvent vede ogni evento del ciclo di vita. Un listener su un’interfaccia vede ogni evento che la implementa.
StoppableEventInterface aggiunge il controllo della propagazione. Un listener può chiamare stopPropagation(). Il dispatcher smette quindi di richiamare ulteriori listener per quel ciclo. Un evento interrompibile è un caso speciale: porta con sé il proprio meccanismo per arrestare la catena dei listener. PSR-14 descrive lo stesso modello per gli eventi interrompibili (PSR-14 psr_14_event#x4). AbstractEvent implementa l’interfaccia tramite StoppableEventTrait. Di conseguenza, ogni evento del ciclo di vita è interrompibile per impostazione predefinita.
Il dispatcher dispone di un percorso rapido a costo zero. Controlla la presenza di un listener sulla classe di evento o su un qualsiasi antenato. Quando non ne esiste alcuno, hasListeners() restituisce false e dispatch() ritorna immediatamente. Un documento senza listener sostiene solo un controllo booleano per ogni punto del ciclo di vita.
EventAwareDocumentTrait è il punto di integrazione. Mantiene un EventDispatcher opzionale ed espone helper di distribuzione protetti. La classe Document richiama questi helper a ogni punto del ciclo di vita. Quando non è impostato alcun dispatcher, ogni helper è un’operazione nulla.
La tassonomia degli eventi del ciclo di vita:
| Evento | Namespace | Generato |
|---|---|---|
DocumentCreatedEvent | Event\Document | Dopo che un documento è stato costruito completamente |
PageAddedEvent | Event\Document | Dopo che una pagina è stata inizializzata |
ContentRenderedEvent | Event\Content | Dopo che il contenuto HTML o testuale è stato reso su una pagina |
FontLoadedEvent | Event\Content | Quando un font è stato analizzato e inserito nel registro |
EncryptionAppliedEvent | Event\Security | Dopo che i parametri di cifratura sono stati configurati |
SignatureAppliedEvent | Event\Security | Dopo che una firma è stata incorporata |
PdfSerializedEvent | Event\Writer | Dopo la serializzazione, prima della consegna dell’output |
DocumentOutputEvent | Event\Document | Prima che i byte del PDF arrivino alla destinazione |
Superficie API
Sezione intitolata “Superficie API”| Simbolo | Tipo | Membri principali |
|---|---|---|
NextPDF\Event\EventInterface | interfaccia | getEventName(): non-empty-string |
NextPDF\Event\StoppableEventInterface | interfaccia | isPropagationStopped(): bool |
NextPDF\Event\StoppableEventTrait | trait | isPropagationStopped(), stopPropagation() |
NextPDF\Event\AbstractEvent | classe astratta | getEventName(); implementa StoppableEventInterface |
NextPDF\Event\EventDispatcher | classe final | dispatch(EventInterface): EventInterface, getListenerProvider() |
NextPDF\Event\ListenerProvider | classe final | addListener(), getListenersForEvent(), hasListeners(), getListenerCount(), clearListeners() |
NextPDF\Event\EventAwareDocumentTrait | trait | setEventDispatcher(), getEventDispatcher() |
NextPDF\Event\Document\DocumentCreatedEvent | classe final | $document, $config |
NextPDF\Event\Document\PageAddedEvent | classe final | $document, $pageIndex, $pageSize, $orientation |
NextPDF\Event\Document\DocumentOutputEvent | classe final | getPdfData(), setPdfData(), getByteSize(), $filename, $destination |
NextPDF\Event\Content\ContentRenderedEvent | classe final | $document, $pageIndex, $contentType, $content |
NextPDF\Event\Content\FontLoadedEvent | classe final | $family, $style, $fontType, $filePath |
NextPDF\Event\Security\EncryptionAppliedEvent | classe final | $document, $algorithm, $allowPrint, $allowCopy, $allowModify |
NextPDF\Event\Security\SignatureAppliedEvent | classe final | $document, $signatureLevel, $signerName, $reason, $location |
NextPDF\Event\Writer\PdfSerializedEvent | classe final | $byteSize, $objectCount, $pageCount, $pdfVersion, $isLinearized, $isEncrypted |
addListener() genera NextPDF\Exception\InvalidConfigException quando la stringa della classe di evento è vuota.
Esempio di codice — Avvio rapido
Sezione intitolata “Esempio di codice — Avvio rapido”Registrare un listener e distribuire un evento del ciclo di vita.
<?php
declare(strict_types=1);
use NextPDF\Event\Document\PageAddedEvent;use NextPDF\Event\EventDispatcher;use NextPDF\Event\ListenerProvider;
$provider = new ListenerProvider();$provider->addListener( PageAddedEvent::class, static function (PageAddedEvent $event): void { \printf("Page %d added (%s)\n", $event->pageIndex, $event->pageSize->name); },);
$dispatcher = new EventDispatcher($provider);La classe Document richiama internamente gli helper di distribuzione dopo il collegamento di un dispatcher tramite setEventDispatcher().
Esempio di codice — Produzione
Sezione intitolata “Esempio di codice — Produzione”Collegare il dispatcher a un documento, usare la priorità dei listener e arrestare la propagazione da un listener di controllo.
<?php
declare(strict_types=1);
use NextPDF\Event\Document\DocumentOutputEvent;use NextPDF\Event\Document\PageAddedEvent;use NextPDF\Event\EventDispatcher;use NextPDF\Event\ListenerProvider;
$provider = new ListenerProvider();
// Higher priority runs first. A licensing gate observes every page.$provider->addListener( PageAddedEvent::class, static function (PageAddedEvent $event): void { if ($event->pageIndex >= 100) { // Stop later page listeners for this dispatch cycle. $event->stopPropagation(); } }, priority: 100,);
// Last-chance hook: replace the PDF bytes before delivery.$provider->addListener( DocumentOutputEvent::class, static function (DocumentOutputEvent $event): void { $optimized = \gzencode($event->getPdfData(), 0); if ($optimized !== false) { $event->setPdfData($optimized); } },);
$dispatcher = new EventDispatcher($provider);// Pass $dispatcher to a Document via setEventDispatcher($dispatcher).Casi limite e insidie
Sezione intitolata “Casi limite e insidie”- La registrazione con caratteri jolly usa la gerarchia delle classi. Un listener su
AbstractEventriceve ogni evento del ciclo di vita perché ogni evento lo estende. Circoscrivere i listener alle classi concrete quando si desidera un solo evento. - La priorità dei listener colloca prima quelle più alte. Le priorità uguali mantengono l’ordine di inserimento (ordinamento stabile).
stopPropagation()arresta solo il ciclo di distribuzione corrente. L’evento distribuito successivo avvia un nuovo ciclo.- La cache dei listener ordinati viene invalidata a ogni chiamata di
addListener(), perché la registrazione di un nuovo genitore o di una nuova interfaccia può modificare la risoluzione per diverse classi di evento. $documentsugli eventi con ambito documento è tipizzato comeobject, non come la classeDocument, per mantenere il modulo Event privo di una dipendenza rigida daCore.DocumentOutputEvent::setPdfData()richiede una stringa non vuota. Sostituire il payload con una stringa vuota produce un documento non valido.- Gli helper di distribuzione su
EventAwareDocumentTraitsono operazioni nulle finché non viene impostato un dispatcher; quindi le esecuzioni prive di listener non aggiungono alcun costo misurabile.
Prestazioni
Sezione intitolata “Prestazioni”La distribuzione senza listener è O(1) per una classe di evento foglia: un controllo booleano hasListeners(), seguito da un ritorno immediato. Con i listener, getListenersForEvent() percorre una volta l’ascendenza dell’evento, ordina le voci raccolte e memorizza nella cache l’elenco ordinato per ciascuna classe di evento fino alla mutazione successiva. La distribuzione ripetuta della stessa classe è quindi O(k) sui k listener corrispondenti. Il performance_budget predefinito per questa pagina di riferimento è wall_ms: 1500, peak_mb: 64.
Note di sicurezza
Sezione intitolata “Note di sicurezza”I listener vengono eseguiti all’interno della pipeline di generazione con gli stessi privilegi del chiamante. Trattare il codice dei listener come codice attendibile. DocumentOutputEvent espone il binario finale del PDF e consente a un listener di sostituirlo. Un listener di audit o di integrità deve essere eseguito prima di qualsiasi listener che trasformi i byte (usare una priorità più alta). Gli eventi con ambito di sicurezza (EncryptionAppliedEvent, SignatureAppliedEvent) riportano i parametri applicati per la registrazione di audit. Non modificano l’esito crittografico.
Conformità
Sezione intitolata “Conformità”| Specifica | Clausola | Argomento |
|---|---|---|
| PSR-14 (PHP-FIG) | psr_14_event#x4 | L’evento interrompibile arresta gli ulteriori listener |
La firma di dispatch(), la separazione tra listener e provider e il modello di evento interrompibile seguono PSR-14. NextPDF dichiara i propri EventInterface e StoppableEventInterface. Il pacchetto non ha alcuna dipendenza runtime da PSR-14, pur restando compatibile tramite duck typing.
Vedere anche
Sezione intitolata “Vedere anche”/modules/core/contracts/— superficie delle interfacce pubbliche/modules/core/observability/— hook per telemetria e metriche/modules/core/audit/— integrazione del trail di audit/modules/core/config/—Configpassato suDocumentCreatedEvent/modules/core/exception/—InvalidConfigExceptiondaaddListener()
Glossario: PSR-14 · evento interrompibile · listener provider