NextPDF Symfony の本番環境での利用
このバンドルは、長時間稼働する PHP ランタイムを想定しています。ドキュメントは非共有で、フォントレジストリはウォームアップ後にロックされ、画像キャッシュはリクエスト間でリセットされます。大容量の PDF はストリーミングし、負荷の高いジョブは Messenger ワーカーにオフロードします。
ワーカーセーフなサービスライフサイクル
「ワーカーセーフなサービスライフサイクル」という見出しのセクション長時間稼働するランタイムは、リクエストをまたいでコンテナを保持するため、リクエストごとの状態が漏れてはなりません。FrankenPHP、RoadRunner、Messenger ワーカーはいずれもこの方式で動作します。バンドルの services.php は以下のライフサイクルを定義しており、サービス定義と照合済みです。
- Document — 非共有。
nextpdf.document(およびPdfDocumentInterface/Documentのエイリアス)は、毎回新しいインスタンスとして resolve(解決)されます。PSR-11 では、同一の ID に対してget()を呼ぶたびに、コンテナが異なる値を返すことは正当な動作として許容されています(PSR-11 §1.1.2)。ドキュメントはリクエストごとに解決してください。リクエストをまたいで保持しないでください。 - FontRegistry — 共有かつロック済み。 このレジストリはプロセスの存続期間を通じてシングルトンです。
warmup()の後(preload_fontsが空でない場合)、コンパイラーパスがlock()を呼び出します。このロックは実行時の変更を防ぎ、結果としてリクエストをまたぐフォント状態の汚染を防ぎます。 - ImageRegistry — 共有、リクエストごとにリセット。 上限付きの LRU(最近最も使われていないもの)方式の画像キャッシュは共有されますが、
kernel.reset(メソッドreset)でタグ付けされているため、Symfony はkernel.resetを尊重するランタイムではリクエスト間でこれをクリアします。 - EInvoice の契約 — 非共有。 Premium 実装がある場合、embedder、validator、profile、schematron の各サービスは非共有として登録されます。呼び出しごとのパーサーコンテキストがリクエストをまたいで漏れることはありません。
推奨されるインジェクションパターン
「推奨されるインジェクションパターン」という見出しのセクション設定を保持する、共有かつステートレスなホルダーである PdfFactory をインジェクトし、リクエストごとに create() を呼び出します。
public function __construct(private readonly PdfFactory $pdf) {}
public function action(): Response{ $doc = $this->pdf->create(); // fresh, disposable // ... build ... return PdfResponse::inline($doc, 'document.pdf');}それ自体が共有され、リクエストをまたいで保持されるサービスに Document や nextpdf.document をインジェクトしないでください。代わりに、リクエストスコープのメソッド内で解決してください。
大容量ドキュメントのストリーミング
「大容量ドキュメントのストリーミング」という見出しのセクションPdfResponse::streamDownload() と streamInline() は StreamedResponse を返します。そのコールバックは PDF 本文を 64 KB のチャンク単位で出力し、各チャンクの後にフラッシュします。これにより、大容量ドキュメントに対するレスポンスバッファーを上限内に抑えられます。以下の両方のトレードオフは、PdfResponse と照合済みです。
- ストリーミング版は意図的に
Content-Lengthを省略します(レスポンスオブジェクトは本文サイズを事前に把握していません)。ダウンロードのプログレスバーや一部のプロキシでは、既知の長さがあるほうが適しています。ドキュメントがメモリに保持できるほど小さく、かつコンテンツ長が望ましい場合は、ストリーミングしないdownload()またはinline()を使用してください。 - ストリーミング版は、バッファリング版と同じセキュリティヘッダーおよび同じ
Cache-Control: private, max-age=0, must-revalidateを出力します。
数メガバイト規模のレポートやバッチエクスポートにはストリーミングを選択してください。サイズが小さく、レイテンシーが重視されるレスポンスにはバッファリング版を選択してください。
大規模での非同期生成
「大規模での非同期生成」という見出しのセクションリクエストを速やかに返す必要がある場合や、レンダリングの CPU 負荷が高い場合は、生成を Messenger にオフロードしてください。
- ドキュメントタイプごとに
PdfBuilderInterfaceを実装します。 - ビルダーを
container.service_locatorに登録し、それをGeneratePdfHandlerの$builderLocatorとしてワイヤリングします。 - 次に、
GeneratePdfMessageを永続的なトランスポートにルーティングします。 - ワーカーは存続期間に上限を設けて実行します。
ワーカー存続期間の上限設定
「ワーカー存続期間の上限設定」という見出しのセクションサードパーティの依存関係でリークしたアロケーションが際限なく増大しないように、ワーカーをリサイクルします。
php bin/console messenger:consume async \ --limit=200 \ --memory-limit=256M \ --time-limit=3600バンドルの messenger.timeout および messenger.retries 設定キーは、メッセージごとに想定されるタイムアウトとリトライ予算を記録します。対応する動作は、Symfony のリトライ戦略とワーカーフラグを通じて適用してください。
ワーカーにおける出力パスの安全性
「ワーカーにおける出力パスの安全性」という見出しのセクションGeneratePdfMessage は、構築時に出力パスを検証します。その後、GeneratePdfHandler が実行時に、ディスクへ書き込む前にこれを再検証します。この 2 段階のチェックは、非同期処理において重要です。メッセージはディスパッチから消費までの間キューに留まる可能性があるため、ハンドラーはキューに入れられたパスを無条件には信頼しません。多層防御として、ワーカーのファイルシステム権限を意図した出力ディレクトリに限定してください。
オブザーバビリティ
「オブザーバビリティ」という見出しのセクション本バンドルの FontRegistry および ImageRegistry サービスは、オプションの Psr\Log\LoggerInterface(nullOnInvalid() でバインド)を受け付けます。アプリケーションがロガーを提供する場合、各レジストリはそれを通じて診断情報を出力できます。ロガーは、PSR-3 ロガーコントラクト(PSR-3)における、オプションで差し替え可能なコラボレーターです。リクエストレベルの可視性を確保するには、アプリケーションコード内で PdfFactory::create() と Messenger ハンドラーの前後でログを記録してください。インシデントのトリアージ時には messenger:consume -vv を使用してください。
デプロイチェックリスト
「デプロイチェックリスト」という見出しのセクション- 依存パッケージ
nextpdf/coreのメジャーバージョンを、アプリケーションのcomposer.jsonで 1 つに固定してください (バンドルは^3.0 || ^5.2を受け付けます)。 - デプロイする PHP イメージで
ext-mbstringとext-zlibが有効になっていることを確認してください(そうでない場合、バンドルは起動時に即座に失敗します)。 - ドキュメントで使用するフォントを
preload_fontsに事前に設定しておくと、レジストリは最初のリクエスト時ではなく起動時にウォームアップしてロックされます。 - デプロイをまたいでキャッシュされたアーティファクトに依存する場合は、
cache_pathを書き込み可能で永続的な場所に指定してください。そうでない場合は、%kernel.cache_dir%のデフォルトで問題ありません。 - デプロイ時に
php bin/console cache:warmupを実行し、コンパイル済みコンテナ(オプション拡張機能のプローブを含む)をトラフィックを受ける前に構築しておきます。 - 本番環境の非同期処理には永続的な Messenger トランスポート(
sync以外)を使用し、--limit/--memory-limit/--time-limitでワーカーをリサイクルしてください。
エッジケースと注意点
「エッジケースと注意点」という見出しのセクション- バッファリングするプロキシの背後でのストリーミングレスポンス — 本文全体をバッファリングするプロキシは、メモリ上の利点を打ち消します。PDF レスポンスをストリーミングするようプロキシを設定するか、その箇所ではバッファリングレスポンスを使用してください。
kernel.resetが尊重されない場合 —kernel.resetを呼び出さないランタイムでは、画像キャッシュはimage_cache_mbによって上限内に抑えられますが、リクエスト間でクリアされません。その前提で上限値を設定してください。- リクエストをまたいでドキュメントを保持する場合 — 以前のリクエストから持ち越された
Documentは、古い状態を引き継ぎます。常にPdfFactoryを介してリクエストごとに解決してください。
各行は、このページで示される規範的な主張を表し、ゲート付き SDO コーパス由来の完全な 64 桁 16 進数の reference_id に紐付けられています。プロベナンス(コーパスマニフェストと取得トランスポート)は、_sidecars/rag-citations.yaml にあります。
| 仕様 | 条項 | リファレンス ID | 主張 |
|---|---|---|---|
| PSR-11 | psr_11_container#1.1.2.p3.b | 非共有サービス: 解決ごとに異なる値 | |
| PSR-3 | psr_3_logger#x3.p17 | オプションのロガーコラボレーター |
- /integrations/symfony/configuration/ — サービスライフサイクルとパラメーター。
- /integrations/symfony/security-and-operations/ — レスポンスヘッダー、パス検証、鍵の取り扱い。
- /integrations/symfony/troubleshooting/ — 起動時および実行時の診断。
- /integrations/symfony/quickstart/ — 最小限の非同期セットアップ。