NextPDF Symfony en production
Le bundle cible les runtimes PHP à exécution longue. Les documents ne sont pas partagés, le registre de polices est verrouillé après le warmup et le cache d’images est réinitialisé entre les requêtes. Sers les gros PDF en streaming et délègue les tâches lourdes aux workers Messenger.
Cycle de vie des services compatible avec les workers
Section intitulée « Cycle de vie des services compatible avec les workers »Les runtimes à exécution longue gardent le conteneur en vie d’une requête à l’autre ; l’état propre à chaque requête ne doit donc pas fuiter. FrankenPHP, RoadRunner et les workers Messenger suivent tous ce modèle. Le services.php du bundle encode le cycle de vie ci-dessous, vérifié à partir des définitions de services :
- Document — non partagé.
nextpdf.document(et les aliasPdfDocumentInterface/Document) est résolu en une nouvelle instance à chaque appel. Sous PSR-11, un conteneur peut légitimement renvoyer une valeur différente à chaqueget()pour le même id (PSR-11 §1.1.2). Résous un document par requête. N’en conserve jamais un d’une requête à l’autre. - FontRegistry — partagé et verrouillé. Le registre est un singleton qui vit pendant toute la durée du processus. Après
warmup()(lorsquepreload_fontsn’est pas vide), la passe de compilation appellelock(). Ce verrou empêche toute mutation à l’exécution et donc toute pollution de l’état des polices entre requêtes. - ImageRegistry — partagé, réinitialisé par requête. Le cache d’images, borné et de type least-recently-used (LRU), est partagé, mais marqué
kernel.resetavec la méthodereset, si bien que Symfony le vide entre les requêtes sous les runtimes qui respectentkernel.reset. - Contrats EInvoice — non partagés. Lorsque les implémentations Premium sont présentes, les services embedder, validator, profile et schematron sont enregistrés comme non partagés. Le contexte du parseur, propre à chaque appel, ne fuit jamais d’une requête à l’autre.
Schéma d’injection recommandé
Section intitulée « Schéma d’injection recommandé »Injecte PdfFactory — un composant partagé et sans état qui porte la configuration — puis appelle create() par requête :
public function __construct(private readonly PdfFactory $pdf) {}
public function action(): Response{ $doc = $this->pdf->create(); // fresh, disposable // ... build ... return PdfResponse::inline($doc, 'document.pdf');}N’injecte pas Document ni nextpdf.document dans un service lui-même partagé et conservé d’une requête à l’autre. Résous-le plutôt à l’intérieur de la méthode, dans le périmètre de la requête.
Streaming de gros documents
Section intitulée « Streaming de gros documents »PdfResponse::streamDownload() et streamInline() renvoient un StreamedResponse. Son callback émet le corps du PDF par blocs de 64 Ko et vide le tampon après chaque bloc. La taille du tampon de réponse reste ainsi bornée pour les gros documents. Les deux compromis ci-dessous sont vérifiés par rapport à PdfResponse :
- Les variantes en streaming omettent intentionnellement
Content-Length(l’objet de réponse ne connaît pas la taille du corps à l’avance). Les barres de progression de téléchargement et certains proxys préfèrent une longueur connue. Utilise les variantes sans streamingdownload()ouinline()lorsque le document est assez petit pour tenir en mémoire et qu’une longueur de contenu est souhaitable. - Les variantes en streaming émettent les mêmes en-têtes de sécurité et le même
Cache-Control: private, max-age=0, must-revalidateque les variantes bufferisées.
Choisis le streaming pour les rapports de plusieurs mégaoctets et les exports par lots. Choisis les variantes bufferisées pour les réponses de petite taille et sensibles à la latence.
Génération asynchrone à grande échelle
Section intitulée « Génération asynchrone à grande échelle »Délègue la génération à Messenger lorsque les requêtes doivent répondre rapidement, ou lorsque le rendu consomme beaucoup de CPU.
- Implémente
PdfBuilderInterfacepour chaque type de document. - Enregistre les builders dans un
container.service_locatoret câble-le sur leGeneratePdfHandleren tant que$builderLocator. - Route
GeneratePdfMessagevers un transport durable. - Exécute les workers avec des durées de vie bornées.
Durée de vie bornée des workers
Section intitulée « Durée de vie bornée des workers »Recycle les workers afin qu’une allocation qui fuit dans une dépendance tierce ne puisse pas croître sans limite :
php bin/console messenger:consume async \ --limit=200 \ --memory-limit=256M \ --time-limit=3600Les clés de configuration messenger.timeout et messenger.retries du bundle enregistrent le délai d’expiration par message et le budget de réessais cibles. Applique le comportement correspondant via la stratégie de réessai de Symfony et les flags du worker.
Sécurité du chemin de sortie dans les workers
Section intitulée « Sécurité du chemin de sortie dans les workers »GeneratePdfMessage valide le chemin de sortie à la construction. Ensuite, GeneratePdfHandler le revalide au moment de l’exécution, avant d’écrire sur le disque. Cette vérification en deux étapes est importante pour le travail asynchrone. Un message peut rester en file d’attente entre l’envoi et la consommation ; le handler ne fait donc pas aveuglément confiance au chemin mis en file. Restreins les droits d’accès au système de fichiers du worker au répertoire de sortie prévu, en défense en profondeur.
Observabilité
Section intitulée « Observabilité »Les services FontRegistry et ImageRegistry acceptent un Psr\Log\LoggerInterface facultatif (lié avec nullOnInvalid()). Lorsque l’application fournit un logger, les registres peuvent émettre des diagnostics par son intermédiaire. Le logger est un collaborateur facultatif et interchangeable selon le contrat de logger PSR-3 (PSR-3). Pour obtenir une visibilité à l’échelle de la requête, journalise autour de PdfFactory::create() et du handler Messenger dans le code de ton application. Utilise messenger:consume -vv lors du diagnostic d’incident.
Checklist de déploiement
Section intitulée « Checklist de déploiement »- Épingle une seule version majeure de
nextpdf/coredans lecomposer.jsonde l’application (le bundle accepte^3.0 || ^5.2). - Assure-toi que
ext-mbstringetext-zlibsont activées dans l’image PHP déployée (sinon le bundle échoue tôt au démarrage). - Pré-remplis
preload_fontspour les polices qu’utilisent tes documents, afin que le registre soit préchauffé et verrouillé au démarrage plutôt qu’à la première requête. - Pointe
cache_pathvers un emplacement persistant et accessible en écriture si tu t’appuies sur des artefacts mis en cache d’un déploiement à l’autre. Sinon, la valeur par défaut%kernel.cache_dir%convient. - Lance
php bin/console cache:warmuppendant le déploiement afin que le conteneur compilé (y compris les sondes d’extensions optionnelles) soit construit avant le trafic. - Utilise un transport Messenger durable (pas
sync) pour le travail asynchrone en production, et recycle les workers avec--limit/--memory-limit/--time-limit.
Cas limites et pièges
Section intitulée « Cas limites et pièges »- Réponses en streaming derrière un proxy à mise en tampon — un proxy qui bufferise tout le corps annule le gain de mémoire. Configure le proxy pour diffuser les réponses PDF en streaming, ou utilise des réponses bufferisées dans ce cas.
kernel.resetnon honoré — sous un runtime qui n’appelle paskernel.reset, le cache d’images est borné parimage_cache_mbmais non vidé entre les requêtes ; dimensionne le plafond en conséquence.- Conserver un document d’une requête à l’autre — un
Documentcapturé lors d’une requête précédente portera un état périmé. Résous-le toujours par requête viaPdfFactory.
Conformité
Section intitulée « Conformité »Chaque ligne correspond à une affirmation normative formulée sur cette page, rattachée à un reference_id complet de 64 caractères hexadécimaux, issu du corpus SDO sous accès contrôlé. La provenance, c’est-à-dire le manifeste du corpus et le transport de récupération, se trouve dans _sidecars/rag-citations.yaml.
| Spec | Clause | reference_id | Affirmation |
|---|---|---|---|
| PSR-11 | psr_11_container#1.1.2.p3.b | Service non partagé : valeur distincte à chaque résolution | |
| PSR-3 | psr_3_logger#x3.p17 | Collaborateur logger optionnel |
Voir aussi
Section intitulée « Voir aussi »- /integrations/symfony/configuration/ — cycle de vie des services et paramètres.
- /integrations/symfony/security-and-operations/ — en-têtes de réponse, validation de chemin, gestion des clés.
- /integrations/symfony/troubleshooting/ — diagnostics de démarrage et d’exécution.
- /integrations/symfony/quickstart/ — la configuration asynchrone minimale.