Pular para o conteúdo

Gatilhos de ação e ouvintes de evento

O NextPDF dispara eventos de ciclo de vida por meio de um sistema em NextPDF\Event compatível com a PHP Standards Recommendation 14 (PSR-14). Registre um ouvinte, escolha uma prioridade e reaja quando um documento for criado, uma página for adicionada, uma fonte for carregada, a assinatura ou a criptografia for executada, bytes forem gravados ou a saída começar. Interrompa a cadeia somente quando isso for necessário.

Terminal window
composer require nextpdf/core:^3

O sistema de eventos tem duas partes públicas. ListenerProvider mapeia cada classe de evento para uma lista ordenada de callables de ouvintes. EventDispatcher percorre essa lista e chama cada ouvinte na ordem de prioridade. As duas classes são final; portanto, estenda o comportamento por composição, não por herança.

As duas classes correspondem à PSR-14 por duck typing. EventDispatcher::dispatch() usa a assinatura dispatch() da PSR-14 e retorna o evento depois que cada ouvinte é executado. ListenerProvider::getListenersForEvent() usa a assinatura de provider da PSR-14. O NextPDF não exige o pacote PSR-14. Se o projeto o instalar, as interfaces ainda assim permanecerão alinhadas.

Dois comportamentos importam para autores de extensões:

  • Escuta com curinga. Para resolver os ouvintes, o provider percorre as classes pai e as interfaces do evento. Vincule um ouvinte à classe base AbstractEvent para observar todos os eventos de ciclo de vida. Vincule um ouvinte a uma interface para capturar uma família.
  • Prioridade e propagação. A prioridade mais alta é executada primeiro. Prioridades iguais preservam a ordem de registro. Todo evento que estende AbstractEvent é interrompível. Um ouvinte pode chamar stopPropagation(); nesse caso, o dispatcher ignora o restante.

O dispatcher tem um caminho rápido de custo zero. Quando nenhum ouvinte está vinculado a uma classe de evento nem a qualquer classe pai, dispatch() retorna imediatamente após uma única verificação hasListeners().

EventoNamespaceDisparado quandoEstabilidade
DocumentCreatedEventNextPDF\Event\DocumentA construção do documento terminaexperimental
PageAddedEventNextPDF\Event\DocumentUma página é totalmente inicializadaexperimental
ContentRenderedEventNextPDF\Event\ContentO conteúdo é renderizado em uma páginaexperimental
FontLoadedEventNextPDF\Event\ContentUma família e um estilo de fonte são carregados pela primeira vezexperimental
SignatureAppliedEventNextPDF\Event\SecurityOs bytes da assinatura são incorporadosexperimental
EncryptionAppliedEventNextPDF\Event\SecurityA criptografia é configuradaexperimental
PdfSerializedEventNextPDF\Event\WriterA serialização é concluídaexperimental
DocumentOutputEventNextPDF\Event\DocumentA entrega da saída está prestes a começarexperimental

O dispatcher, o provider, a interface marcadora e a classe base são stable (desde 3.0.0). Os payloads de evento são experimental. Os argumentos de construtor e as propriedades readonly deles podem mudar em uma versão menor. Versões de patch apenas adicionam novos itens. Vincule-se a nomes de propriedades de payload tendo essa restrição em mente.

NextPDF\Event\ListenerProvider (stable, final):

MétodoRetornaPropósito
addListener(string $eventClass, callable $listener, int $priority = 0)voidRegistra um ouvinte; a prioridade mais alta é executada primeiro. Lança InvalidConfigException quando a classe está vazia.
getListenersForEvent(EventInterface $event)list<callable>Resolve os ouvintes, incluindo registros em classes pai e interfaces.
hasListeners(string $eventClass)boolVerifica a hierarquia de classes sem overhead.
getListenerCount(string $eventClass)intConta apenas os registros diretos.
clearListeners()voidRedefine o provider.

NextPDF\Event\EventDispatcher (stable, final):

MétodoRetornaPropósito
dispatch(EventInterface $event)EventInterfaceInvoca os ouvintes na ordem de prioridade, respeita as interrupções de propagação e retorna o evento.
getListenerProvider()ListenerProviderAcessa o provider para adicionar ouvintes em tempo de execução.

Os documentos que disparam eventos usam NextPDF\Event\EventAwareDocumentTrait. O método setEventDispatcher() dele conecta um dispatcher a um documento. Sem um dispatcher, todo auxiliar de dispatch permanece inativo.

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

Este ouvinte de auditoria de produção usa prioridade alta para ser executado primeiro, grava logs estruturados e adiciona um catch-all na classe base para completar a cobertura.

<?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);
}
}
  • Classes final. EventDispatcher e ListenerProvider são final. Use composição; não herde delas.
  • Classe de evento vazia lança exceção. addListener('', ...) lança InvalidConfigException. Sempre passe uma constante de class-string.
  • Custo do curinga. Um ouvinte em AbstractEvent dispara para todos os eventos. Use prioridade baixa para ouvintes catch-all e mantenha-os leves.
  • Mutação da saída. DocumentOutputEvent carrega os bytes do Portable Document Format (PDF). O motor os lê de volta após o dispatch. Se você alterar esses bytes, terá controle amplo e risco na mesma medida. Um deslocamento de byte errado corrompe o PDF e pode quebrar uma assinatura. Prefira apenas observar, a menos que você assuma o resultado em nome do determinismo e das assinaturas.
  • Sem dispatcher, sem eventos. Um documento sem dispatcher definido por meio de EventAwareDocumentTrait não dispara eventos. Este é o caminho de custo zero previsto, não um erro de configuração.

O caminho rápido é uma única verificação hasListeners() na cadeia de pais. Sem ouvintes, o dispatch é praticamente gratuito. O provider mantém em cache a lista ordenada de ouvintes por classe de evento e limpa esse cache apenas quando os ouvintes mudam. Mantenha os ouvintes não bloqueantes, pois eles são executados inline no caminho de renderização.

SignatureAppliedEvent e EncryptionAppliedEvent são as âncoras de auditoria. Registre ouvintes de prioridade alta para registrar a assinatura e a criptografia em um armazenamento à prova de adulteração. Não interrompa a cadeia em um evento de segurança, a menos que você pretenda silenciar os ouvintes posteriores. Interrompê-la pode desativar silenciosamente os hooks de auditoria executados depois.

Esta página não faz nenhuma afirmação normativa além da compatibilidade com a PSR-14. Essa compatibilidade ocorre apenas por duck-type e não requer o pacote PSR-14.

O NextPDF Enterprise fornece ouvintes auditados para eventos de assinatura e criptografia que alimentam um log de auditoria à prova de adulteração. Como o contrato do ouvinte é a application programming interface (API) pública de eventos, os seus próprios ouvintes podem coexistir com os ouvintes do Enterprise no mesmo provider.

O glossário define os termos event listener, event dispatcher, listener provider e stoppable event usados nesta página. Consulte o glossário publicado para as definições canônicas.