跳到內容

在正式環境中執行 compat-legacy

這個轉接器可安全用於 HTTP handler、佇列工作程序與長時間執行的程序。它比舊版 TCPDF 6.2.13 更安全,因為已移除兩個最常見的正式環境風險:直接輸出到緩衝區,以及發生錯誤時 die()。本頁說明如何正確操作它。

先決條件:先在 /integrations/tcpdf-compat/migration/ 中完成嚴格模式稽核,部署時則將嚴格模式關閉

舊版 TCPDF Output() 會直接輸出到作用中的輸出緩衝區。這會破壞 HTTP 框架的回應,也會讓佇列工作程序失效。此轉接器則改由安全的目的地橋接器導向輸出。

請依呼叫端使用相符的目的地:

情境目的地原因
寫入儲存空間的佇列工作程序Output($path, 'F')寫入檔案;回傳空字串。不會與緩衝區互動。
產生後再 attach/uploadOutput($name, 'S')回傳 PDF 位元組;由你決定它們的去向。
電子郵件附件Output($name, 'E')回傳包含 Content-Type: application/pdf 的 base64 MIME 主體。
由你控制的 HTTP 回應Output($name, 'S')取得位元組後,再設定你自己的標頭與主體。
examples/production-worker.php
<?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;
}

HTTP handler 建議優先使用 'S',並自行設定標頭:

examples/production-http.php
<?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;

Error() 會擲回 RuntimeException;它絕不會呼叫 die()。相較於舊版 TCPDF,這是最大的單一營運變動。

  • 將每個算繪進入點都包在 try/catch 中。
  • 將例外對應至你應用程式的錯誤合約(HTTP 5xx、工作失敗、重試、無法投遞佇列)。
  • 不要假設程序會在算繪失敗時結束;它不會。

若你定期執行嚴格模式 CI 工作(建議如此),其中出現的 TcpdfNotImplementedException 是實際發現:某條程式碼路徑依賴不支援的 TCPDF 參數。請將它視為移轉工作項目,而不是不穩定的測試。

  • 文件會等到第一次輸出呼叫時,才延遲建構其 PDF 位元組。Close() 為選用;呼叫它會快取位元組。Open() 是安全的無操作。
  • endPage() 不會執行任何動作;NextPDF 會管理頁面生命週期。請從高頻迴圈中移除它;它沒有任何作用。
  • 讓 PHP 在不同工作之間回收該轉接器。_destroy() 會重設該轉接器的快取資料,但在正常的工作程序迴圈中,你不需要明確呼叫它。
  • 請為每份文件建構一個全新的轉接器。不要在長時間執行的工作程序中,於不相關文件之間重複使用同一個轉接器實例;文件狀態是以實例為單位的。
  • 此轉接器是輕量的委派層;效能成本主要取決於引擎,而非轉接器。
  • 在啟動時定義一次舊版常數。LegacyDefaults::register()LegacyBootstrap::enableAliases() 兩者都是冪等且受保護的,因此重複呼叫的成本很低。每次請求都重新定義常數沒有實質效益。
  • 在非瀏覽器情境中,建議優先使用 Output(..., 'S')'F',而非 'I'/'D'。inline/download 路徑會產生與框架無關的輸出,這通常不是你在工作程序中需要的行為。
  • 大量產生時,請對引擎進行效能分析,而非轉接器。相較於算繪,轉接器本身的每頁額外負擔很小。
  • 每個轉接器實例都是獨立的,並各自持有文件狀態。只要每個工作單元都使用自己的轉接器實例,程序或工作程序層級的並行就是安全的。
  • LegacyBootstrapLegacyDefaults 中的冪等性防護使用程序本機的靜態狀態;這些防護在典型的 PHP per-request/per-worker 模型下是安全的。它們並不是設計來讓多個執行緒共用可變狀態。
  • 已完成嚴格模式稽核;正式環境在嚴格模式關閉的情況下執行。
  • 所有算繪進入點都已包在 try/catch 中以攔截 RuntimeException(不依賴 die())。
  • 工作程序使用 Output(..., 'F')'S',絕不使用 inline 路徑。
  • 已在啟動時、首次建構之前定義一次舊版常數。
  • 已建立定期的嚴格模式 CI 工作以攔截回歸。
  • 已重新建立位元組層級測試斷言的基準(請參閱 /integrations/tcpdf-compat/migration/)。
  • /integrations/tcpdf-compat/security-and-operations/ — 加密、簽署態勢、強化
  • /integrations/tcpdf-compat/troubleshooting/ — 正式環境失敗模式與修正
  • /integrations/tcpdf-compat/configuration/ — 嚴格模式與常數整潔
  • /integrations/tcpdf-compat/migration/ — 必須在上線前完成的稽核