コンテンツにスキップ

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');
}

それ自体が共有され、リクエストをまたいで保持されるサービスに Documentnextpdf.document をインジェクトしないでください。代わりに、リクエストスコープのメソッド内で解決してください。

PdfResponse::streamDownload()streamInline()StreamedResponse を返します。そのコールバックは PDF 本文を 64 KB のチャンク単位で出力し、各チャンクの後にフラッシュします。これにより、大容量ドキュメントに対するレスポンスバッファーを上限内に抑えられます。以下の両方のトレードオフは、PdfResponse と照合済みです。

  • ストリーミング版は意図的に Content-Length を省略します(レスポンスオブジェクトは本文サイズを事前に把握していません)。ダウンロードのプログレスバーや一部のプロキシでは、既知の長さがあるほうが適しています。ドキュメントがメモリに保持できるほど小さく、かつコンテンツ長が望ましい場合は、ストリーミングしない download() または inline() を使用してください。
  • ストリーミング版は、バッファリング版と同じセキュリティヘッダーおよび同じ Cache-Control: private, max-age=0, must-revalidate を出力します。

数メガバイト規模のレポートやバッチエクスポートにはストリーミングを選択してください。サイズが小さく、レイテンシーが重視されるレスポンスにはバッファリング版を選択してください。

リクエストを速やかに返す必要がある場合や、レンダリングの CPU 負荷が高い場合は、生成を Messenger にオフロードしてください。

  1. ドキュメントタイプごとに PdfBuilderInterface を実装します。
  2. ビルダーを container.service_locator に登録し、それを GeneratePdfHandler$builderLocator としてワイヤリングします。
  3. 次に、GeneratePdfMessage を永続的なトランスポートにルーティングします。
  4. ワーカーは存続期間に上限を設けて実行します。

サードパーティの依存関係でリークしたアロケーションが際限なく増大しないように、ワーカーをリサイクルします。

Terminal window
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\LoggerInterfacenullOnInvalid() でバインド)を受け付けます。アプリケーションがロガーを提供する場合、各レジストリはそれを通じて診断情報を出力できます。ロガーは、PSR-3 ロガーコントラクト(PSR-3)における、オプションで差し替え可能なコラボレーターです。リクエストレベルの可視性を確保するには、アプリケーションコード内で PdfFactory::create() と Messenger ハンドラーの前後でログを記録してください。インシデントのトリアージ時には messenger:consume -vv を使用してください。

  • 依存パッケージ nextpdf/core のメジャーバージョンを、アプリケーションの composer.json で 1 つに固定してください (バンドルは ^3.0 || ^5.2 を受け付けます)。
  • デプロイする PHP イメージで ext-mbstringext-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-11psr_11_container#1.1.2.p3.b非共有サービス: 解決ごとに異なる値
PSR-3psr_3_logger#x3.p17オプションのロガーコラボレーター
  • /integrations/symfony/configuration/ — サービスライフサイクルとパラメーター。
  • /integrations/symfony/security-and-operations/ — レスポンスヘッダー、パス検証、鍵の取り扱い。
  • /integrations/symfony/troubleshooting/ — 起動時および実行時の診断。
  • /integrations/symfony/quickstart/ — 最小限の非同期セットアップ。