Ga naar inhoud

Action triggers en event listeners

NextPDF verstuurt lifecycle-events via het met PHP Standards Recommendation 14 (PSR-14) compatibele systeem in NextPDF\Event. Registreer een listener, kies een prioriteit en reageer wanneer een document wordt aangemaakt, een pagina wordt toegevoegd, een lettertype wordt geladen, ondertekening of versleuteling wordt uitgevoerd, bytes worden weggeschreven of de uitvoer begint. Stop de keten alleen wanneer dat nodig is.

Terminal window
composer require nextpdf/core:^3

Het event-systeem heeft twee publieke onderdelen. ListenerProvider koppelt elke event-klasse aan een gesorteerde lijst met listener-callables. EventDispatcher loopt die lijst af en roept elke listener aan op volgorde van prioriteit. Omdat beide klassen final zijn, gebeurt uitbreiding via compositie en niet via subclassing.

Beide klassen voldoen via duck typing aan PSR-14. EventDispatcher::dispatch() volgt de PSR-14-signatuur van dispatch() en geeft het event terug nadat elke listener is uitgevoerd. ListenerProvider::getListenersForEvent() volgt de PSR-14-provider-signatuur. NextPDF vereist het PSR-14-pakket niet. Ook wanneer uw project dat pakket installeert, blijven de interfaces op elkaar aansluiten.

Voor extensie-auteurs zijn vooral twee gedragingen van belang:

  • Wildcard-luisteren. Om listeners te resolven, doorloopt de provider de bovenliggende klassen en interfaces van het event. Koppel een listener aan de basisklasse AbstractEvent om elk lifecycle-event te volgen. Koppel er een aan een interface om een eventfamilie af te vangen.
  • Prioriteit en propagatie. Bij een hogere prioriteit wordt de listener eerder uitgevoerd. Gelijke prioriteiten behouden de registratievolgorde. Elk event dat AbstractEvent uitbreidt, is stopbaar. Een listener kan stopPropagation() aanroepen; daarna slaat de dispatcher de rest over.

De dispatcher heeft een snelpad zonder overhead. Wanneer er voor een event-klasse of bovenliggende klasse geen listener is gekoppeld, keert dispatch() na één controle met hasListeners() direct terug.

EventNamespaceGeactiveerd wanneerStabiliteit
DocumentCreatedEventNextPDF\Event\DocumentDocumentconstructie is voltooidexperimental
PageAddedEventNextPDF\Event\DocumentEen pagina is volledig geïnitialiseerdexperimental
ContentRenderedEventNextPDF\Event\ContentInhoud wordt naar een pagina gerenderdexperimental
FontLoadedEventNextPDF\Event\ContentEen lettertypefamilie en -stijl worden voor het eerst geladenexperimental
SignatureAppliedEventNextPDF\Event\SecurityHandtekeningbytes worden ingebedexperimental
EncryptionAppliedEventNextPDF\Event\SecurityVersleuteling wordt geconfigureerdexperimental
PdfSerializedEventNextPDF\Event\WriterSerialisatie is voltooidexperimental
DocumentOutputEventNextPDF\Event\DocumentUitvoerlevering staat op het punt te beginnenexperimental

De dispatcher, de provider, de marker-interface en de basisklasse zijn stable (sinds 3.0.0). De event-payloads zijn experimental. Hun constructorargumenten en readonly-eigenschappen kunnen in een minor release veranderen. Patch-releases voegen alleen toe. Houd bij koppeling aan payload-eigenschapsnamen rekening met die beperking.

NextPDF\Event\ListenerProvider (stable, final):

MethodeGeeft terugDoel
addListener(string $eventClass, callable $listener, int $priority = 0)voidRegistreer een listener; een hogere prioriteit wordt eerst uitgevoerd. Werpt InvalidConfigException wanneer de klasse leeg is.
getListenersForEvent(EventInterface $event)list<callable>Bepaal listeners, inclusief registraties op bovenliggende klassen en interfaces.
hasListeners(string $eventClass)boolControleer de klassenhiërarchie zonder overhead.
getListenerCount(string $eventClass)intTel alleen directe registraties.
clearListeners()voidReset de provider.

NextPDF\Event\EventDispatcher (stable, final):

MethodeGeeft terugDoel
dispatch(EventInterface $event)EventInterfaceRoept listeners aan op volgorde van prioriteit, respecteert propagatiestops en geeft het event terug.
getListenerProvider()ListenerProviderGeeft toegang tot de provider om tijdens runtime listeners toe te voegen.

Documenten die events versturen, gebruiken NextPDF\Event\EventAwareDocumentTrait. De methode setEventDispatcher() van die trait koppelt een dispatcher aan één document. Zonder dispatcher doet elke dispatch-helper niets.

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

Deze auditlistener voor productie gebruikt een hoge prioriteit zodat hij als eerste wordt uitgevoerd, schrijft gestructureerde logs en voegt voor de volledigheid een catch-all op de basisklasse toe.

<?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);
}
}
  • Final-klassen. EventDispatcher en ListenerProvider zijn final. Stel ze samen via compositie; maak er geen subklasse van.
  • Lege event-klasse werpt een fout. addListener('', ...) werpt InvalidConfigException. Geef altijd een class-string-constante door.
  • Wildcard-kosten. Een listener op AbstractEvent wordt bij elk event geactiveerd. Geef catch-all-listeners een lage prioriteit en houd ze lichtgewicht.
  • Uitvoermutatie. DocumentOutputEvent draagt bytes in Portable Document Format (PDF). De engine leest ze na de dispatch weer in. Als u die bytes wijzigt, krijgt u veel controle en ook veel risico. Een verkeerde byte-offset beschadigt de PDF en kan een handtekening breken. Observeer bij voorkeur, tenzij u het resultaat zelf beheert met het oog op determinisme en handtekeningen.
  • Geen dispatcher, geen events. Een document waarvoor via EventAwareDocumentTrait geen dispatcher is ingesteld, verstuurt geen events. Dit is het beoogde snelpad zonder overhead, geen installatiefout.

Het snelpad bestaat uit één controle van de bovenliggende keten met hasListeners(). Zonder listeners is dispatch vrijwel gratis. De provider bewaart de gesorteerde listenerlijst per event-klasse in de cache en wist die cache alleen wanneer de listeners veranderen. Houd listeners niet-blokkerend, want ze worden inline op het renderpad uitgevoerd.

SignatureAppliedEvent en EncryptionAppliedEvent zijn de auditankers. Registreer listeners met hoge prioriteit om ondertekening en versleuteling weg te schrijven naar een fraudebestendige opslag. Stop de keten niet bij een beveiligingsevent, tenzij u latere listeners wilt onderdrukken. Door te stoppen kunt u stilzwijgend audithooks uitschakelen die daarna zouden worden uitgevoerd.

Deze pagina doet geen normatieve uitspraken buiten PSR-14-compatibiliteit. Die compatibiliteit is uitsluitend gebaseerd op duck typing en vereist het PSR-14-pakket niet.

NextPDF Enterprise levert geauditeerde listeners voor handtekening- en versleutelingsevents die een fraudebestendig auditlog voeden. Omdat het listener-contract de publieke event-application programming interface (API) is, kunnen uw eigen listeners naast de Enterprise-listeners op dezelfde provider draaien.

De woordenlijst definieert de termen event listener, event dispatcher, listener provider en stoppable event die op deze pagina worden gebruikt. Raadpleeg de gepubliceerde woordenlijst voor canonieke definities.