ใช้งาน compat-legacy ในสภาพแวดล้อมจริง
โดยสรุป
หัวข้อที่มีชื่อว่า “โดยสรุป”อะแดปเตอร์ปลอดภัยสำหรับการใช้งานใน Hypertext Transfer Protocol (HTTP) handler, queue worker และโปรเซสที่ทำงานต่อเนื่องยาวนาน อะแดปเตอร์นี้ปลอดภัยกว่า TCPDF 6.2.13 แบบเดิม เพราะตัดความเสี่ยงในการใช้งานจริงสองอย่างที่มักพบมากที่สุด ได้แก่ การส่งเอาต์พุตไปยังบัฟเฟอร์โดยตรง และการเรียก die() เมื่อเกิดข้อผิดพลาด ใช้หน้านี้เป็นแนวทางสำหรับใช้งานอะแดปเตอร์อย่างถูกต้อง
ก่อนนำขึ้นใช้งานจริง ให้ตรวจสอบ strict-mode ใน /integrations/tcpdf-compat/migration/ ให้เสร็จสมบูรณ์ แล้วดีพลอยโดยปิด strict mode (off)
การจัดการเอาต์พุตใน worker และ handler
หัวข้อที่มีชื่อว่า “การจัดการเอาต์พุตใน worker และ handler”เมธอด Output() ของ TCPDF แบบเดิมเขียนไปยังบัฟเฟอร์เอาต์พุตที่กำลังใช้งานอยู่โดยตรง การทำเช่นนี้อาจทำให้การตอบสนองใน HTTP framework เสียหายและทำให้ queue worker ทำงานผิดพลาด อะแดปเตอร์จึงส่งเอาต์พุตผ่านบริดจ์ปลายทางที่ปลอดภัยแทน
เลือกปลายทางให้ตรงกับผู้เรียกใช้:
| บริบท | ปลายทาง | เหตุผล |
|---|---|---|
| Queue worker ที่เขียนไปยังพื้นที่จัดเก็บ | Output($path, 'F') | เขียนไฟล์และคืนค่าสตริงว่าง โดยไม่โต้ตอบกับบัฟเฟอร์ |
| สร้างไฟล์แล้ว attach/upload | Output($name, 'S') | คืนค่าไบต์ Portable Document Format (PDF) แล้วให้ผู้เรียกควบคุมว่าจะนำไบต์เหล่านั้นไปไว้ที่ใด |
| ไฟล์แนบอีเมล | Output($name, 'E') | คืนค่าเนื้อหา (body) แบบ Multipurpose Internet Mail Extensions (MIME) ที่เข้ารหัส base64 พร้อม Content-Type: application/pdf ในส่วนหัว |
| การตอบสนอง HTTP ที่คุณควบคุมเอง | Output($name, 'S') | รับไบต์ จากนั้นกำหนดเฮดเดอร์และเนื้อหา (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;}ใน HTTP handler ให้ใช้ 'S' และกำหนดเฮดเดอร์ด้วยตนเอง:
<?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เสมอ - แมป exception ไปยัง error contract ของแอปพลิเคชัน เช่น HTTP 5xx, งานที่ล้มเหลว, การลองใหม่ หรือ dead-letter
- อย่าสันนิษฐานว่าโปรเซสจะสิ้นสุดเมื่อการเรนเดอร์ล้มเหลว เพราะโปรเซสจะไม่สิ้นสุด
ข้อยกเว้น TcpdfNotImplementedException ในงาน continuous integration (CI) แบบ strict-mode ที่รันเป็นระยะ (แนะนำ) ถือเป็นผลการตรวจพบจริง หมายความว่ามีเส้นทางโค้ดที่พึ่งพาพารามิเตอร์ TCPDF ที่ไม่รองรับ ให้ถือว่าเป็นงานย้ายระบบที่ต้องจัดการ ไม่ใช่การทดสอบที่ไม่เสถียร (flaky test)
วงจรชีวิตและการจัดการทรัพยากร
หัวข้อที่มีชื่อว่า “วงจรชีวิตและการจัดการทรัพยากร”- เอกสารจะสร้างไบต์ PDF แบบ lazy เมื่อมีการเรียกเอาต์พุตครั้งแรก
Close()เป็นทางเลือก และเมื่อเรียกใช้จะแคชไบต์ไว้Open()เป็น no-op ที่ปลอดภัย endPage()ไม่ทำสิ่งใดเลย เพราะ NextPDF เป็นผู้จัดการวงจรชีวิตของหน้า ให้นำออกจาก hot loop เพราะไม่ได้เพิ่มคุณค่า- ปล่อยให้ PHP เก็บกวาดหน่วยความจำ (garbage-collect) อะแดปเตอร์ระหว่างงานแต่ละงาน
_destroy()จะรีเซ็ตข้อมูลที่แคชไว้ของอะแดปเตอร์ แต่ไม่จำเป็นต้องเรียกใช้อย่างชัดเจนใน worker loop ปกติ - สร้างอะแดปเตอร์ใหม่สำหรับเอกสารแต่ละฉบับ อย่าใช้อินสแตนซ์อะแดปเตอร์เดียวซ้ำกับเอกสารที่ไม่เกี่ยวข้องกันใน worker ที่ทำงานต่อเนื่องยาวนาน เพราะสถานะของเอกสารอยู่ในแต่ละอินสแตนซ์
คำแนะนำด้านประสิทธิภาพ
หัวข้อที่มีชื่อว่า “คำแนะนำด้านประสิทธิภาพ”- อะแดปเตอร์เป็นเลเยอร์การส่งต่อ (delegation) ที่บางเบา ต้นทุนหลักมาจากเอนจิน ไม่ใช่อะแดปเตอร์
- กำหนดค่าคงที่แบบเดิมเพียงครั้งเดียวตอนบูต
LegacyDefaults::register()และLegacyBootstrap::enableAliases()เป็น idempotent และมีตัวป้องกัน ดังนั้นการเรียกซ้ำจึงมีต้นทุนต่ำ การกำหนดค่าคงที่ในทุกคำขอทำให้สิ้นเปลืองการทำงาน - ในบริบทที่ไม่ใช่เบราว์เซอร์ ควรใช้
Output(..., 'S')หรือ'F'แทน'I'/'D'เส้นทาง inline/download จะสร้างเอาต์พุตที่ไม่ขึ้นกับ framework ซึ่งโดยทั่วไปไม่ใช่สิ่งที่ต้องการใน worker - สำหรับการสร้างเอกสารปริมาณมาก ให้ทำโปรไฟล์ที่เอนจิน ไม่ใช่ที่อะแดปเตอร์ งบประมาณต่อหน้าสำหรับโอเวอร์เฮดของตัวอะแดปเตอร์เองน้อยเมื่อเทียบกับการเรนเดอร์
การทำงานพร้อมกัน (Concurrency)
หัวข้อที่มีชื่อว่า “การทำงานพร้อมกัน (Concurrency)”- อินสแตนซ์อะแดปเตอร์แต่ละตัวเป็นอิสระต่อกันและถือสถานะเอกสารของตนเอง การทำงานพร้อมกันในระดับโปรเซสหรือ worker ปลอดภัยเมื่อแต่ละงานใช้อินสแตนซ์อะแดปเตอร์ของตนเอง
- ตัวป้องกัน idempotency ใน
LegacyBootstrapและLegacyDefaultsใช้สถานะ static ในระดับ process-local ซึ่งปลอดภัยภายใต้โมเดล per-request/per-worker ทั่วไปของ PHP ตัวป้องกันเหล่านี้ไม่ได้ออกแบบมาเพื่อแบ่งปันสถานะที่เปลี่ยนแปลงได้ข้ามเธรด
รายการตรวจสอบก่อนนำขึ้นใช้งานจริง
หัวข้อที่มีชื่อว่า “รายการตรวจสอบก่อนนำขึ้นใช้งานจริง”- ตรวจสอบ strict-mode เสร็จสมบูรณ์แล้ว และการใช้งานจริงรันโดยปิด strict mode
- จุดเริ่มต้นของการเรนเดอร์ทั้งหมดครอบด้วย
try/catchสำหรับRuntimeException(ไม่พึ่งพาdie()) - Worker ใช้
Output(..., 'F')หรือ'S'และไม่ใช้เส้นทาง inline เด็ดขาด - ค่าคงที่แบบเดิมถูกกำหนดเพียงครั้งเดียวตอนบูต ก่อนการสร้างอินสแตนซ์ครั้งแรก
- มีงาน CI แบบ strict-mode ที่รันเป็นระยะเพื่อตรวจจับการถดถอย (regression)
- การตรวจสอบในการทดสอบระดับไบต์ตั้งเส้นฐานใหม่แล้ว (ดู /integrations/tcpdf-compat/migration/)
ดูเพิ่มเติม
หัวข้อที่มีชื่อว่า “ดูเพิ่มเติม”- /integrations/tcpdf-compat/security-and-operations/ — การเข้ารหัสลับ แนวทางการลงนาม และการเสริมความแข็งแกร่ง
- /integrations/tcpdf-compat/troubleshooting/ — รูปแบบความล้มเหลวในการใช้งานจริงและวิธีแก้ไข
- /integrations/tcpdf-compat/configuration/ — strict mode และความสะอาดของค่าคงที่
- /integrations/tcpdf-compat/migration/ — การตรวจสอบที่ต้องทำก่อนใช้งานจริง