Run compat-legacy in production
At a glance
Section titled “At a glance”The adapter is safe to run in Hypertext Transfer Protocol (HTTP)
handlers, queue workers, and long-running processes. It is safer than
legacy TCPDF 6.2.13 because it removes the two production hazards you
are most likely to hit: direct output to the buffer, and die() on
error. Use this page to operate it correctly.
Before production, complete the strict-mode audit in /integrations/tcpdf-compat/migration/ and deploy with strict mode off.
Output handling in workers and handlers
Section titled “Output handling in workers and handlers”Legacy TCPDF Output() writes directly to the active output buffer.
That can corrupt responses in HTTP frameworks and break queue workers.
The adapter routes output through a safe destination bridge instead.
Choose the destination that matches the caller:
| Context | Destination | Why |
|---|---|---|
| Queue worker writing to storage | Output($path, 'F') | Writes the file and returns an empty string. It does not interact with the buffer. |
| Generate then attach/upload | Output($name, 'S') | Returns the Portable Document Format (PDF) bytes; you control where they go. |
| Email attachment | Output($name, 'E') | Returns a base64 Multipurpose Internet Mail Extensions (MIME) body with Content-Type: application/pdf. |
| HTTP response you control | Output($name, 'S') | Gets bytes, then you set your own headers and body. |
<?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;}In an HTTP handler, prefer 'S' and set headers yourself:
<?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;Failure handling
Section titled “Failure handling”Error() throws RuntimeException; it never calls die(). This is the
most important operational change from legacy TCPDF.
- Wrap every render entry point in
try/catch. - Map the exception to your application’s error contract, such as HTTP 5xx, failed job, retry, or dead-letter.
- Do not assume the process ends on a render failure; it does not.
A TcpdfNotImplementedException in a periodic strict-mode continuous
integration (CI) job (recommended) is a real finding. It means a code
path relies on an unsupported TCPDF parameter. Treat it as a migration
work item, not a flaky test.
Lifecycle and resource handling
Section titled “Lifecycle and resource handling”- The document builds its PDF bytes lazily on the first output call.
Close()is optional; calling it caches the bytes.Open()is a safe no-op. endPage()does nothing because NextPDF manages page lifecycle. Remove it from hot loops; it adds no value.- Let PHP garbage-collect the adapter between jobs.
_destroy()resets the adapter’s cached data, but you do not need to call it explicitly in normal worker loops. - Construct a fresh adapter per document. Do not reuse one adapter instance across unrelated documents in a long-running worker; the document state is per-instance.
Performance guidance
Section titled “Performance guidance”- The adapter is a thin delegation layer; the engine dominates the cost, not the adapter.
- Define legacy constants once at boot.
LegacyDefaults::register()andLegacyBootstrap::enableAliases()are idempotent and guarded, so repeated calls are cheap. Defining constants per request wastes work. - Prefer
Output(..., 'S')or'F'over'I'/'D'in non-browser contexts. The inline/download paths produce framework-agnostic output that you usually do not want in a worker. - For high-volume generation, profile the engine, not the adapter. The per-page budget for the adapter’s own overhead is small relative to rendering.
Concurrency
Section titled “Concurrency”- Each adapter instance is independent and holds its own document state. Concurrency at the process or worker level is safe when each unit of work uses its own adapter instance.
- The idempotency guards in
LegacyBootstrapandLegacyDefaultsuse process-local static state; they are safe under typical PHP per-request/per-worker models. They are not designed for sharing mutable state across threads.
Pre-production checklist
Section titled “Pre-production checklist”- Strict-mode audit complete; production runs with strict mode off.
- All render entry points wrapped in
try/catchforRuntimeException(no reliance ondie()). - Workers use
Output(..., 'F')or'S', never the inline path. - Legacy constants defined once at boot, before first construction.
- A periodic strict-mode CI job is in place to catch regressions.
- Byte-level test assertions are re-baselined (see /integrations/tcpdf-compat/migration/).
See also
Section titled “See also”- /integrations/tcpdf-compat/security-and-operations/ — encryption, signing posture, and hardening
- /integrations/tcpdf-compat/troubleshooting/ — production failure patterns and fixes
- /integrations/tcpdf-compat/configuration/ — strict mode and constant hygiene
- /integrations/tcpdf-compat/migration/ — the audit that must precede production