การย้ายจาก TCPDF 6.x ไปยัง NextPDF
ภาพรวมโดยสังเขป
หัวข้อที่มีชื่อว่า “ภาพรวมโดยสังเขป”ย้ายระบบตามลำดับที่ชัดเจน เริ่มจากย้ายไปยังเอนจิน NextPDF ด้วยการเปลี่ยนแปลงให้น้อยที่สุดเท่าที่ทำได้ พิสูจน์ว่าสิ่งที่ทำงานอยู่แล้วยังคงทำงานได้ ตรวจสอบสิ่งที่ยังทำงานไม่ได้ แก้ไขแต่ละจุดเรียกใช้ จากนั้นจึงนำอะแดปเตอร์ออก เลเยอร์ความเข้ากันได้รองรับขั้นตอนที่สองถึงสี่ ไม่ใช่ปลายทางสุดท้าย
หน้านี้นำเสนอกลยุทธ์ สำหรับพฤติกรรมที่แม่นยำของเมธอดแต่ละตัว ให้ใช้ /integrations/tcpdf-compat/method-coverage/ ควบคู่กับ docs/TCPDF_COVERAGE.md ซึ่งเป็นเมทริกซ์ในรีโปและเป็นแหล่งอ้างอิงหลัก
โมเดลการย้ายระบบ
หัวข้อที่มีชื่อว่า “โมเดลการย้ายระบบ”ทุกขั้นตอนทำให้แอปพลิเคชันยังพร้อมส่งมอบได้ คุณไม่จำเป็นต้องเปลี่ยนทั้งระบบในคราวเดียว
ขั้นตอนที่ 1 — สลับ dependency
หัวข้อที่มีชื่อว่า “ขั้นตอนที่ 1 — สลับ dependency”ติดตั้ง nextpdf/compat-legacy (ดู /integrations/tcpdf-compat/install/) แต่ ยังห้าม นำ tecnickcom/tcpdf ออกในตอนนี้ การเก็บทั้งสองตัวไว้จะช่วยให้คุณเปรียบเทียบผลลัพธ์ได้
เลือกวิธีให้จุดเรียกใช้แบบเดิม (legacy) อ้างอิงคลาส:
- แนะนำ: เปลี่ยน
use/requireของแต่ละไฟล์เป็นuse NextPDF\Compat\Tcpdf\TCPDF;วิธีนี้ชัดเจนและค้นหาได้ง่าย - เมื่อยังแก้ไขจุดเรียกใช้ไม่ได้: เปิดใช้ global alias แบบเลือกใช้เองเพียงครั้งเดียวตอนบูตด้วย
LegacyBootstrap::enableAliases()(ดู /integrations/tcpdf-compat/boot-and-discovery/) วิธีนี้จะทำให้\TCPDFและคลาสตัวช่วยอีกสี่ตัวอ้างอิงไปยังอะแดปเตอร์
ในทางปฏิบัติ กลยุทธ์ทั้งสองนี้ใช้ร่วมกันไม่ได้ หากไลบรารี TCPDF ตัวจริงยังคง autoload ได้และคุณเปิดใช้ global alias alias จะถูกข้ามเมื่อมีคลาส
\TCPDFอยู่แล้ว คุณอาจ ยังใช้ TCPDF ตัวเดิมอยู่โดยไม่รู้ตัว ระหว่างขั้นตอนที่ 1 ควรใช้การ import แบบรายไฟล์ เพื่อให้ทราบชัดว่าแต่ละจุดเรียกใช้ใช้คลาสใด ดู /integrations/tcpdf-compat/troubleshooting/
ขั้นตอนที่ 2 — รันชุดทดสอบเดิมโดยไม่เปลี่ยนแปลง
หัวข้อที่มีชื่อว่า “ขั้นตอนที่ 2 — รันชุดทดสอบเดิมโดยไม่เปลี่ยนแปลง”รันชุดทดสอบทั้งหมดของคุณกับอะแดปเตอร์โดยไม่เปลี่ยนแปลงสิ่งอื่นใด เมธอดที่ส่งต่อ (delegate) ส่วนใหญ่ (94 จากประมาณ 120 ตัวที่สำรวจ) มีพฤติกรรมที่เข้ากันได้ คาดว่าจะพบความล้มเหลวสองประเภท:
- การยืนยันระดับไบต์ (Byte-level assertions) การทดสอบที่เปรียบเทียบไบต์ของ Portable Document Format (PDF) ทุกไบต์แบบตรงไปตรงมาจะล้มเหลว เนื่องจากเอนจินเป็นการนำไปใช้งานแยกต่างหาก นี่เป็นสิ่งที่คาดไว้ ไม่ใช่ข้อบกพร่อง เลื่อนรายการเหล่านี้ไปจัดการในขั้นตอนที่ 4
- การแตกสาขาตามค่าที่ส่งคืน (Return-value branches) เมธอดบางตัวส่งคืนค่าตัวแทนเพื่อความเข้ากันได้ ไม่ใช่ค่าที่คำนวณจริง จุดที่เห็นชัดที่สุดคือ
MultiCell()ส่งคืน1และWrite()ส่งคืน0โค้ดที่แยกการทำงานตามค่าที่ส่งคืนเหล่านั้นจำเป็นต้องปรับแก้
จัดทำรายการความล้มเหลวทุกรายการ แล้วจำแนกแต่ละรายการเป็น byte-baseline, return-value หรือ true behavioral gap อย่างใดอย่างหนึ่ง
ขั้นตอนที่ 3 — การตรวจสอบในโหมดเข้มงวด (strict mode)
หัวข้อที่มีชื่อว่า “ขั้นตอนที่ 3 — การตรวจสอบในโหมดเข้มงวด (strict mode)”ขั้นตอนนี้ช่วยให้การย้ายระบบปลอดภัยขึ้น รันชุดทดสอบหรือเส้นทางการทำงานบนระบบจริงที่เป็นตัวแทน โดยเปิดใช้โหมดเข้มงวด:
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\Exception\TcpdfNotImplementedException;use NextPDF\Compat\Tcpdf\TCPDF;
function renderInvoice(TCPDF $pdf): void{ // ... your existing rendering code, unchanged ...}
$pdf = new TCPDF('P', 'mm', 'A4');$pdf->setStrictMode(true);
try { renderInvoice($pdf); $pdf->Output(__DIR__ . '/audit.pdf', 'F');} catch (TcpdfNotImplementedException $e) { // Each message names the method, the ignored parameters, and a hint. fwrite(STDERR, 'MIGRATION GAP: ' . $e->getMessage() . "\n");}ถือว่า TcpdfNotImplementedException แต่ละรายการเป็นงานหนึ่งรายการ ข้อความจะระบุเมธอด รายการพารามิเตอร์ที่ถูกละเว้นทั้งหมด และคำแนะนำในการย้ายระบบ ชุดเมธอดที่จะ throw ถูกแจกแจงและยืนยันด้วยการทดสอบไว้ใน tests/Unit/Compat/Tcpdf/TcpdfStrictModeTest.php เหตุผลของแต่ละรายการอธิบายไว้ครบถ้วนใน docs/TCPDF_COVERAGE.md
รันโหมดเข้มงวดเป็น งาน continuous integration (CI) ที่จัดไว้โดยเฉพาะ ไม่ใช่บนระบบจริง จุดประสงค์คือเปิดเผยช่องว่าง ไม่ใช่ทำให้ระบบจริง throw
ขั้นตอนที่ 4 — แก้ไขจุดเรียกใช้
หัวข้อที่มีชื่อว่า “ขั้นตอนที่ 4 — แก้ไขจุดเรียกใช้”สำหรับแต่ละช่องว่าง ให้เลือกวิธีแก้ที่ถูกต้องและมีต้นทุนต่ำที่สุด:
| รูปแบบช่องว่าง | วิธีแก้ |
|---|---|
พารามิเตอร์ที่ถูกละเว้นไม่มีผล (e.g. $align ของ TCPDF ที่คุณไม่ได้พึ่งพา) | ตัดพารามิเตอร์ออก การเรียกใช้จะเข้ากันได้อย่างสมบูรณ์ |
พารามิเตอร์ที่ถูกละเว้นมีผล (e.g. ลิงก์ Image() ที่คลิกได้) | เขียนใหม่ด้วย API สมัยใหม่ วาดรูปภาพ แล้วเพิ่ม Document::link() ทับบนสี่เหลี่ยมผืนผ้า |
เมธอดยังไม่ได้นำไปใช้งาน (setSignature(), endPage()) | endPage() / Open(): นำการเรียกใช้ออก การลงนาม: ดู /integrations/tcpdf-compat/security-and-operations/ ซึ่งจำเป็นต้องใช้รุ่นเชิงพาณิชย์ |
เมธอดที่ไม่สามารถใช้ได้ (setPDFVersion(), setUserRights()) | นำการเรียกใช้ออก ผลลัพธ์เป็น PDF 2.0 เสมอ ส่วน user-rights ถูกเลิกใช้แล้วใน PDF 2.0 |
| สาขาที่อิงค่าที่ส่งคืน | คำนวณค่าด้วยตนเอง หรือย้ายตรรกะนั้นไปยัง API สมัยใหม่ |
ใช้ทางออกสำรอง (escape hatch) เมื่ออินเทอร์เฟซของ TCPDF ไม่สามารถแสดงสิ่งที่ต้องการได้:
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\TCPDF;
$pdf = new TCPDF();$pdf->AddPage();
// Legacy path stays as-is for the parts that work:$pdf->SetFont('helvetica', '', 12);$pdf->Cell(0, 10, 'Header line', 0, 1);
// Modern path for what the TCPDF surface cannot express here:$document = $pdf->getDocument();$document->image('logo.png', 10, 30, 40, 0);$document->link(10, 30, 40, 20, 'https://example.com');ตั้งค่าพื้นฐานใหม่สำหรับการทดสอบระดับไบต์
หัวข้อที่มีชื่อว่า “ตั้งค่าพื้นฐานใหม่สำหรับการทดสอบระดับไบต์”แทนที่การยืนยันแบบตรงทุกไบต์ด้วยการยืนยันสิ่งที่สำคัญจริง:
- ผลลัพธ์เริ่มต้นด้วย
%PDFและแยกวิเคราะห์ได้ (ระดับ smoke) - มีเนื้อหาข้อความที่เรนเดอร์อยู่ (ดึงข้อความออกมาแล้วยืนยันกับข้อความนั้น)
- คุณสมบัติเชิงโครงสร้าง (จำนวนหน้า ขนาดหน้า และการมีอยู่ของ outline) ตรงกัน
ต้นทุนที่จ่ายเพียงครั้งเดียวนี้จะทำให้คุณได้การทดสอบที่ทนต่อการอัปเกรดเอนจินในอนาคต
ขั้นตอนที่ 5 — นำ dependency ของ TCPDF ออก
หัวข้อที่มีชื่อว่า “ขั้นตอนที่ 5 — นำ dependency ของ TCPDF ออก”หลังจากการตรวจสอบในโหมดเข้มงวดผ่านแล้ว โหมดเข้มงวด ปิด อยู่บนระบบจริง และชุดทดสอบผ่าน (green) ด้วยการยืนยันที่ตั้งค่าพื้นฐานใหม่แล้ว ให้นำ tecnickcom/tcpdf ออก:
composer remove tecnickcom/tcpdfรันชุดทดสอบอีกครั้ง หากยังมีสิ่งใดอ้างอิงไปยังคลาส TCPDF ตัวจริง แสดงว่าข้อควรระวังเรื่อง alias ในขั้นตอนที่ 1 มีผล ให้แก้ไขจุดเรียกใช้ที่เหลือให้ import อะแดปเตอร์อย่างชัดเจน
ขั้นตอนที่ 6 — เลิกใช้อะแดปเตอร์
หัวข้อที่มีชื่อว่า “ขั้นตอนที่ 6 — เลิกใช้อะแดปเตอร์”อะแดปเตอร์เป็นตัวช่วยในการย้ายระบบ ไม่ใช่เลเยอร์ถาวร หลังจากนำ TCPDF ออกและพิสูจน์เอนจินแล้ว ให้เลิกใช้อะแดปเตอร์ทีละน้อย:
- ในแต่ละโมดูล ให้แทนที่
new TCPDF(...)ด้วยการสร้างNextPDF\Core\Documentแบบสมัยใหม่ - แทนที่การเรียกเมธอดของ TCPDF ด้วยตัวเทียบเท่าแบบสมัยใหม่ (การเรียก
getDocument()ที่เพิ่มไว้แล้วในขั้นตอนที่ 4 คือแม่แบบ) - เมื่อโมดูลไม่อ้างอิงอะแดปเตอร์อีกต่อไป ให้ลบ import เพื่อความเข้ากันได้ของโมดูลนั้น
- เมื่อไม่มีโมดูลใดอ้างอิงอะแดปเตอร์อีก ให้นำ
nextpdf/compat-legacyออกจากcomposer.jsonทิ้งไป
ถึงจุดนั้น คุณจะอยู่บน API ของ PDF 2.0 แบบสมัยใหม่โดยไม่มีเลเยอร์ความเข้ากันได้
รายการตรวจสอบการย้ายระบบ
หัวข้อที่มีชื่อว่า “รายการตรวจสอบการย้ายระบบ”- ติดตั้ง
nextpdf/compat-legacyแล้ว และยืนยันการเชื่อมต่อกับเอนจินแล้ว - จุดเรียกใช้ import อะแดปเตอร์อย่างชัดเจน (หรือเปิดใช้ alias โดยนำ TCPDF ตัวจริงออกจากเส้นทาง autoload แล้ว)
- รันชุดทดสอบทั้งหมดกับอะแดปเตอร์และจำแนกความล้มเหลวแล้ว
- เพิ่มงาน CI โหมดเข้มงวดและจัดทำรายการช่องว่างทุกรายการแล้ว
- แก้ไขช่องว่างแต่ละรายการแล้ว (ตัดพารามิเตอร์ / API สมัยใหม่ / นำการเรียกใช้ออก)
- ตั้งค่าพื้นฐานใหม่ให้การยืนยันระดับไบต์อิงเนื้อหาและโครงสร้างแล้ว
- นำ
tecnickcom/tcpdfออกแล้ว และชุดทดสอบผ่าน (green) - เลิกใช้อะแดปเตอร์ทีละโมดูล และนำ dependency ออกแล้ว
ดูเพิ่มเติม
หัวข้อที่มีชื่อว่า “ดูเพิ่มเติม”- /integrations/tcpdf-compat/method-coverage/ — พฤติกรรมรายเมธอดและคำแนะนำในการแทนที่
docs/TCPDF_COVERAGE.md— เมทริกซ์แหล่งอ้างอิงหลักที่ตรวจสอบด้วยการทดสอบแล้ว- /integrations/tcpdf-compat/configuration/ — การย้ายการกำหนดค่าออกจากค่าคงที่ global
- /integrations/tcpdf-compat/security-and-operations/ — การเข้ารหัสลับและการลงนามระหว่างการย้ายระบบ
- /integrations/tcpdf-compat/troubleshooting/ — ความขัดแย้งของ alias/real-TCPDF และกับดักอื่นๆ