ข้ามไปยังเนื้อหา

การย้ายจาก TCPDF 6.x ไปยัง NextPDF

ย้ายระบบตามลำดับที่ชัดเจน เริ่มจากย้ายไปยังเอนจิน NextPDF ด้วยการเปลี่ยนแปลงให้น้อยที่สุดเท่าที่ทำได้ พิสูจน์ว่าสิ่งที่ทำงานอยู่แล้วยังคงทำงานได้ ตรวจสอบสิ่งที่ยังทำงานไม่ได้ แก้ไขแต่ละจุดเรียกใช้ จากนั้นจึงนำอะแดปเตอร์ออก เลเยอร์ความเข้ากันได้รองรับขั้นตอนที่สองถึงสี่ ไม่ใช่ปลายทางสุดท้าย

หน้านี้นำเสนอกลยุทธ์ สำหรับพฤติกรรมที่แม่นยำของเมธอดแต่ละตัว ให้ใช้ /integrations/tcpdf-compat/method-coverage/ ควบคู่กับ docs/TCPDF_COVERAGE.md ซึ่งเป็นเมทริกซ์ในรีโปและเป็นแหล่งอ้างอิงหลัก

TCPDF 6.x codebase

Swap dependency: install compat-legacy

Run existing suite unchanged

Strict-mode audit: enumerate behavioral gaps

Fix call sites: drop ignored params or move to modern API

Re-baseline byte-level test assertions

Remove the TCPDF dependency

Incrementally retire the adapter onto Document

Diagram

ทุกขั้นตอนทำให้แอปพลิเคชันยังพร้อมส่งมอบได้ คุณไม่จำเป็นต้องเปลี่ยนทั้งระบบในคราวเดียว

ติดตั้ง 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 ตัวที่สำรวจ) มีพฤติกรรมที่เข้ากันได้ คาดว่าจะพบความล้มเหลวสองประเภท:

  1. การยืนยันระดับไบต์ (Byte-level assertions) การทดสอบที่เปรียบเทียบไบต์ของ Portable Document Format (PDF) ทุกไบต์แบบตรงไปตรงมาจะล้มเหลว เนื่องจากเอนจินเป็นการนำไปใช้งานแยกต่างหาก นี่เป็นสิ่งที่คาดไว้ ไม่ใช่ข้อบกพร่อง เลื่อนรายการเหล่านี้ไปจัดการในขั้นตอนที่ 4
  2. การแตกสาขาตามค่าที่ส่งคืน (Return-value branches) เมธอดบางตัวส่งคืนค่าตัวแทนเพื่อความเข้ากันได้ ไม่ใช่ค่าที่คำนวณจริง จุดที่เห็นชัดที่สุดคือ MultiCell() ส่งคืน 1 และ Write() ส่งคืน 0 โค้ดที่แยกการทำงานตามค่าที่ส่งคืนเหล่านั้นจำเป็นต้องปรับแก้

จัดทำรายการความล้มเหลวทุกรายการ แล้วจำแนกแต่ละรายการเป็น byte-baseline, return-value หรือ true behavioral gap อย่างใดอย่างหนึ่ง

ขั้นตอนนี้ช่วยให้การย้ายระบบปลอดภัยขึ้น รันชุดทดสอบหรือเส้นทางการทำงานบนระบบจริงที่เป็นตัวแทน โดยเปิดใช้โหมดเข้มงวด:

examples/migration-audit.php
<?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

สำหรับแต่ละช่องว่าง ให้เลือกวิธีแก้ที่ถูกต้องและมีต้นทุนต่ำที่สุด:

รูปแบบช่องว่างวิธีแก้
พารามิเตอร์ที่ถูกละเว้นไม่มีผล (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 ไม่สามารถแสดงสิ่งที่ต้องการได้:

examples/migration-escape-hatch.php
<?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) ตรงกัน

ต้นทุนที่จ่ายเพียงครั้งเดียวนี้จะทำให้คุณได้การทดสอบที่ทนต่อการอัปเกรดเอนจินในอนาคต

หลังจากการตรวจสอบในโหมดเข้มงวดผ่านแล้ว โหมดเข้มงวด ปิด อยู่บนระบบจริง และชุดทดสอบผ่าน (green) ด้วยการยืนยันที่ตั้งค่าพื้นฐานใหม่แล้ว ให้นำ tecnickcom/tcpdf ออก:

Terminal window
composer remove tecnickcom/tcpdf

รันชุดทดสอบอีกครั้ง หากยังมีสิ่งใดอ้างอิงไปยังคลาส TCPDF ตัวจริง แสดงว่าข้อควรระวังเรื่อง alias ในขั้นตอนที่ 1 มีผล ให้แก้ไขจุดเรียกใช้ที่เหลือให้ import อะแดปเตอร์อย่างชัดเจน

อะแดปเตอร์เป็นตัวช่วยในการย้ายระบบ ไม่ใช่เลเยอร์ถาวร หลังจากนำ TCPDF ออกและพิสูจน์เอนจินแล้ว ให้เลิกใช้อะแดปเตอร์ทีละน้อย:

  1. ในแต่ละโมดูล ให้แทนที่ new TCPDF(...) ด้วยการสร้าง NextPDF\Core\Document แบบสมัยใหม่
  2. แทนที่การเรียกเมธอดของ TCPDF ด้วยตัวเทียบเท่าแบบสมัยใหม่ (การเรียก getDocument() ที่เพิ่มไว้แล้วในขั้นตอนที่ 4 คือแม่แบบ)
  3. เมื่อโมดูลไม่อ้างอิงอะแดปเตอร์อีกต่อไป ให้ลบ import เพื่อความเข้ากันได้ของโมดูลนั้น
  4. เมื่อไม่มีโมดูลใดอ้างอิงอะแดปเตอร์อีก ให้นำ 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 และกับดักอื่นๆ