NextPDF Symfony in der Produktion
Auf einen Blick
Abschnitt betitelt „Auf einen Blick“Das Bundle ist auf langlebige PHP-Laufzeiten ausgelegt. Dokumente werden nicht geteilt, die Font-Registry wird nach dem Warmup gesperrt, und der Image-Cache wird zwischen den Requests zurückgesetzt. Streamen Sie große PDFs und lagern Sie rechenintensive Jobs an Messenger-Worker aus.
Worker-sicherer Servicelebenszyklus
Abschnitt betitelt „Worker-sicherer Servicelebenszyklus“Langlebige Laufzeiten halten den Container über mehrere Requests hinweg am Leben; deshalb darf kein Zustand aus einem einzelnen Request durchsickern. FrankenPHP, RoadRunner und Messenger-Worker arbeiten alle nach diesem Prinzip. Die services.php des Bundles bildet den folgenden Lebenszyklus ab, geprüft anhand der Service-Definitionen:
- Document — nicht geteilt.
nextpdf.document(und die AliasePdfDocumentInterface/Document) werden bei jeder Auflösung als frische Instanz bereitgestellt. Unter PSR-11 darf ein Container für dieselbe ID bei jedemget()legitim einen anderen Wert zurückgeben (PSR-11 §1.1.2). Lösen Sie pro Request ein Dokument auf. Halten Sie niemals eines über mehrere Requests hinweg. - FontRegistry — geteilt und gesperrt. Die Registry ist ein Singleton für die Prozesslebensdauer. Nach
warmup()(wennpreload_fontsnicht leer ist) ruft der Compiler-Passlock()auf. Die Sperre verhindert Änderungen zur Laufzeit und damit eine Verunreinigung des Font-Zustands über Request-Grenzen hinweg. - ImageRegistry — geteilt, pro Request zurückgesetzt. Der begrenzte Least-recently-used-Image-Cache (LRU) wird geteilt, ist aber mit
kernel.resetfür die Methoderesetgetaggt, sodass Symfony ihn in Laufzeiten, diekernel.resetberücksichtigen, zwischen den Requests leert. - EInvoice-Contracts — nicht geteilt. Wenn Premium-Implementierungen vorhanden sind, werden die Embedder-, Validator-, Profil- und Schematron-Services nicht geteilt registriert. Der Parser-Kontext eines einzelnen Aufrufs sickert dadurch niemals über Requests hinweg durch.
Empfohlenes Injection-Muster
Abschnitt betitelt „Empfohlenes Injection-Muster“Injizieren Sie PdfFactory — einen geteilten, zustandslosen Konfigurationshalter — und rufen Sie create() pro Request auf:
public function __construct(private readonly PdfFactory $pdf) {}
public function action(): Response{ $doc = $this->pdf->create(); // fresh, disposable // ... build ... return PdfResponse::inline($doc, 'document.pdf');}Injizieren Sie Document oder nextpdf.document nicht in einen Service, der selbst geteilt ist und über mehrere Requests hinweg gehalten wird. Lösen Sie es stattdessen innerhalb der Request-gebundenen Methode auf.
Streaming großer Dokumente
Abschnitt betitelt „Streaming großer Dokumente“PdfResponse::streamDownload() und streamInline() geben einen StreamedResponse zurück. Der Callback gibt den PDF-Body in 64-KB-Chunks aus und führt nach jedem Chunk einen Flush aus. Das begrenzt den Response-Puffer bei großen Dokumenten. Die folgenden beiden Trade-offs wurden anhand von PdfResponse geprüft:
- Die gestreamten Varianten lassen
Content-Lengthbewusst weg (das Response-Objekt kennt die Body-Größe nicht im Voraus). Fortschrittsbalken beim Download und manche Proxys bevorzugen eine bekannte Länge. Verwenden Sie das nicht gestreamtedownload()oderinline(), wenn das Dokument klein genug ist, um im Speicher gehalten zu werden, und eine Content-Länge wünschenswert ist. - Die gestreamten Varianten geben dieselben Security-Header und dasselbe
Cache-Control: private, max-age=0, must-revalidateaus wie die gepufferten Varianten.
Wählen Sie Streaming für mehrere Megabyte große Reports und Batch-Exporte. Wählen Sie die gepufferten Varianten für kleine, latenzkritische Responses.
Asynchrone Generierung im großen Maßstab
Abschnitt betitelt „Asynchrone Generierung im großen Maßstab“Lagern Sie die Generierung an Messenger aus, wenn Requests schnell beantwortet werden müssen oder wenn das Rendern CPU-intensiv ist.
- Implementieren Sie
PdfBuilderInterfacefür jeden Dokumenttyp. - Registrieren Sie Builder in einem
container.service_locatorund verdrahten Sie diesen als denGeneratePdfHandler-$builderLocator. - Routen Sie
GeneratePdfMessagean einen dauerhaften Transport. - Betreiben Sie Worker mit begrenzter Lebensdauer.
Begrenzte Worker-Lebensdauer
Abschnitt betitelt „Begrenzte Worker-Lebensdauer“Recyceln Sie Worker, damit eine nicht freigegebene Allokation in einer Drittanbieterabhängigkeit nicht unbegrenzt anwachsen kann:
php bin/console messenger:consume async \ --limit=200 \ --memory-limit=256M \ --time-limit=3600Die Konfigurationsschlüssel messenger.timeout und messenger.retries des Bundles legen das vorgesehene Timeout pro Nachricht und das Retry-Budget fest. Erzwingen Sie das passende Verhalten über die Retry-Strategie und die Worker-Flags von Symfony.
Sicherheit des Ausgabepfads in Workern
Abschnitt betitelt „Sicherheit des Ausgabepfads in Workern“GeneratePdfMessage validiert den Ausgabepfad bei der Konstruktion. Anschließend validiert GeneratePdfHandler ihn zur Ausführungszeit erneut, bevor auf die Festplatte geschrieben wird. Diese zweistufige Prüfung ist für asynchrone Arbeit wichtig. Eine Nachricht kann zwischen Dispatch und Konsum in einer Queue liegen, daher vertraut der Handler dem in der Queue liegenden Pfad nicht blind. Beschränken Sie die Dateisystemberechtigungen der Worker als Defense-in-Depth auf das vorgesehene Ausgabeverzeichnis.
Observability
Abschnitt betitelt „Observability“Die Services FontRegistry und ImageRegistry akzeptieren ein optionales Psr\Log\LoggerInterface (gebunden mit nullOnInvalid()). Wenn die Anwendung einen Logger bereitstellt, können die Registries Diagnosemeldungen ausgeben. Der Logger ist unter dem PSR-3-Logger-Contract (PSR-3) ein optionaler, austauschbarer Kollaborator. Für Sichtbarkeit auf Request-Ebene loggen Sie rund um PdfFactory::create() und den Messenger-Handler in Ihrem Anwendungscode. Verwenden Sie messenger:consume -vv während der Incident-Triage.
Deployment-Checkliste
Abschnitt betitelt „Deployment-Checkliste“- Fixieren Sie eine einzige
nextpdf/core-Major-Version in dercomposer.jsonder Anwendung (das Bundle akzeptiert^3.0 || ^5.2). - Stellen Sie sicher, dass
ext-mbstringundext-zlibim deployten PHP-Image aktiviert sind (andernfalls schlägt das Bundle beim Boot frühzeitig fehl). - Befüllen Sie
preload_fontsvorab mit den Fonts, die Ihre Dokumente verwenden, damit die Registry beim Boot statt beim ersten Request aufgewärmt und gesperrt wird. - Richten Sie
cache_pathauf einen beschreibbaren, persistenten Ort, wenn Sie sich auf gecachte Artefakte über Deployments hinweg verlassen. Andernfalls ist der Standard%kernel.cache_dir%in Ordnung. - Führen Sie
php bin/console cache:warmupbeim Deployment aus, damit der kompilierte Container (einschließlich der Probes für optionale Erweiterungen) vor dem Traffic aufgebaut wird. - Verwenden Sie für produktive asynchrone Arbeit einen dauerhaften Messenger-Transport (nicht
sync) und recyceln Sie Worker mit--limit/--memory-limit/--time-limit.
Sonderfälle und Stolperfallen
Abschnitt betitelt „Sonderfälle und Stolperfallen“- Gestreamte Responses hinter einem puffernden Proxy — ein Proxy, der den gesamten Body puffert, hebt den Speichervorteil auf. Konfigurieren Sie den Proxy so, dass er PDF-Responses streamt, oder verwenden Sie dort gepufferte Responses.
kernel.resetwird nicht berücksichtigt — in einer Laufzeit, diekernel.resetnicht aufruft, ist der Image-Cache durchimage_cache_mbbegrenzt, wird aber nicht zwischen den Requests geleert; dimensionieren Sie die Obergrenze entsprechend.- Ein Dokument über mehrere Requests hinweg halten — ein festgehaltenes
Documentaus einem vorherigen Request trägt veralteten Zustand mit sich. Lösen Sie es immer pro Request überPdfFactoryauf.
Konformität
Abschnitt betitelt „Konformität“Jede auf dieser Seite getroffene normative Aussage ist an eine vollständige 64-stellige hexadezimale reference_id aus dem zugangsbeschränkten SDO-Korpus gebunden. Die Provenienz, also das Korpus-Manifest und der Retrieval-Transport, befindet sich in _sidecars/rag-citations.yaml.
| Spec | Klausel | reference_id | Aussage |
|---|---|---|---|
| PSR-11 | psr_11_container#1.1.2.p3.b | Nicht geteilter Service: bei jeder Auflösung ein eigener Wert | |
| PSR-3 | psr_3_logger#x3.p17 | Optionaler Logger-Kollaborator |
Siehe auch
Abschnitt betitelt „Siehe auch“- /integrations/symfony/configuration/ — Servicelebenszyklus und Parameter.
- /integrations/symfony/security-and-operations/ — Response-Header, Pfadvalidierung, Schlüsselhandhabung.
- /integrations/symfony/troubleshooting/ — Boot- und Laufzeitdiagnostik.
- /integrations/symfony/quickstart/ — das minimale asynchrone Setup.