Disparadores de acciones y escuchas de eventos
De un vistazo
Sección titulada «De un vistazo»NextPDF emite eventos de ciclo de vida mediante un sistema compatible con PSR-14 en NextPDF\Event. Permite registrar escuchas con prioridad y reaccionar a la creación de un documento, a una página nueva, a la carga de una fuente, a la firma, al cifrado, a la escritura o a la salida. La cadena puede detenerse cuando sea necesario.
Instalación
Sección titulada «Instalación»composer require nextpdf/core:^3Visión conceptual
Sección titulada «Visión conceptual»El sistema de eventos expone dos partes públicas. ListenerProvider asocia una clase de evento a una lista ordenada de callables de escucha. EventDispatcher recorre esa lista y llama a cada escucha en orden de prioridad. Ambas clases son final, por lo que deben ampliarse mediante composición en lugar de herencia.
Ambas clases cumplen PSR-14 mediante duck typing. EventDispatcher::dispatch() respeta la firma dispatch() de PSR-14 y devuelve el evento después de ejecutar las escuchas aplicables. ListenerProvider::getListenersForEvent() respeta la firma del provider de PSR-14. NextPDF no requiere el paquete PSR-14, pero, si tu proyecto lo incluye, las interfaces siguen siendo compatibles.
Dos comportamientos son especialmente relevantes para los autores de extensiones:
- Escucha con comodín. Al resolver las escuchas, el provider recorre las clases padre del evento y sus interfaces. Vincular una escucha a la clase base
AbstractEventpermite observar todos los eventos de ciclo de vida. Vincularla a una interfaz permite capturar toda una familia. - Prioridad y propagación. La prioridad más alta se ejecuta primero. Con prioridades iguales, se conserva el orden de registro. Todo evento que extiende
AbstractEventes detenible. Una escucha puede llamar astopPropagation(). Entonces, el dispatcher omite las restantes.
El dispatcher tiene una ruta rápida sin costo. Cuando no hay ninguna escucha vinculada a una clase de evento ni a ningún padre, dispatch() retorna de inmediato tras una sola comprobación de hasListeners().
Eventos de ciclo de vida
Sección titulada «Eventos de ciclo de vida»| Evento | Espacio de nombres | Se emite cuando | Estabilidad |
|---|---|---|---|
DocumentCreatedEvent | NextPDF\Event\Document | Finaliza la construcción de un documento | experimental |
PageAddedEvent | NextPDF\Event\Document | Una página queda completamente inicializada | experimental |
ContentRenderedEvent | NextPDF\Event\Content | El contenido se renderiza en una página | experimental |
FontLoadedEvent | NextPDF\Event\Content | Una familia y un estilo de fuente se cargan por primera vez | experimental |
SignatureAppliedEvent | NextPDF\Event\Security | Los bytes de la firma quedan incrustados | experimental |
EncryptionAppliedEvent | NextPDF\Event\Security | El cifrado queda configurado | experimental |
PdfSerializedEvent | NextPDF\Event\Writer | La serialización se completa | experimental |
DocumentOutputEvent | NextPDF\Event\Document | Antes de entregar la salida | experimental |
El dispatcher, el provider, la interfaz marcadora y la clase base son stable (desde la 3.0.0). Los payloads de los eventos son experimental. Sus argumentos de constructor y sus propiedades de solo lectura pueden cambiar en una versión menor. Un parche solo añade elementos. Conviene vincularse a los nombres de las propiedades del payload teniendo esto en cuenta.
Superficie de la API
Sección titulada «Superficie de la API»NextPDF\Event\ListenerProvider (stable, final):
| Método | Devuelve | Propósito |
|---|---|---|
addListener(string $eventClass, callable $listener, int $priority = 0) | void | Registra una escucha; la prioridad más alta se ejecuta primero. Lanza InvalidConfigException ante una clase vacía. |
getListenersForEvent(EventInterface $event) | list<callable> | Resuelve las escuchas, incluidos los registros de las clases padre y de las interfaces. |
hasListeners(string $eventClass) | bool | Comprobación sin sobrecarga a lo largo de la jerarquía de clases. |
getListenerCount(string $eventClass) | int | Cuenta solo los registros directos. |
clearListeners() | void | Restablece el provider. |
NextPDF\Event\EventDispatcher (stable, final):
| Método | Devuelve | Propósito |
|---|---|---|
dispatch(EventInterface $event) | EventInterface | Invoca las escuchas en orden de prioridad; respeta la detención de la propagación; devuelve el evento. |
getListenerProvider() | ListenerProvider | Permite acceder al provider para añadir escuchas en tiempo de ejecución. |
Los documentos que emiten eventos usan NextPDF\Event\EventAwareDocumentTrait. Su método setEventDispatcher() conecta un dispatcher a un documento. Sin un dispatcher, los ayudantes de despacho no realizan ninguna acción.
Ejemplo de código — Inicio rápido
Sección titulada «Ejemplo de código — Inicio rápido»<?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);Ejemplo de código — Producción
Sección titulada «Ejemplo de código — Producción»Esta es una escucha de auditoría de producción. Usa una prioridad alta para ejecutarse primero, registra en formato estructurado y añade una escucha general en la clase base para lograr una trazabilidad más completa.
<?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); }}Casos límite y trampas
Sección titulada «Casos límite y trampas»- Clases finales.
EventDispatcheryListenerProvidersonfinal. Se debe componer en lugar de derivar subclases. - Una clase de evento vacía lanza una excepción.
addListener('', ...)lanzaInvalidConfigException. Se debe pasar siempre una constante de tipo class-string. - Costo del comodín. Una escucha en
AbstractEventse dispara para cada evento. Conviene asignar prioridad baja a las escuchas generales y mantenerlas ligeras. - Mutación de la salida.
DocumentOutputEventtransporta los bytes del PDF. El motor los vuelve a leer después del despacho. Modificar esos bytes otorga un control amplio y conlleva un riesgo real. Un desplazamiento de bytes incorrecto corrompe el PDF y puede romper una firma. Es preferible observar, salvo que se asuma la responsabilidad del resultado para el determinismo y las firmas. - Sin dispatcher, no hay eventos. Un documento sin un dispatcher configurado mediante
EventAwareDocumentTraitno emite eventos. Esta es la ruta sin costo prevista. No es un error de configuración.
Rendimiento
Sección titulada «Rendimiento»La ruta rápida consiste en una única comprobación de la cadena de clases padre con hasListeners(). Sin escuchas, el despacho es prácticamente gratuito. El provider almacena en caché la lista ordenada de escuchas por clase de evento. Solo invalida esa caché cuando hay un cambio. Mantener las escuchas sin bloqueo. Se ejecutan en línea en la ruta de renderizado.
Notas de seguridad
Sección titulada «Notas de seguridad»SignatureAppliedEvent y EncryptionAppliedEvent son los anclajes de auditoría. Registrar escuchas de prioridad alta permite dejar constancia de la firma y el cifrado en un almacén a prueba de manipulaciones. La cadena no debe detenerse en un evento de seguridad, a menos que la intención sea silenciar las escuchas posteriores. Detenerla puede desactivar de forma silenciosa los ganchos de auditoría que se ejecutan después.
Conformidad
Sección titulada «Conformidad»En esta página no se hace ninguna afirmación normativa más allá de la compatibilidad con PSR-14, que se basa únicamente en duck typing y no requiere el paquete PSR-14.
Contexto comercial
Sección titulada «Contexto comercial»NextPDF Enterprise incluye escuchas auditadas para los eventos de firma y de cifrado que alimentan un registro de auditoría a prueba de manipulaciones. El contrato de las escuchas es la API pública de eventos, por lo que tus propias escuchas conviven con las de Enterprise en el mismo provider.
Véase también
Sección titulada «Véase también»- Visión general de la creación de extensiones
- Fuentes personalizadas
- Maquetación personalizada e interceptación de texto
- Contrato del proveedor de KMS
- Reglas de estabilidad de la SPI
Contratos y módulos relacionados
Sección titulada «Contratos y módulos relacionados»- Referencia del módulo de eventos — la taxonomía de eventos de ciclo de vida de PSR-14 y los detalles internos del dispatcher.
- Referencia de los contratos de firma — los contratos que respaldan
SignatureAppliedEvent. - Reglas de estabilidad de la SPI — cómo se versionan el dispatcher estable y los payloads experimentales.
- Fuentes personalizadas — cómo emparejar
FontLoadedEventcon el contrato del registro. - Visión general de la creación de extensiones — la superficie pública completa de la SPI.
El glosario define los términos event listener, event dispatcher, listener provider y stoppable event que se usan en esta página. Consulta el glosario publicado para ver cada definición canónica.