NextPDF Symfony en producción
Vista general
Sección titulada «Vista general»El bundle está diseñado para runtimes de PHP de larga duración. Los documentos no se comparten, el registro de fuentes queda bloqueado tras el precalentamiento y la caché de imágenes se reinicia entre solicitudes. Usar streaming para los PDF de gran tamaño y delegar los trabajos pesados en los workers de Messenger.
Ciclo de vida seguro de los servicios para workers
Sección titulada «Ciclo de vida seguro de los servicios para workers»Los runtimes de larga duración mantienen vivo el contenedor entre solicitudes, por lo que el estado por solicitud no debe filtrarse. FrankenPHP, RoadRunner y los workers de Messenger funcionan de esta manera. El services.php del bundle codifica el ciclo de vida descrito a continuación, verificado contra las definiciones de servicio:
- Document: no compartido.
nextpdf.document(y los aliasPdfDocumentInterface/Document) se resuelve como una instancia nueva cada vez. Según PSR-11, un contenedor puede devolver legítimamente un valor distinto en cadaget()para el mismo id (PSR-11 §1.1.2). Resolver un documento por solicitud. No conservarlo nunca entre solicitudes. - FontRegistry: compartido y bloqueado. El registro es un singleton que vive durante todo el proceso. Tras
warmup()(cuandopreload_fontsno está vacío), el compiler pass llama alock(). El bloqueo impide mutaciones en tiempo de ejecución y, por tanto, la contaminación del estado de las fuentes entre solicitudes. - ImageRegistry: compartido, reiniciado por solicitud. La caché LRU de imágenes se comparte, pero está etiquetada con
kernel.resetcon el métodoreset, de modo que Symfony la limpia entre solicitudes en los runtimes que respetankernel.reset. - Contratos de EInvoice: no compartidos. Cuando hay implementaciones de Premium presentes, los servicios de embedder, validador, perfil y schematron se registran como no compartidos. El contexto del parser de cada llamada nunca se filtra entre solicitudes.
Patrón de inyección recomendado
Sección titulada «Patrón de inyección recomendado»Inyectar PdfFactory —un contenedor de configuración compartido y sin estado— y llamar a create() por solicitud:
public function __construct(private readonly PdfFactory $pdf) {}
public function action(): Response{ $doc = $this->pdf->create(); // fresh, disposable // ... build ... return PdfResponse::inline($doc, 'document.pdf');}No inyectar Document ni nextpdf.document en un servicio que sea compartido y se conserve entre solicitudes. En su lugar, resolverlo dentro del método con alcance de solicitud.
Streaming de documentos grandes
Sección titulada «Streaming de documentos grandes»PdfResponse::streamDownload() y streamInline() devuelven un StreamedResponse. Su callback emite el cuerpo del PDF en bloques de 64 KB y vacía el búfer después de cada bloque. Esto acota el búfer de respuesta para documentos de gran tamaño. Las dos salvedades descritas a continuación están verificadas contra PdfResponse:
- Las variantes con streaming omiten
Content-Lengthde forma intencionada (el objeto de respuesta no conoce de antemano el tamaño del cuerpo). Las barras de progreso de descarga y algunos proxies prefieren una longitud conocida. Usar los métodos sin streamingdownload()oinline()cuando el documento sea lo bastante pequeño como para mantenerlo en memoria y convenga una longitud de contenido. - Las variantes con streaming emiten las mismas cabeceras de seguridad y el mismo
Cache-Control: private, max-age=0, must-revalidateque las variantes con búfer.
Elegir streaming para informes de varios megabytes y exportaciones por lotes. Elegir las variantes con búfer para respuestas pequeñas y sensibles a la latencia.
Generación asíncrona a escala
Sección titulada «Generación asíncrona a escala»Delegar la generación en Messenger cuando las solicitudes deban responder rápido o cuando el renderizado consuma mucha CPU.
- Implementar
PdfBuilderInterfacepara cada tipo de documento. - Registrar los builders en un
container.service_locatory conectarlo enGeneratePdfHandlercomo su$builderLocator. - Enrutar
GeneratePdfMessagea un transporte duradero. - Ejecutar los workers con tiempos de vida acotados.
Tiempo de vida acotado de los workers
Sección titulada «Tiempo de vida acotado de los workers»Reciclar los workers para que una fuga de memoria en una dependencia de terceros no pueda crecer sin límite:
php bin/console messenger:consume async \ --limit=200 \ --memory-limit=256M \ --time-limit=3600Las claves de configuración messenger.timeout y messenger.retries del bundle registran el tiempo de espera por mensaje y el presupuesto de reintentos previstos. Aplicar el comportamiento correspondiente mediante la estrategia de reintentos de Symfony y los flags del worker.
Seguridad de la ruta de salida en los workers
Sección titulada «Seguridad de la ruta de salida en los workers»GeneratePdfMessage valida la ruta de salida al construirse. Después, GeneratePdfHandler la revalida durante la ejecución, antes de escribir en disco. Esta comprobación en dos etapas es importante para el trabajo asíncrono. Un mensaje puede permanecer en una cola entre el envío y el consumo, por lo que el handler no confía a ciegas en la ruta encolada. Restringir los permisos del sistema de archivos del worker al directorio de salida previsto como defensa en profundidad.
Observabilidad
Sección titulada «Observabilidad»Los servicios FontRegistry e ImageRegistry aceptan un Psr\Log\LoggerInterface opcional (vinculado mediante nullOnInvalid()). Cuando la aplicación proporciona un logger, esos registros pueden emitir diagnósticos a través de él. El logger es un colaborador opcional e intercambiable según el contrato de logger de PSR-3 (PSR-3). Para obtener visibilidad a nivel de solicitud, añadir logging alrededor de PdfFactory::create() y del handler de Messenger en el código de la aplicación. Usar messenger:consume -vv durante el triaje de incidentes.
Lista de verificación de despliegue
Sección titulada «Lista de verificación de despliegue»- Fijar un único major de
nextpdf/coreen elcomposer.jsonde la aplicación (el bundle acepta^3.0 || ^5.2). - Asegurarse de que
ext-mbstringyext-zlibestén habilitadas en la imagen de PHP desplegada (de lo contrario, el bundle falla de inmediato al arrancar). - Rellenar de antemano
preload_fontscon las fuentes que usan los documentos para que el registro se caliente y se bloquee al arrancar en lugar de en la primera solicitud. - Apuntar
cache_patha una ubicación persistente y con permisos de escritura si se depende de artefactos en caché entre despliegues. De lo contrario, el valor predeterminado%kernel.cache_dir%es suficiente. - Ejecutar
php bin/console cache:warmupdurante el despliegue para que el contenedor compilado (incluidas las sondas de extensiones opcionales) se construya antes de que llegue el tráfico. - Usar un transporte de Messenger duradero (no
sync) para el trabajo asíncrono en producción, y reciclar los workers con--limit/--memory-limit/--time-limit.
Casos límite y consejos
Sección titulada «Casos límite y consejos»- Respuestas con streaming detrás de un proxy con búfer: un proxy que almacena en búfer el cuerpo completo anula el beneficio de memoria. Configurar el proxy para que transmita por streaming las respuestas PDF, o usar respuestas con búfer en ese caso.
kernel.resetno respetado: en un runtime que no llama akernel.reset, la caché de imágenes está acotada porimage_cache_mbpero no se limpia entre solicitudes; dimensionar el límite en consecuencia.- Retener un documento entre solicitudes: un
Documentcapturado de una solicitud anterior conservará estado obsoleto. Resolver siempre por solicitud mediantePdfFactory.
Conformidad
Sección titulada «Conformidad»Cada fila contiene una afirmación normativa hecha en esta página, anclada a un reference_id completo de 64 dígitos hexadecimales del corpus SDO con publish gate. La procedencia, es decir, el manifiesto del corpus y el transporte de recuperación, está en _sidecars/rag-citations.yaml.
| Especificación | Cláusula | reference_id | Afirmación |
|---|---|---|---|
| PSR-11 | psr_11_container#1.1.2.p3.b | Servicio no compartido: valor distinto por resolución | |
| PSR-3 | psr_3_logger#x3.p17 | Colaborador logger opcional |
Véase también
Sección titulada «Véase también»- /integrations/symfony/configuration/: ciclo de vida de los servicios y parámetros.
- /integrations/symfony/security-and-operations/: cabeceras de respuesta, validación de rutas, gestión de claves.
- /integrations/symfony/troubleshooting/: diagnósticos de arranque y en tiempo de ejecución.
- /integrations/symfony/quickstart/: configuración asíncrona mínima.