Lewati ke konten

Event: taksonomi event siklus hidup PSR-14

Modul Event melakukan dispatch event siklus hidup bertipe pada setiap tahap pembuatan PDF. Listener dapat mengamati atau mengubah dokumen tanpa mengubah bagian internal engine. Dispatcher mengikuti PHP Standards Recommendation 14 (PSR-14), sehingga perkakas PSR-14 yang sudah ada tetap kompatibel.

Terminal window
composer require nextpdf/core:^3

Modul Event sudah termasuk dalam paket core. Modul ini tidak memiliki dependensi tambahan: tipe EventInterface dan StoppableEventInterface mencerminkan kontrak PSR-14 tanpa memerlukan paket psr/event-dispatcher.

Modul ini terdiri dari tiga bagian: sebuah dispatcher, sebuah listener provider, dan sekumpulan kelas event siklus hidup yang tetap.

EventDispatcher menerima sebuah instance EventInterface, meminta listener yang cocok dari ListenerProvider, lalu memanggilnya satu per satu sesuai urutan prioritas. Metode dispatch() mengembalikan objek event yang sama. Sebuah listener dapat membaca state yang dimasukkan engine ke dalam event dan menulis kembali state yang kemudian dibaca engine. Perilaku ini sesuai dengan model dispatcher PSR-14.

ListenerProvider memetakan sebuah kelas event ke daftar callable dalam urutan prioritas. Registrasi bersifat instance-scoped, tanpa state statis, sehingga sebuah proses worker dapat menyimpan instance provider miliknya sendiri. Provider juga menelusuri pohon kelas event beserta interface-nya. Sebuah listener pada AbstractEvent melihat setiap event siklus hidup. Sebuah listener pada interface tertentu melihat setiap event yang mengimplementasikannya.

StoppableEventInterface menambahkan kontrol propagasi. Sebuah listener dapat memanggil stopPropagation(), lalu dispatcher berhenti memanggil listener berikutnya untuk siklus tersebut. Sebuah stoppable event adalah event khusus yang membawa mekanismenya sendiri untuk menghentikan rantai listener. PSR-14 menggunakan model yang sama untuk stoppable event (PSR-14 psr_14_event#x4). AbstractEvent mengimplementasikan interface tersebut melalui StoppableEventTrait, sehingga setiap event siklus hidup secara default dapat dihentikan.

Dispatcher memiliki jalur cepat tanpa overhead. Ia memeriksa apakah ada listener pada kelas event atau salah satu ancestor-nya. Bila tidak ada, hasListeners() mengembalikan false, dan dispatch() langsung kembali. Dokumen tanpa listener hanya menanggung satu pemeriksaan boolean pada setiap titik siklus hidup.

EventAwareDocumentTrait adalah titik integrasinya. Trait ini menyimpan sebuah EventDispatcher opsional dan menyediakan helper dispatch yang protected. Kelas Document memanggil helper ini pada setiap titik siklus hidup. Bila tidak ada dispatcher yang disetel, setiap helper menjadi no-op.

Taksonomi siklus hidup mencakup event berikut:

EventNamespaceDipicu
DocumentCreatedEventEvent\DocumentSetelah dokumen terbentuk sepenuhnya
PageAddedEventEvent\DocumentSetelah sebuah halaman diinisialisasi
ContentRenderedEventEvent\ContentSetelah konten HTML atau teks dirender ke sebuah halaman
FontLoadedEventEvent\ContentSaat sebuah font diurai ke dalam registry
EncryptionAppliedEventEvent\SecuritySetelah parameter enkripsi dikonfigurasi
SignatureAppliedEventEvent\SecuritySetelah sebuah tanda tangan disematkan
PdfSerializedEventEvent\WriterSetelah serialisasi, sebelum pengiriman output
DocumentOutputEventEvent\DocumentSebelum byte PDF mencapai tujuan
SimbolJenisAnggota utama
NextPDF\Event\EventInterfaceinterfacegetEventName(): non-empty-string
NextPDF\Event\StoppableEventInterfaceinterfaceisPropagationStopped(): bool
NextPDF\Event\StoppableEventTraittraitisPropagationStopped(), stopPropagation()
NextPDF\Event\AbstractEventabstract classgetEventName(); mengimplementasikan 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() melempar NextPDF\Exception\InvalidConfigException jika string kelas event kosong.

Daftarkan sebuah listener, lalu dispatch sebuah event siklus hidup.

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

Kelas Document memanggil helper dispatch secara internal setelah dispatcher dilampirkan melalui setEventDispatcher().

Lampirkan dispatcher ke dokumen, atur prioritas listener, dan hentikan propagasi dari sebuah gate listener.

<?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).
  • Registrasi wildcard menggunakan hierarki kelas. Sebuah listener pada AbstractEvent menerima setiap event siklus hidup karena setiap event meng-extend kelas tersebut. Batasi listener ke kelas konkret bila Anda hanya menginginkan satu event.
  • Prioritas listener diurutkan dari yang tertinggi terlebih dahulu. Prioritas yang sama mempertahankan urutan penyisipan (pengurutan stabil).
  • stopPropagation() hanya menghentikan siklus dispatch saat ini. Event berikutnya yang di-dispatch memulai siklus baru.
  • Cache listener terurut menjadi tidak valid pada setiap pemanggilan addListener() karena registrasi parent atau interface baru dapat mengubah resolusi untuk beberapa kelas event.
  • $document pada event berlingkup dokumen bertipe object, bukan kelas Document, agar modul Event bebas dari hard dependency terhadap Core.
  • DocumentOutputEvent::setPdfData() mengharapkan string yang tidak kosong. Mengganti payload dengan string kosong menghasilkan dokumen yang tidak valid.
  • Helper dispatch pada EventAwareDocumentTrait bersifat no-op sampai sebuah dispatcher disetel, sehingga jalur tanpa listener tidak menambah biaya yang terukur.

Dispatch tanpa listener adalah O(1) untuk kelas event daun: satu pemeriksaan boolean hasListeners(), lalu langsung kembali. Dengan adanya listener, getListenersForEvent() menelusuri ancestry event satu kali, mengurutkan entri yang terkumpul, dan menyimpan daftar terurut tersebut per kelas event hingga mutasi berikutnya. Oleh karena itu, dispatch berulang untuk kelas yang sama adalah O(k) terhadap k listener yang cocok. performance_budget baku untuk halaman referensi ini adalah wall_ms: 1500, peak_mb: 64.

Listener berjalan di dalam pipeline pembuatan dengan privilese yang sama seperti pemanggil. Perlakukan kode listener sebagai kode tepercaya. DocumentOutputEvent mengekspos biner PDF final dan memungkinkan sebuah listener menggantinya. Listener audit atau integritas sebaiknya berjalan sebelum listener apa pun yang mengubah byte. Gunakan prioritas yang lebih tinggi. Event berlingkup keamanan (EncryptionAppliedEvent, SignatureAppliedEvent) melaporkan parameter yang diterapkan untuk pencatatan audit. Event tersebut tidak mengubah hasil kriptografi.

SpesifikasiKlausulTopik
PSR-14 (PHP-FIG)psr_14_event#x4Stoppable event menghentikan listener berikutnya

Tanda tangan dispatch(), pemisahan listener-provider, dan model stoppable-event mengikuti PSR-14. NextPDF mendeklarasikan EventInterface dan StoppableEventInterface miliknya sendiri. Paket ini tidak memiliki dependensi runtime PSR-14, tetapi tetap kompatibel secara duck-type.

  • /modules/core/contracts/ — permukaan interface publik
  • /modules/core/observability/ — hook telemetri dan metrik
  • /modules/core/audit/ — integrasi jejak audit
  • /modules/core/config/Config yang diteruskan pada DocumentCreatedEvent
  • /modules/core/exception/InvalidConfigException dari addListener()

Glosarium: PSR-14 · stoppable event · listener provider