Ga naar inhoud

Event: PSR-14-taxonomie voor levenscyclusgebeurtenissen

De Event-module verstuurt getypeerde levenscyclusgebeurtenissen tijdens elke fase van de PDF-generatie. Listeners kunnen een document observeren of transformeren zonder de interne werking van de engine te wijzigen. De dispatcher volgt PHP Standards Recommendation 14 (PSR-14), zodat bestaande PSR-14-tooling compatibel blijft.

Terminal window
composer require nextpdf/core:^3

De Event-module wordt meegeleverd met het core-pakket. De module heeft geen extra afhankelijkheden: de typen EventInterface en StoppableEventInterface weerspiegelen de PSR-14-contracten zonder dat het pakket psr/event-dispatcher vereist is.

De module bestaat uit drie delen: een dispatcher, een listener-provider en een vaste set klassen voor levenscyclusgebeurtenissen.

EventDispatcher ontvangt een EventInterface-instance, vraagt de ListenerProvider om bijbehorende listeners en roept elke listener in volgorde van prioriteit aan. De methode dispatch() retourneert hetzelfde event-object. Een listener kan de status lezen die de engine in de gebeurtenis heeft geplaatst en status terugschrijven die de engine later leest. Dit komt overeen met het PSR-14-dispatchermodel.

ListenerProvider koppelt een event-klasse aan een lijst met aanroepbare functies in volgorde van prioriteit. Registratie is gebonden aan een instance, zonder statische status, zodat een worker-proces zijn eigen provider-instances kan aanhouden. De provider doorloopt ook de klassenboom van de gebeurtenis, inclusief de bijbehorende interfaces. Een listener op AbstractEvent ontvangt elke levenscyclusgebeurtenis. Een listener op een interface ontvangt elke gebeurtenis die deze implementeert.

StoppableEventInterface voegt propagatiebeheer toe. Een listener kan stopPropagation() aanroepen, waarna de dispatcher stopt met het aanroepen van latere listeners voor die cyclus. Een stopbare gebeurtenis draagt zelf de mogelijkheid mee om de listenerketen te stoppen. PSR-14 gebruikt hetzelfde model voor stopbare gebeurtenissen (PSR-14 psr_14_event#x4). AbstractEvent implementeert de interface via StoppableEventTrait, zodat elke levenscyclusgebeurtenis standaard stopbaar is.

De dispatcher heeft een snelpad zonder overhead. Hij controleert of er een listener is voor de event-klasse of een van de voorouders. Als die er niet is, retourneert hasListeners() false en keert dispatch() meteen terug. Een document zonder listeners kost per levenscycluspunt één booleaanse controle.

EventAwareDocumentTrait is het integratiepunt. Hij houdt een optionele EventDispatcher aan en stelt protected dispatch-helpers beschikbaar. De klasse Document roept deze helpers aan bij elk levenscycluspunt. Wanneer er geen dispatcher is ingesteld, is elke helper een no-op.

De levenscyclustaxonomie omvat deze gebeurtenissen:

GebeurtenisNamespaceGeactiveerd
DocumentCreatedEventEvent\DocumentNadat een document volledig is opgebouwd
PageAddedEventEvent\DocumentNadat een pagina is geïnitialiseerd
ContentRenderedEventEvent\ContentNadat HTML- of tekstinhoud op een pagina is gerenderd
FontLoadedEventEvent\ContentWanneer een lettertype in het register wordt geparseerd
EncryptionAppliedEventEvent\SecurityNadat de versleutelingsparameters zijn geconfigureerd
SignatureAppliedEventEvent\SecurityNadat een handtekening is ingebed
PdfSerializedEventEvent\WriterNa serialisatie, vóór de afgifte van de uitvoer
DocumentOutputEventEvent\DocumentVoordat de PDF-bytes de bestemming bereiken
SymboolSoortBelangrijkste leden
NextPDF\Event\EventInterfaceinterfacegetEventName(): non-empty-string
NextPDF\Event\StoppableEventInterfaceinterfaceisPropagationStopped(): bool
NextPDF\Event\StoppableEventTraittraitisPropagationStopped(), stopPropagation()
NextPDF\Event\AbstractEventabstracte klassegetEventName(); implementeert StoppableEventInterface
NextPDF\Event\EventDispatcherfinal classdispatch(EventInterface): EventInterface, getListenerProvider()
NextPDF\Event\ListenerProviderfinal classaddListener(), getListenersForEvent(), hasListeners(), getListenerCount(), clearListeners()
NextPDF\Event\EventAwareDocumentTraittraitsetEventDispatcher(), getEventDispatcher()
NextPDF\Event\Document\DocumentCreatedEventfinal class$document, $config
NextPDF\Event\Document\PageAddedEventfinal class$document, $pageIndex, $pageSize, $orientation
NextPDF\Event\Document\DocumentOutputEventfinal classgetPdfData(), setPdfData(), getByteSize(), $filename, $destination
NextPDF\Event\Content\ContentRenderedEventfinal class$document, $pageIndex, $contentType, $content
NextPDF\Event\Content\FontLoadedEventfinal class$family, $style, $fontType, $filePath
NextPDF\Event\Security\EncryptionAppliedEventfinal class$document, $algorithm, $allowPrint, $allowCopy, $allowModify
NextPDF\Event\Security\SignatureAppliedEventfinal class$document, $signatureLevel, $signerName, $reason, $location
NextPDF\Event\Writer\PdfSerializedEventfinal class$byteSize, $objectCount, $pageCount, $pdfVersion, $isLinearized, $isEncrypted

addListener() gooit NextPDF\Exception\InvalidConfigException wanneer de tekenreeks van de event-klasse leeg is.

Registreer een listener en verstuur vervolgens een levenscyclusgebeurtenis.

<?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);

De klasse Document roept de dispatch-helpers intern aan nadat een dispatcher via setEventDispatcher() is gekoppeld.

Koppel de dispatcher aan een document, stel de listenerprioriteit in en laat een gate-listener de propagatie stoppen.

<?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).
  • Wildcard-registratie gebruikt de klassenhiërarchie. Een listener op AbstractEvent ontvangt elke levenscyclusgebeurtenis, omdat elke gebeurtenis deze uitbreidt. Beperk listeners tot concrete klassen wanneer je slechts één gebeurtenis wilt verwerken.
  • Listeners met de hoogste prioriteit worden als eerste uitgevoerd. Gelijke prioriteiten behouden de invoegvolgorde (stabiele sortering).
  • stopPropagation() stopt alleen de huidige dispatch-cyclus. De volgende verstuurde gebeurtenis start een nieuwe cyclus.
  • De gesorteerde listener-cache wordt bij elke aanroep van addListener() ongeldig gemaakt, omdat een nieuwe registratie van een ouder of interface de resolutie voor meerdere event-klassen kan wijzigen.
  • $document op documentgebonden gebeurtenissen is getypeerd als object, niet als de klasse Document, om de Event-module vrij te houden van een harde Core-afhankelijkheid.
  • DocumentOutputEvent::setPdfData() verwacht een niet-lege tekenreeks. Het vervangen van de payload door een lege tekenreeks levert een ongeldig document op.
  • De dispatch-helpers op EventAwareDocumentTrait zijn no-ops totdat er een dispatcher is ingesteld, zodat runs zonder listeners geen meetbare kosten toevoegen.

Verzending zonder listeners is O(1) voor een leaf-event-klasse: één booleaanse controle met hasListeners(), waarna direct wordt teruggekeerd. Met listeners doorloopt getListenersForEvent() de event-voorouders eenmaal, sorteert het de verzamelde items en zet het de gesorteerde lijst per event-klasse in de cache tot de volgende mutatie. Een herhaalde verzending van dezelfde klasse is daarom O(k) over k overeenkomende listeners. Het standaard performance_budget voor deze referentiepagina is wall_ms: 1500, peak_mb: 64.

Listeners draaien binnen de generatiepijplijn met dezelfde rechten als de aanroeper. Behandel listener-code als vertrouwde code. DocumentOutputEvent stelt de uiteindelijke PDF-binary beschikbaar en laat een listener deze vervangen. Een auditlistener of integriteitslistener zou moeten draaien vóór elke listener die de bytes transformeert. Gebruik een hogere prioriteit. De beveiligingsgebonden gebeurtenissen (EncryptionAppliedEvent, SignatureAppliedEvent) rapporteren de toegepaste parameters voor auditlogging. Ze wijzigen de cryptografische uitkomst niet.

SpecificatieClausuleOnderwerp
PSR-14 (PHP-FIG)psr_14_event#x4Stopbare gebeurtenis stopt verdere listeners

De signatuur van dispatch(), de splitsing van de listener-provider en het stopbare-gebeurtenismodel volgen PSR-14. NextPDF declareert zijn eigen EventInterface en StoppableEventInterface. Het pakket heeft geen PSR-14-runtime-afhankelijkheid en blijft toch duck-type-compatibel.

  • /modules/core/contracts/ — publiek interface-oppervlak
  • /modules/core/observability/ — telemetrie- en metric-hooks
  • /modules/core/audit/ — integratie van het auditspoor
  • /modules/core/config/Config doorgegeven bij DocumentCreatedEvent
  • /modules/core/exception/InvalidConfigException van addListener()

Woordenlijst: PSR-14 · stopbare gebeurtenis · listener-provider