跳转到内容

NextPDF compat-legacy 的引导与发现

nextpdf/compat-legacy 提供一个 TCPDF 兼容门面——类 NextPDF\Compat\Tcpdf\TCPDF——并将操作委托给 NextPDF 引擎。它是一个兼容层,并不是可直接替换的克隆实现。在已调研的约 120 个 TCPDF 6.x 方法中,它通过直接委托覆盖了 94 个。其余方法存在已记录的行为差异(参见 /integrations/tcpdf-compat/method-coverage/)。

不会在自动加载时进行全局接线。默认情况下,引入该包并不会创建全局的 \TCPDF 类。你需要显式选择启用全局别名,或者采用迁移期间推荐的方式:在每个文件中导入适配器类。

该门面是一个普通的 PSR-4 自动加载类:

项目
门面类NextPDF\Compat\Tcpdf\TCPDF
PSR-4 前缀NextPDF\Compat\Tcpdf\ 映射到 src/Compat/Tcpdf/
共享契约NextPDF\Compat\Contracts\CompatAdapterInterface
逃生口TCPDF::getDocument() 返回被包装的 NextPDF\Core\Document

该类有意不是 final 的:旧版 TCPDF 用户通常会子类化 TCPDF 来覆盖 Header()Footer(),因此适配器保留了这个扩展点。在内部,该类是一个门面。它组合了 25 个按单一职责拆分的关注点 trait,并将所有 PDF 操作委托给构造时创建的 Document 实例。

Composer autoload

Class referenced: new TCPDF or new global TCPDF

Constructor runs

LegacyDefaults::register defines K_/PDF_ constants if absent

ConstructorBridge::build maps orientation/unit/format to Document

Document, UnitConverter, PageSize stored on the facade

Creator/Author seeded from PDF_CREATOR / PDF_AUTHOR

Diagram

构造是唯一的“引导”步骤。包本身没有服务容器注册,也没有框架引导。框架集成需要由你自行添加一层很薄的封装——参见 /integrations/tcpdf-compat/integration/.

LegacyDefaults::register() 是幂等的:它仅在某个常量尚未定义时才定义该常量。只要在首次构造之前定义,应用自定义的常量始终优先(参见 /integrations/tcpdf-compat/configuration/ § 配置解析顺序)。

如果你的代码库通过全局命名空间调用 new \TCPDF(...),而你暂时无法修改这些调用点,可以在应用引导时一次性注册全局别名:

examples/boot-aliases.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\LegacyBootstrap;
LegacyBootstrap::enableAliases();
// Global names now resolve to the adapter:
$pdf = new \TCPDF('P', 'mm', 'A4');

enableAliases() 会注册 \TCPDF\TCPDF_STATIC\TCPDF_FONTS\TCPDF_COLORS 以及 \TCPDF_IMAGES。它的行为(由 tests/Unit/Compat/Tcpdf/LegacyBootstrapTest.php 断言)如下:

  • 它是幂等的——调用两次不会抛出异常,且只注册一次。
  • LegacyBootstrap::isRegistered() 会报告它是否已运行。
  • 注册之后,new \TCPDF() 是适配器的一个实例。

这是本页面最重要的一条规则。

enableAliases() 仅在不存在同名类时才注册别名(class_exists($alias, autoload: false))。因此:

  • 如果已安装 tecnickcom/tcpdf,并且其 \TCPDF 先被加载,别名会被静默跳过,你的代码将继续使用旧版 TCPDF,而不是适配器。
  • 不支持在同一进程中同时运行两个库并启用全局别名;这样会导致行为不确定。

迁移期间,优先使用显式的逐文件导入use NextPDF\Compat\Tcpdf\TCPDF;)。它们可以用 grep 检索,也不会产生歧义。一旦严格模式审计通过,就移除 tecnickcom/tcpdf(参见 /integrations/tcpdf-compat/migration/ 阶段 5)。当 \TCPDF 解析为错误的类时,/integrations/tcpdf-compat/troubleshooting/ 中有相应的诊断方法。

该包不附带任何框架容器绑定。如果你要在容器中绑定该门面,请绑定一个工厂,让它为每个文档返回一个全新的 NextPDF\Compat\Tcpdf\TCPDF。文档状态按实例隔离,不得在彼此无关的文档之间共享(参见 /integrations/tcpdf-compat/production-usage/ § 并发)。/integrations/tcpdf-compat/integration/. 中展示了一个典型的绑定。

构造时,适配器按以下顺序解析配置:首先是构造函数参数,其次是任何已存在且由应用自定义的旧版常量,最后是 LegacyDefaults 的 TCPDF 6.2.13 默认值(用于任何尚未定义的常量)。有关完整细节以及现代化的 AdaptationConfig 对象,参见 /integrations/tcpdf-compat/configuration/.

要确认门面已接线且引擎链接已解析,请构造一个适配器并生成一个单页 PDF,然后检查 %PDF 前缀。这与包输出测试断言的是同一行为表面。可运行的检查见 /integrations/tcpdf-compat/install/ § 验证安装。

要在启用别名之前检测真实 TCPDF 冲突,请在你将要调用 enableAliases() 的位置,检查是否已存在全局 \TCPDF。如果存在,某个别名将被跳过,因此请在依赖适配器之前解决冲突(使用显式导入,或移除真实的 TCPDF)。

权威且经过测试验证的覆盖矩阵是仓库内文件 docs/TCPDF_COVERAGE.md。面向读者的摘要(包括静默忽略和未实现方法列表)是 /integrations/tcpdf-compat/method-coverage/. 该包并不宣称自己是“可直接替换的替代品”或“100% TCPDF 兼容”;它是一个 TCPDF 兼容的替代方案,具有已知且经过测试验证的兼容表面,以及已记录的行为差异。

  • docs/TCPDF_COVERAGE.md —— 权威覆盖信息来源(仓库内)
  • /integrations/tcpdf-compat/integration/ —— 将门面接入 application/framework
  • /integrations/tcpdf-compat/method-coverage/ —— 各方法的行为与差距
  • /integrations/tcpdf-compat/migration/ —— 分阶段迁移策略
  • /integrations/tcpdf-compat/troubleshooting/ —— alias/real-TCPDF 冲突诊断
  • tests/Unit/Compat/Tcpdf/LegacyBootstrapTest.php —— 别名行为判定基准