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

ย้ายโค้ดเบส TCPDF 6.x ไปยัง NextPDF

แพ็กเกจ nextpdf/compat-legacy เปิดให้ใช้ชื่อเมธอดสาธารณะ ลำดับพารามิเตอร์ และค่าเริ่มต้นของ TCPDF 6.x บนเอนจิน NextPDF core ผ่านอะแดปเตอร์ NextPDF\Compat\Tcpdf\TCPDF ให้ย้ายตามลำดับนี้: เปลี่ยนไปใช้เอนจินด้วยการแก้ไขให้น้อยที่สุด ยืนยันว่าส่วนใดยังทำงานได้อยู่ เปิด strict mode เพื่อหาจุดที่ยังทำงานไม่ได้ แก้ไข call site ทีละจุด แล้วจึงเลิกใช้อะแดปเตอร์และเปลี่ยนไปใช้ API สมัยใหม่ อะแดปเตอร์มีไว้รองรับการย้าย ไม่ใช่ปลายทางสุดท้าย

ข้อกำหนดเบื้องต้นก่อนเริ่ม:

  • ติดตั้ง NextPDF core และ nextpdf/compat-legacy แล้ว
  • มีโค้ดเบส TCPDF 6.x ที่มีอยู่พร้อมชุดทดสอบ ชุดทดสอบนี้เป็นกลไกป้องกันความเสี่ยงสำหรับแต่ละขั้นตอนด้านล่าง

หน้านี้เป็นคู่มือเชิงปฏิบัติ สำหรับพฤติกรรมรายเมธอดของการเรียก TCPDF หนึ่งครั้ง ให้อ่านหน้า method-coverage หากต้องการกลยุทธ์แบบครบถ้วนทีละไฟล์พร้อมโค้ด ให้อ่านหน้า migration ต้นทาง ลิงก์ทั้งสองอยู่ในส่วน ดูเพิ่มเติม

ติดตั้งอะแดปเตอร์ควบคู่กับ core อย่าเพิ่งลบไลบรารี TCPDF จริง — การเก็บทั้งสองไว้ช่วยให้เปรียบเทียบเอาต์พุตระหว่างการย้ายได้

Terminal window
composer require nextpdf/compat-legacy

ก่อนเปลี่ยนแปลงโค้ดใดๆ ให้ยืนยันว่าลิงก์ของเอนจินถูก resolve (nextpdf/core ^3.0) และชุดทดสอบยังคงรันได้

อะแดปเตอร์เป็นเลเยอร์ความเข้ากันได้ ไม่ใช่ fork ของ TCPDF และไม่ใช่โคลนที่เหมือนกันระดับไบต์ จากเมธอดสาธารณะของ TCPDF 6.x ที่สำรวจไว้ราว 120 เมธอด ประมาณ 94 เมธอดแมปไปยังการดำเนินการของ NextPDF\Core\Document โดยตรงและทำงานเข้ากันได้กับพารามิเตอร์ที่มีเอกสารกำกับ เมธอดส่วนน้อยที่ระบุไว้แบ่งเป็นกลุ่มที่รับพารามิเตอร์รุ่นเก่าที่เอนจินไม่รองรับแล้วละเว้นอย่างเงียบ (silent-ignore) หรือกลุ่มที่ไม่สร้างเอาต์พุต (unimplemented หรือ not-applicable) เมทริกซ์ความครอบคลุมที่เป็นทางการและผ่านการตรวจสอบด้วยการทดสอบอยู่ในที่เก็บแพ็กเกจที่ docs/TCPDF_COVERAGE.md หากคู่มือนี้ขัดแย้งกับเมทริกซ์นั้น ให้ยึดตามเมทริกซ์

ข้อเท็จจริงสองข้อต่อไปนี้กำหนดรูปแบบการย้ายทั้งหมด:

  • ไบต์ของเอาต์พุตแตกต่างกัน เอนจินเป็น implementation ของ PDF 2.0 ที่พัฒนาขึ้นอย่างอิสระ ดังนั้นไบต์เอาต์พุตจึงแตกต่างจากเอาต์พุตของ TCPDF แม้ว่าผลลัพธ์ที่มองเห็นจะดูเหมือนกันก็ตาม การทดสอบที่ assert ว่าไบต์ PDF ตรงกันทุกตัวจำเป็นต้องปรับเส้นฐานใหม่ให้วัดจากเนื้อหาที่เรนเดอร์หรือคุณสมบัติเชิงโครงสร้าง
  • Strict mode คือเครื่องมือตรวจสอบของคุณ เมื่อปิด strict mode (ค่าเริ่มต้น) เมธอดที่ไม่สามารถจำลองพฤติกรรมของ TCPDF ได้จะถูกลดทอนอย่างเงียบๆ เมื่อเปิด strict mode การเรียกเหล่านั้นจะโยน TcpdfNotImplementedException พร้อมระบุพารามิเตอร์ที่ถูกละเว้นอย่างชัดเจนและให้คำแนะนำสำหรับการย้าย รัน strict mode ในรอบการตรวจสอบเฉพาะ ไม่ใช่ในระบบโปรดักชัน

อะแดปเตอร์ยังเปิดเผยอ็อบเจ็กต์เอกสารของเอนจินที่ห่อหุ้มอยู่ผ่าน getDocument() ซึ่งคืนค่า NextPDF\Core\Document ใช้เป็นเส้นทางออก แล้วย้าย call site ไปยัง API สมัยใหม่ทีละจุดจนกว่าจะลบอะแดปเตอร์ได้

ประเด็นพื้นผิว
การสร้างอินสแตนซ์new NextPDF\Compat\Tcpdf\TCPDF('P', 'mm', 'A4')
global alias แบบเลือกเปิดใช้NextPDF\Compat\Tcpdf\LegacyBootstrap::enableAliases()
เปิดใช้การตรวจสอบTCPDF::setStrictMode(true)
ข้อยกเว้นของการตรวจสอบNextPDF\Compat\Tcpdf\Exception\TcpdfNotImplementedException
ทางออกฉุกเฉินสู่ API สมัยใหม่TCPDF::getDocument(): NextPDF\Core\Document
เอาต์พุตTCPDF::Output(string $name, string $dest)S, F, E, I, D

LegacyBootstrap::enableAliases() เป็น idempotent โดยจะลงทะเบียน \TCPDF, \TCPDF_STATIC, \TCPDF_FONTS, \TCPDF_COLORS และ \TCPDF_IMAGES เฉพาะเมื่อคลาสเหล่านั้นยังไม่มีอยู่ หน้า method-coverage และ quickstart ที่ลิงก์ไว้ในส่วน ดูเพิ่มเติม ครอบคลุมพฤติกรรมรายเมธอดและปลายทางเอาต์พุตอย่างครบถ้วน

เปลี่ยน import แต่เก็บการเรียกแบบ TCPDF เดิมไว้ แล้วสร้าง PDF

quickstart-first.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\TCPDF;
$pdf = new TCPDF('P', 'mm', 'A4');
$pdf->SetCreator('Quickstart');
$pdf->SetTitle('First Document');
$pdf->SetFont('helvetica', '', 12);
$pdf->AddPage();
$pdf->Cell(0, 10, 'Hello from the NextPDF engine', 1, 1, 'C');
$pdf->Output(__DIR__ . '/quickstart.pdf', 'F');

Output($name, 'F') เขียนไฟล์และคืนค่าสตริงว่าง ต่างจาก TCPDF รุ่นเก่า Output() ของอะแดปเตอร์ไม่ echo ไปยังบัฟเฟอร์เอาต์พุตที่ทำงานอยู่ คุณจึงเรียกใช้ภายใน queue worker หรือ HTTP handler ที่ควบคุม response ของตนเองได้อย่างปลอดภัย

หากยังเปลี่ยน call site ที่สร้าง new \TCPDF(...) กับ global namespace ไม่ได้ ให้เปิดใช้ alias แบบเลือกเปิดใช้หนึ่งครั้งตอนบูต

quickstart-alias.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\LegacyBootstrap;
LegacyBootstrap::enableAliases();
// Legacy code now resolves \TCPDF to the adapter:
$pdf = new \TCPDF('P', 'mm', 'A4');
$pdf->AddPage();
$pdf->SetFont('helvetica', '', 12);
$pdf->Cell(0, 10, 'Legacy call site, modern engine');
$pdf->Output(__DIR__ . '/aliased.pdf', 'F');

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

ขั้นตอนที่ปลอดภัยสำหรับการย้ายคือการตรวจสอบด้วย strict mode โดยรันเส้นทางโปรดักชันที่เป็นตัวแทนหรือชุดทดสอบขณะเปิด strict mode และรวบรวม TcpdfNotImplementedException ทุกรายการ ข้อยกเว้นแต่ละรายการคืองานหนึ่งรายการ โดยระบุชื่อเมธอด พารามิเตอร์ที่ถูกละเว้น และคำแนะนำ

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 $exception) {
// Each message names the method, the ignored parameters, and a hint.
fwrite(STDERR, 'MIGRATION GAP: ' . $exception->getMessage() . "\n");
}

สำหรับช่องว่างแต่ละจุด ให้เลือกการแก้ไขที่ถูกต้องและประหยัดที่สุด ละทิ้งพารามิเตอร์ที่ไม่เคยพึ่งพา หรือแสดงเจตนาผ่าน API สมัยใหม่ด้วย getDocument() ทางออกฉุกเฉินนี้ใช้จัดการสิ่งใดๆที่พื้นผิว TCPDF ไม่สามารถแสดงออกได้

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 for the parts that already work:
$pdf->SetFont('helvetica', '', 12);
$pdf->Cell(0, 10, 'Header line', 0, 1);
// Modern path for what the TCPDF surface cannot express here —
// for example a clickable image (the legacy Image() link parameter
// is one of the silently ignored parameters):
$document = $pdf->getDocument();
$document->image('logo.png', 10, 30, 40, 0);
$document->link(10, 30, 40, 20, 'https://example.com');

รัน strict mode เป็นงาน continuous integration (CI) แยกเฉพาะ จากนั้นปิดและปรับใช้เส้นทางโค้ดที่ผ่านการตรวจสอบแล้ว เก็บงาน CI แบบ strict mode ไว้เป็นระยะ เพื่อจับ regression ขณะ refactor

  • MultiCell() คืนค่า 1 Write() คืนค่า 0 ค่าเหล่านี้เป็นตัวยึดตำแหน่งเพื่อความเข้ากันได้ ไม่ใช่ค่าที่คำนวณได้ ปรับโค้ดใดๆที่ branch ตามค่าที่คืนเหล่านี้
  • Error() โยนข้อยกเว้นแทนการเรียก die() อะแดปเตอร์โยน RuntimeException โค้ดที่พึ่งพาการสิ้นสุดโปรเซสต้องดักจับข้อยกเว้น
  • พารามิเตอร์ที่ถูกละเว้นอย่างเงียบๆ เมธอดอย่าง Image() writeHTML() SetProtection() และ Bookmark() รับพารามิเตอร์รุ่นเก่าที่ถูกละเว้น ใช้ strict mode เพื่อค้นหาพารามิเตอร์เหล่านั้น สำหรับรูปภาพที่คลิกได้ ให้วาดรูปภาพ จากนั้นเพิ่ม Document::link() ครอบรูปสี่เหลี่ยมเดียวกัน
  • เมธอดที่ยังไม่ได้พัฒนา setSignature() addEmptySignatureAppearance() และ endPage() เป็น no-op ที่โยนข้อยกเว้นใน strict mode ส่วน Open() เป็น no-op ที่ปลอดภัยและไม่โยนข้อยกเว้นเลย ลบ endPage() และ Open() การลงนามต้องใช้ NextPDF รุ่นเชิงพาณิชย์ผ่าน signature API สมัยใหม่
  • เวอร์ชัน PDF ถูกกำหนดตายตัว setPDFVersion() ไม่สามารถลดเป้าหมายไปยังเวอร์ชัน PDF ที่เก่ากว่าได้ เอาต์พุตเป็น PDF 2.0 เสมอ setUserRights() ถูกเลิกใช้ใน PDF 2.0 และถูกละเว้นพร้อมแจ้งเตือน
  • ความขัดแย้งของ alias หากยังมีส่วนใด resolve ไปยังคลาส TCPDF จริงหลังจากที่ลบ tecnickcom/tcpdf แล้ว ข้อควรระวังเรื่อง alias จะมีผล — ให้ import อะแดปเตอร์อย่างชัดเจนที่ call site เหล่านั้น

อะแดปเตอร์มอบหมายงานให้เอนจิน ต้นทุนการสร้างเอกสารจึงแปรผันตามเนื้อหา ไม่ใช่ตามเลเยอร์ของอะแดปเตอร์ เนื่องจาก Output() ของอะแดปเตอร์ไม่เขียนลงในบัฟเฟอร์เอาต์พุต จึงปลอดภัยภายใน queue worker — ให้ย้ายการสร้างแบบ TCPDF ที่หนักออกจากเธรดของ request เช่นเดียวกับที่ย้ายการสร้าง NextPDF ใดๆ การปรับเส้นฐานของการทดสอบระดับไบต์ใหม่ให้ยึดเนื้อหาที่เรนเดอร์เป็นต้นทุนครั้งเดียว และทำให้ได้การทดสอบที่ทนต่อการอัปเกรดเอนจินในอนาคต

  • การเข้ารหัสลับ SetProtection() ละเว้นพารามิเตอร์รุ่นเก่า mode และ pubkeys เอนจินใช้ AES-256 สำหรับ handler มาตรฐาน สำหรับการเข้ารหัสลับแบบใช้ใบรับรอง ให้ใช้จุดเข้าใช้งานการเข้ารหัสลับด้วยกุญแจสาธารณะสมัยใหม่ที่อะแดปเตอร์เปิดเผย ไม่ใช่พารามิเตอร์รุ่นเก่า
  • การลงนามถูกจำกัดสิทธิ์ การรองรับลายเซ็นพื้นฐานเป็นความสามารถของรุ่นเชิงพาณิชย์ที่เข้าถึงได้ผ่าน signature API สมัยใหม่ด้วย value object ของใบรับรอง ส่วน setSignature() รุ่นเก่าเป็น no-op คู่มือนี้ไม่อ้างถึง long-term-validation หรือโปรไฟล์ลายเซ็นที่ประทับเวลาสำหรับรุ่นใดๆ
  • ล้มเหลวอย่างชัดเจนระหว่างการตรวจสอบ strict mode ทำให้การสูญเสียพารามิเตอร์อย่างเงียบๆมองเห็นได้ คุณจึงทราบเมื่ออะแดปเตอร์ไม่สามารถทำตามเจตนาของผู้เรียกได้ ถือว่าข้อยกเว้นที่รวบรวมได้เป็นรายการงานสำหรับการย้าย ไม่ใช่พฤติกรรมในโปรดักชัน
  • อย่าเขียนบล็อก catch ที่ว่างเปล่าเด็ดขาด ตัวอย่างการตรวจสอบดักจับ TcpdfNotImplementedException และเขียนบรรทัดรายการงานตามที่กำหนดไว้

แนวปฏิบัติด้านการเข้ารหัสลับและลายเซ็นทั้งหมดระหว่างการย้ายอยู่ในหน้า security-and-operations ของ compat-legacy

คู่มือนี้ไม่ได้สร้างข้อกล่าวอ้างเชิงบรรทัดฐานใหม่ของตนเอง อะแดปเตอร์สร้างเอาต์พุต PDF 2.0 (ISO 32000-2) และไม่สามารถลดเป้าหมายไปยังเวอร์ชันที่เก่ากว่าได้ พฤติกรรมและข้อกำหนดดังกล่าวถูกตรึงไว้ในหน้า method-coverage ต้นทาง ซึ่งยังบันทึกหลักการ fail-explicitly ของ OWASP ที่อยู่เบื้องหลัง strict mode และกรอบความครบถ้วนเชิงฟังก์ชันตาม ISO/IEC 25023 ของการตรวจสอบความครอบคลุม หน้า cookbook นี้ทบทวนการใช้งานซ้ำ โดยให้หน้าต้นทางเป็นแหล่งอ้างอิงสำหรับรายละเอียดเหล่านั้น