Ejecutar compat-legacy en producción
De un vistazo
Sección titulada «De un vistazo»El adaptador se puede ejecutar de forma segura en manejadores HTTP, procesos de cola y procesos de larga duración. Es más seguro que el TCPDF heredado 6.2.13 porque elimina los dos riesgos de producción más habituales: la salida directa al búfer y die() ante un error. Esta página explica cómo usarlo correctamente en producción.
Requisito previo: completar la auditoría de modo estricto en /integrations/tcpdf-compat/migration/ y desplegar con el modo estricto desactivado.
Gestión de la salida en procesos y manejadores
Sección titulada «Gestión de la salida en procesos y manejadores»El método Output() del TCPDF heredado escribe directamente en el búfer de salida activo. Eso corrompe las respuestas en los frameworks HTTP y rompe los procesos de cola. En cambio, el adaptador redirige la salida a través de un puente de destino seguro.
Usar el destino que corresponda al llamador:
| Contexto | Destino | Por qué |
|---|---|---|
| Proceso de cola que escribe en el almacenamiento | Output($path, 'F') | Escribe el archivo y devuelve una cadena vacía, sin interactuar con el búfer. |
| Generar y luego adjuntar/subir | Output($name, 'S') | Devuelve los bytes del PDF; la aplicación controla su destino. |
| Adjunto de correo electrónico | Output($name, 'E') | Devuelve un cuerpo MIME en base64 con Content-Type: application/pdf. |
| Respuesta HTTP controlada por la aplicación | Output($name, 'S') | Obtener los bytes y después definir sus propias cabeceras y cuerpo. |
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\Exception\TcpdfNotImplementedException;use NextPDF\Compat\Tcpdf\TCPDF;
/** * Render an invoice in a queue worker. Returns the storage path. * * @throws \RuntimeException on a render failure (Error() throws, not die()). */function renderInvoiceJob(array $invoice, string $storageDir): string{ $pdf = new TCPDF('P', 'mm', 'A4'); $pdf->SetFont('helvetica', '', 12); $pdf->AddPage(); $pdf->Cell(0, 10, 'Invoice ' . $invoice['number'], 0, 1);
$path = $storageDir . '/invoice-' . $invoice['number'] . '.pdf';
try { $pdf->Output($path, 'F'); // writes file, no buffer pollution } catch (TcpdfNotImplementedException $e) { // Only reachable if strict mode is on — it must NOT be in production. throw new \RuntimeException('Adapter strict-mode gap in production: ' . $e->getMessage(), 0, $e); } catch (\RuntimeException $e) { // Error() throws RuntimeException instead of die(). throw new \RuntimeException('PDF render failed: ' . $e->getMessage(), 0, $e); }
return $path;}En un manejador HTTP, conviene preferir 'S' y definir las cabeceras directamente:
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\TCPDF;
$pdf = new TCPDF();$pdf->AddPage();$pdf->SetFont('helvetica', '', 12);$pdf->Cell(0, 10, 'Report');
$bytes = $pdf->Output('report.pdf', 'S');
header('Content-Type: application/pdf');header('Content-Length: ' . strlen($bytes));header('Content-Disposition: inline; filename="report.pdf"');echo $bytes;Gestión de errores
Sección titulada «Gestión de errores»Error() lanza RuntimeException; nunca llama a die(). Es el cambio operativo más importante respecto al TCPDF heredado.
- Envolver cada punto de entrada de renderizado en
try/catch. - Asignar la excepción al contrato de errores de la aplicación (HTTP 5xx, trabajo fallido, reintento, dead-letter).
- No suponer que el proceso termina ante un fallo de renderizado — no lo hace.
Si se ejecuta un trabajo de CI periódico en modo estricto (recomendado), una TcpdfNotImplementedException allí es un hallazgo real: una ruta de código depende de un parámetro de TCPDF no admitido. Debe tratarse como una tarea de migración, no como una prueba inestable.
Ciclo de vida y gestión de recursos
Sección titulada «Ciclo de vida y gestión de recursos»- Los bytes del PDF se construyen de forma diferida en la primera llamada de salida.
Close()es opcional; llamarlo almacena los bytes en caché.Open()es una operación nula segura. endPage()no hace nada — NextPDF gestiona el ciclo de vida de las páginas. Conviene quitarlo de los bucles intensivos; no aporta ningún valor.- Dejar que PHP recolecte la basura del adaptador entre trabajos.
_destroy()restablece los datos en caché del adaptador, pero no es necesario llamarlo explícitamente en los bucles de procesos habituales. - Construir un adaptador nuevo por documento. No reutilice una misma instancia del adaptador entre documentos no relacionados en un proceso de larga duración; el estado del documento es por instancia.
Recomendaciones de rendimiento
Sección titulada «Recomendaciones de rendimiento»- El adaptador es una capa de delegación ligera; el coste está dominado por el motor, no por el adaptador.
- Definir las constantes heredadas una sola vez en el arranque.
LegacyDefaults::register()yLegacyBootstrap::enableAliases()son idempotentes y están protegidos, por lo que las llamadas repetidas son económicas. Definir las constantes en cada solicitud es trabajo desperdiciado. - Preferir
Output(..., 'S')o'F'en lugar de'I'/'D'en contextos sin navegador. Las rutas inline/download producen una salida independiente del framework que normalmente no se desea en un proceso. - Para la generación de gran volumen, perfilar el motor, no el adaptador. El presupuesto de sobrecarga propia del adaptador por página es pequeño en comparación con el renderizado.
Concurrencia
Sección titulada «Concurrencia»- Cada instancia del adaptador es independiente y mantiene su propio estado de documento. La concurrencia a nivel de proceso o de worker es segura siempre que cada unidad de trabajo use su propia instancia del adaptador.
- Las protecciones de idempotencia de
LegacyBootstrapyLegacyDefaultsusan estado estático local al proceso; son seguras bajo los modelos típicos de PHP per-request/per-worker. No están diseñadas para compartir estado mutable entre hilos.
Lista de comprobación previa a producción
Sección titulada «Lista de comprobación previa a producción»- Auditoría de modo estricto completada; el entorno de producción se ejecuta con el modo estricto desactivado.
- Todos los puntos de entrada de renderizado están envueltos en
try/catchparaRuntimeException(sin depender dedie()). - Los workers usan
Output(..., 'F')o'S', nunca la ruta inline. - Constantes heredadas definidas una sola vez en el arranque, antes de la primera construcción.
- Existe un trabajo de CI periódico en modo estricto para detectar regresiones.
- Aserciones de prueba a nivel de byte recalibradas (consulte /integrations/tcpdf-compat/migration/).
Véase también
Sección titulada «Véase también»- /integrations/tcpdf-compat/security-and-operations/ — cifrado, postura de firma, endurecimiento
- /integrations/tcpdf-compat/troubleshooting/ — patrones de fallo en producción y soluciones
- /integrations/tcpdf-compat/configuration/ — modo estricto e higiene de constantes
- /integrations/tcpdf-compat/migration/ — auditoría que debe preceder a la producción