跳转到内容

从 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——同时保留两者可以方便你进行对比。

选择旧版调用点解析该类的方式:

  • **建议做法:**逐文件将 use/require 改为 use NextPDF\Compat\Tcpdf\TCPDF;。明确,而且可以用 grep 搜索。
  • **当你还无法变更调用点时:**在启动时通过 LegacyBootstrap::enableAliases() 一次性启用可选的全局别名(请参阅 /integrations/tcpdf-compat/boot-and-discovery/)。这会将 \TCPDF 以及四个辅助类解析到适配层。

这两种策略在实践中是互斥的。如果真正的 TCPDF 库仍可被自动加载,而你又启用了全局别名, 那么当 \TCPDF 类已存在时,别名会被跳过。你可能因此在不知情的情况下继续使用旧版 TCPDF。在阶段 1 期间,建议采用逐文件的导入方式,这样你才能明确知道每个调用点使用的是哪个类。请参阅 /integrations/tcpdf-compat/troubleshooting/.

阶段 2 — 不做变更地执行现有测试套件

标题为“阶段 2 — 不做变更地执行现有测试套件”的章节

在不做其他代码变更的情况下,针对适配层执行完整测试套件。大多数委托方法(受测的约 120 个中有 94 个)行为都兼容。你应预期会出现两类可预测的失败:

  1. **字节级断言。**比对精确 PDF 字节的测试会失败,因为这个引擎是独立实现。这符合预期,并不是缺陷。请将这些延后到阶段 4 处理。
  2. **返回值分支。**少数方法返回的是兼容性占位值,而不是计算后的数值——最值得注意的是 MultiCell() 返回 1,而 Write() 返回 0。依赖这些返回值进行分支判断的代码需要调整。

为每一项失败建立清单。将每一项归类为 字节基准返回值真正的行为差异

这是确保迁移安全的阶段。启用严格模式后执行测试套件(或具有代表性的生产环境路径):

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 都是一项工作项。该消息包含方法名称、被忽略参数的精确清单,以及一条迁移提示。会抛出异常的方法集合,已在 tests/Unit/Compat/Tcpdf/TcpdfStrictModeTest.php 中列出并通过测试断言。每一项背后的原理说明都记录在 docs/TCPDF_COVERAGE.md

请将严格模式作为专用的 CI 作业执行,而不是在生产环境中执行。它的目的在于暴露差异,而不是让生产环境抛出异常。

针对每项差异,选择成本最低且正确的修复方式:

差异类型修复方式
被忽略的参数无关紧要(e.g. 你从未依赖的 TCPDF $align移除该参数。该调用就会完全兼容。
被忽略的参数确实会产生影响(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。

对于 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 开头且可以被解析(冒烟测试级别)。
  • 已渲染的文本内容存在(提取文本并对其进行断言)。
  • 结构属性(页数、页面大小、是否存在大纲)相符。

这是一项一次性成本,并能得到在未来引擎升级后仍然有效的测试。

当严格模式审计通过,生产环境的严格模式保持关闭,并且测试套件在重新建立基准后的断言下全部通过时,即可移除 tecnickcom/tcpdf

Terminal window
composer remove tecnickcom/tcpdf

重新执行测试套件。如果仍有任何内容解析到真正的 TCPDF 类,说明阶段 1 的别名注意事项已经触发——请修正其余调用点,明确导入适配层。

适配层是迁移辅助工具,并不是永久保留的层。在 TCPDF 已移除且引擎已验证后,请逐步淘汰适配层:

  1. 在每个模块中,将 new TCPDF(...) 替换为现代化的 NextPDF\Core\Document 构建方式。
  2. 将 TCPDF 方法调用替换为对应的现代化调用(你在阶段 4 已加入的 getDocument() 调用就是范本)。
  3. 当某个模块不再引用适配层时,删除其兼容性导入。
  4. 当没有任何模块再引用适配层时,将 nextpdf/compat-legacycomposer.json 中移除。

此时,你已经运行在现代化的 PDF 2.0 API 之上,不再依赖兼容性层。

  • 已安装 nextpdf/compat-legacy;引擎连接已验证。
  • 调用点已明确导入适配层(或已启用别名,并已将真正的 TCPDF 从自动加载路径中移除)。
  • 已针对适配层执行完整测试套件;失败已分类。
  • 已加入严格模式 CI 作业;每项差异都已编目记录。
  • 每项差异都已修正(移除参数/改用现代化 API/移除调用)。
  • 字节级断言已按 content/structure 重新建立基准。
  • 已移除 tecnickcom/tcpdf;测试套件全部通过。
  • 已逐模块淘汰适配层;依赖包已移除。
  • /integrations/tcpdf-compat/method-coverage/ — 各方法的行为与替换指引
  • docs/TCPDF_COVERAGE.md — 权威且经测试验证的对照表
  • /integrations/tcpdf-compat/configuration/ — 将设置从全局常量迁移出去
  • /integrations/tcpdf-compat/security-and-operations/ — 迁移期间的加密与签名
  • /integrations/tcpdf-compat/troubleshooting/ — alias/real-TCPDF 冲突及其他陷阱