Déclencheurs d'action et écouteurs d'événements
En un coup d’œil
Section intitulée « En un coup d’œil »NextPDF émet des événements de cycle de vie via un système compatible PSR-14, exposé dans NextPDF\Event. Enregistre des écouteurs avec une priorité pour réagir à la création d’un document, à l’ajout d’une page, au chargement d’une police, à la signature, au chiffrement, à l’écriture sur disque ou à la sortie. Interromps la chaîne quand tu en as besoin.
Installation
Section intitulée « Installation »composer require nextpdf/core:^3Vue d’ensemble conceptuelle
Section intitulée « Vue d’ensemble conceptuelle »Le système d’événements expose deux composants publics. ListenerProvider associe une classe d’événement à une liste triée d’écouteurs appelables. EventDispatcher parcourt cette liste et appelle chaque écouteur par ordre de priorité. Les deux classes étant final, compose avec elles plutôt que d’en hériter.
Les deux classes s’alignent sur PSR-14 par duck typing. EventDispatcher::dispatch() correspond à la signature PSR-14 dispatch() et renvoie l’événement une fois tous les écouteurs exécutés. ListenerProvider::getListenersForEvent() correspond à la signature du provider PSR-14. NextPDF n’a pas besoin du paquet PSR-14, mais si ton projet l’installe, les interfaces restent alignées.
Deux comportements sont importants pour les auteurs d’extensions :
- Écoute par joker. Pour résoudre les écouteurs, le provider parcourt les classes parentes de l’événement ainsi que ses interfaces. Associe un écouteur à la classe de base
AbstractEventpour surveiller chaque événement du cycle de vie. Associe-le à une interface pour capter toute une famille. - Priorité et propagation. La priorité la plus élevée s’exécute en premier. Les priorités égales conservent l’ordre d’enregistrement. Tout événement qui étend
AbstractEventpeut être interrompu. Un écouteur peut appelerstopPropagation(). Le répartiteur ignore alors les écouteurs restants.
Le répartiteur dispose d’un chemin rapide à coût nul. Quand aucun écouteur n’est lié à une classe d’événement ou à l’un de ses parents, dispatch() renvoie aussitôt après une seule vérification hasListeners().
Événements du cycle de vie
Section intitulée « Événements du cycle de vie »| Événement | Espace de noms | Déclenché quand | Stabilité |
|---|---|---|---|
DocumentCreatedEvent | NextPDF\Event\Document | La construction d’un document est terminée | experimental |
PageAddedEvent | NextPDF\Event\Document | Une page est entièrement initialisée | experimental |
ContentRenderedEvent | NextPDF\Event\Content | Du contenu est rendu sur une page | experimental |
FontLoadedEvent | NextPDF\Event\Content | Une famille et un style de police sont chargés pour la première fois | experimental |
SignatureAppliedEvent | NextPDF\Event\Security | Les octets de signature sont intégrés | experimental |
EncryptionAppliedEvent | NextPDF\Event\Security | Le chiffrement est configuré | experimental |
PdfSerializedEvent | NextPDF\Event\Writer | La sérialisation est terminée | experimental |
DocumentOutputEvent | NextPDF\Event\Document | Avant l’envoi de la sortie | experimental |
Le répartiteur, le provider, l’interface de marquage et la classe de base sont stable (depuis la 3.0.0). Les charges utiles des événements sont experimental. Leurs arguments de constructeur et leurs propriétés readonly peuvent changer dans une version mineure. Une version corrective n’ajoute que des éléments. Garde cela à l’esprit si tu relies ton code aux noms des propriétés de charge utile.
Surface de l’API
Section intitulée « Surface de l’API »NextPDF\Event\ListenerProvider (stable, final) :
| Méthode | Retourne | Rôle |
|---|---|---|
addListener(string $eventClass, callable $listener, int $priority = 0) | void | Enregistre un écouteur ; la priorité la plus élevée s’exécute en premier. Lève InvalidConfigException si la classe est vide. |
getListenersForEvent(EventInterface $event) | list<callable> | Résout les écouteurs, y compris les enregistrements de parents et d’interfaces. |
hasListeners(string $eventClass) | bool | Vérifie sans surcoût dans la hiérarchie de classes. |
getListenerCount(string $eventClass) | int | Compte uniquement les enregistrements directs. |
clearListeners() | void | Réinitialise le provider. |
NextPDF\Event\EventDispatcher (stable, final) :
| Méthode | Retourne | Rôle |
|---|---|---|
dispatch(EventInterface $event) | EventInterface | Appelle les écouteurs par ordre de priorité ; respecte l’arrêt de propagation ; renvoie l’événement. |
getListenerProvider() | ListenerProvider | Donne accès au provider pour ajouter des écouteurs à l’exécution. |
Les documents qui émettent des événements utilisent NextPDF\Event\EventAwareDocumentTrait. Sa méthode setEventDispatcher() raccorde un répartiteur à un document. Sans répartiteur, chaque assistant de dispatch reste inactif.
Exemple de code — Démarrage rapide
Section intitulée « Exemple de code — Démarrage rapide »<?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);Exemple de code — Production
Section intitulée « Exemple de code — Production »Voici un écouteur d’audit de production. Il utilise une priorité élevée pour s’exécuter en premier, journalise les données sous forme structurée et ajoute un écouteur fourre-tout sur la classe de base par souci d’exhaustivité.
<?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); }}Cas limites et pièges
Section intitulée « Cas limites et pièges »- Classes finales.
EventDispatcheretListenerProvidersontfinal. Utilise la composition, ne les sous-classe pas. - Une classe d’événement vide lève une exception.
addListener('', ...)lèveInvalidConfigException. Passe toujours une constante class-string. - Coût du joker. Un écouteur sur
AbstractEventse déclenche pour chaque événement. Donne aux écouteurs fourre-tout une priorité basse. Garde-les légers. - Mutation de la sortie.
DocumentOutputEventtransporte les octets du PDF. Le moteur les relit après le dispatch. Modifier ces octets donne un contrôle étendu et comporte un risque réel. Un mauvais décalage d’octet corrompt le PDF et peut casser une signature. Privilégie l’observation, sauf si tu assumes la responsabilité du résultat pour le déterminisme et les signatures. - Pas de répartiteur, pas d’événements. Un document sans répartiteur défini via
EventAwareDocumentTraitn’émet aucun événement. C’est le chemin à coût nul prévu. Ce n’est pas une erreur de configuration.
Performance
Section intitulée « Performance »Le chemin rapide se limite à une seule vérification hasListeners() sur la chaîne de parents. Sans écouteur, le dispatch est quasiment gratuit. Le provider met en cache la liste triée des écouteurs par classe d’événement. Il ne vide ce cache que lorsqu’il y a un changement. Garde les écouteurs non bloquants. Ils s’exécutent en ligne sur le chemin de rendu.
Notes de sécurité
Section intitulée « Notes de sécurité »SignatureAppliedEvent et EncryptionAppliedEvent sont les points d’ancrage de l’audit. Enregistre des écouteurs de priorité élevée pour journaliser la signature et le chiffrement dans un magasin inviolable. N’interromps pas la chaîne sur un événement de sécurité, sauf si tu veux faire taire les écouteurs ultérieurs. L’interrompre peut désactiver silencieusement des hooks d’audit qui s’exécutent après.
Conformité
Section intitulée « Conformité »Aucune affirmation normative n’est faite sur cette page au-delà de la compatibilité PSR-14, qui repose uniquement sur le duck typing et ne nécessite pas le paquet PSR-14.
Contexte commercial
Section intitulée « Contexte commercial »NextPDF Enterprise fournit des écouteurs audités pour les événements de signature et de chiffrement qui alimentent un journal d’audit inviolable. Comme le contrat des écouteurs est l’API publique des événements, tes propres écouteurs coexistent avec ceux d’Enterprise sur le même provider.
Voir aussi
Section intitulée « Voir aussi »- Présentation de la création d’extensions
- Polices personnalisées
- Mise en page personnalisée et interception de texte
- Contrat du fournisseur KMS
- Règles de stabilité du SPI
Contrats et modules associés
Section intitulée « Contrats et modules associés »- Référence du module Event — la taxonomie des événements de cycle de vie PSR-14 et le fonctionnement interne du répartiteur.
- Référence des contrats de signature — les contrats sous-jacents à
SignatureAppliedEvent. - Règles de stabilité du SPI — comment le répartiteur stable et les charges utiles expérimentales sont versionnés.
- Polices personnalisées — associe
FontLoadedEventau contrat du registre. - Présentation de la création d’extensions — la surface SPI publique complète.
Le glossaire définit les termes écouteur d’événement, répartiteur d’événements, fournisseur d’écouteurs et événement interruptible utilisés sur cette page. Consulte le glossaire publié pour chaque définition canonique.