契约:41 个公开接口(SPI)
NextPDF\Contracts 是公开的 service-provider interface(SPI,服务提供者接口):src/Contracts/ 下共有 41 个接口与枚举,每个都带有明确的 @stability 标记和向后兼容承诺。扩展包、Framework(框架)桥接器,以及 Pro 与 Enterprise 版本都面向这些类型编写代码,绝不直接面向具体类。
composer require nextpdf/core:^3概念总览
标题为“概念总览”的章节引擎将两个接口层分开。src/Core/、src/Html/ 与 src/Writer/ 下的具体类不附带任何兼容性承诺。它们可能在各个次要版本之间自由变动。Contracts 命名空间则恰好相反。它是一组精选类型,其签名会按各自声明的稳定性层级冻结。引擎之外的一切都只依赖这个命名空间,不再越过它访问更深层实现。这包括 Laravel、Symfony 与 CodeIgniter 桥接器、compat-tcpdf shim(兼容层)、NextPDF Server,以及 Pro 与 Enterprise 版本。
每个契约都会在其 PHPDoc 中声明四个层级之一。stable 契约在次要或补丁版本中不接受任何破坏性变更。新方法只会随默认实现一同加入。experimental 契约可能在次要版本中变更,但会附上弃用通知。deprecated 契约会指明其替代类型。少数类型是仅契约类型,例如 StreamingWriterInterface 与 CursorInterface。这些类型已发布并冻结,但尚未随附任何生产环境实现。
权威的层级清单是 docs/extension-points.json(清单版本 3.0.0,覆盖 Contracts 与 Event 共 67 个已发布的扩展点)。一个机器可验证的测试 tests/Unit/Contracts/StabilityContractTest.php 会读取该清单。它会在五种情况下让构建失败。第一种是清单列出的类型缺失。第二种是反射取得的类型与清单不一致。第三种是 @stability PHPDoc 标记与清单发生偏移。第四种是 src/Contracts/ 下存在契约,却未出现在清单中。第五种是 @internal 类型泄漏到清单中。契约接口层一旦出现偏移,就一定会被检测到。
契约分为九个领域。每个领域都有专属页面:文件构建、签章、条码编码、字体排印、安全策略、提取、可观测性与流式处理。这样的划分对应整合者采用引擎的方式。你会依赖文件契约来生成 PDF。你会依赖签章契约来加入签章。你会依赖安全策略契约来约束不受信任的 HTML。
在整个引擎中,resolve(解析)可选实现都遵循同一种模式。Core 会以 class_exists() 检查具体类是否存在,再按对应契约使用它。LtvManagerInterface 与 PdfAManagerInterface 就是以这种方式解析其 Pro 实现。因此 Core 保持 Apache-2.0 授权,且不会强制依赖商业代码。
API 接口层
标题为“API 接口层”的章节| 契约 | 类型 | 稳定性 | 起始版本 | 领域 |
|---|---|---|---|---|
PdfDocumentInterface | 接口 | 稳定 | 1.0.0 | 文件 |
DocumentFactoryInterface | 接口 | 稳定 | 1.7.0 | 文件 |
ResettableService | 接口 | 稳定 | 1.7.0 | 文件 |
OutputDestination | 枚举 | 稳定 | 1.0.0 | 文件 |
Orientation | 枚举 | 稳定 | 1.0.0 | 文件 |
Alignment | 枚举 | 稳定 | 1.0.0 | 文件 |
SignerInterface | 接口 | 稳定 | 1.0.0 | 签章 |
HsmSignerInterface | 接口 | 稳定 | 1.0.0 | 签章 |
DeferredSignerInterface | 接口 | 实验性 | 3.0.0 | 签章 |
TimestampProviderInterface | 接口 | 实验性 | 3.0.0 | 签章 |
LtvManagerInterface | 接口 | 稳定 | 1.10.0 | 签章 |
CryptoPolicyInterface | 接口 | 稳定 | 1.9.0 | 签章 |
Barcode1DEncoderInterface | 接口 | 稳定 | 1.0.0 | 条码 |
Barcode2DEncoderInterface | 接口 | 稳定 | 1.0.0 | 条码 |
BarcodeEncoderInterface | 接口 | 稳定 | 3.0.0 | 条码 |
Gs1DataParserInterface | 接口 | 稳定 | 1.0.0 | 条码 |
FontRegistryInterface | 接口 | 稳定 | 1.7.0 | 字体排印 |
TextPreprocessorInterface | 接口 | 稳定 | 1.9.0 | 字体排印 |
HtmlSecurityPolicyInterface | 接口 | 稳定 | 3.1.0 | 安全策略 |
ExternalResourcePolicyInterface | 接口 | 稳定 | 4.0.0 | 安全策略 |
InspectorInterface | 接口 | 实验性 | 2.2.0 | 提取 |
EmbeddingServiceInterface | 接口 | 实验性 | 2.1.0 | 提取 |
VectorIndexInterface | 接口 | 实验性 | 2.1.0 | 提取 |
JobNotificationInterface | 接口 | 实验性 | 2.2.0 | 可观测性 |
SpectrumInterface | 接口 | 实验性 | 2.1.0 | 可观测性 |
StreamingWriterInterface | 接口 | 实验性 | 3.1.0 | 流式处理 |
CursorInterface | 接口 | 实验性 | 3.1.0 | 流式处理 |
此表列出主要契约。其余类型——value-object DTO(TextSegment、TextPreprocessResult)、EInvoice 子命名空间、行为枚举(DegradationPolicy、UnderlineStyle),以及导入契约(ImportedFormObjectInterface、EmbeddedPdfObjectInterface、ChromeRenderResultInterface)——都记录在 另见 所列的领域页面中。完整的机器可读清单是 docs/extension-points.json,并同步镜像到 .ai/contracts-map.md。
代码示例——快速上手
标题为“代码示例——快速上手”的章节<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Hello World');$doc->addPage();$doc->setFont('helvetica', '', 24);$doc->cell(0, 15, 'Hello, NextPDF!', newLine: true);$doc->save(__DIR__ . '/output/01-hello-world.pdf');Document::createStandalone() 会返回一个具体的 Document,并满足 PdfDocumentInterface。请在自己的服务中以接口作为类型提示,让引擎内部保持可替换。
代码示例——生产环境
标题为“代码示例——生产环境”的章节<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\DocumentFactory;use NextPDF\Core\PdfFactory;use NextPDF\Graphics\ImageRegistry;use NextPDF\Typography\FontRegistry;
// Created once at process boot in a RoadRunner/Swoole/Octane worker.$fontRegistry = new FontRegistry();$imageRegistry = new ImageRegistry(maxCacheBytes: 50 * 1024 * 1024);$documentFactory = new DocumentFactory($fontRegistry, $imageRegistry);
$factory = PdfFactory::new() ->withCompress(true) ->withDocumentFactory($documentFactory);
for ($request = 1; $request <= 3; $request++) { $doc = $factory->create(); $doc->setTitle("Worker Request #{$request}"); $doc->addPage(); $doc->setFont('helvetica', 'B', 16); $doc->cell(0, 12, "Worker Request #{$request}", newLine: true); $doc->save(__DIR__ . "/output/14-worker-request-{$request}.pdf");}DocumentFactory 实现 DocumentFactoryInterface。它会持有进程生命周期级别的 FontRegistryInterface 与 ImageRegistryInterface 单例,并把它们注入每个可丢弃的 Document,因此一个 worker 在数千个请求中只需解析每个字体一次。
边界情况与陷阱
标题为“边界情况与陷阱”的章节- 仅契约类型可以通过编译,但没有任何运行时支持。
new无法用于StreamingWriterInterface或CursorInterface,因为目前还没有任何类实现它们。请把它们视为预先声明的 API。 - 单个类型的真实来源是
@stabilityPHPDoc 标记。docs/extension-points.json是整组类型的真实来源。当两者不一致时,StabilityContractTest就会失败——不要只改其中一边来掩盖这种不一致。 experimental在实践中并不代表不稳定,而是代表兼容性承诺较弱。在固定依赖某个契约之前,请先阅读其bc_promise字段(位于.ai/contracts-map.md)。- 标示为
@internal的类永远不是契约,即使其他包在技术上能够引用它。稳定性测试会拒绝任何出现在清单中的@internal类型。 - 对
stable接口新增方法,对实现者而言是破坏性变更,除非该方法随附默认实现。引擎通过新增接口来扩展能力,而不是扩展既有接口。
面向 Contracts 编写代码不会增加任何可测量的运行时成本:接口类型提示在链接时解析,而不是每次调用时解析。本页 worker 示例的 performance_budget 为三份文件合计 1500 ms 墙钟时间与 64 MB 峰值内存。第一个请求的字体解析会主导这份预算。后续请求会重用 registry 缓存,使契约相关的工作降到个位数毫秒。成本模型在每次契约分派上为 O(1);真正的工作发生在具体实现中,并记录于各领域页面。
安全注意事项
标题为“安全注意事项”的章节SPI 同时也是一道安全边界。HtmlSecurityPolicyInterface 与 ExternalResourcePolicyInterface 是默认拒绝的契约,会在不受信任的 HTML 抵达 renderer(渲染器)之前约束它能做的事。CryptoPolicyInterface 会把关签章与加密所用的算法和密钥强度选择。因为这些是契约,整合者可以提供更严格的策略,而无需分叉引擎。任何与安全相关的策略都请固定依赖 stable 层级。实验性策略契约可能在各个次要版本之间改变形态。签章与安全策略领域页面会说明完整的威胁模型与规范性参考。
符合性
标题为“符合性”的章节本概览不作任何直接的规范性主张;每个领域页面都会提供自己的 citations 区块。签章契约对应到 ISO 32000-2 §12.8(数字签名)与 ETSI EN 319 142(PAdES 基准)。PDF/A 管理器对应到 ISO 19005-4。关于条款层级的符合性表格,请参阅签章、安全策略与提取页面。
商业背景
标题为“商业背景”的章节Pro 与 Enterprise 版本实现了数个 Core 契约背后的生产环境代码:LtvManagerInterface(长期验证)、PdfAManagerInterface(PDF/A 强制)、Hardware Security Module(HSM,硬件安全模块)与延迟签署者、条码编码器,以及嵌入与向量 Index(索引)契约。Core 发布并冻结接口;Premium 套件则随附实现。这让开源引擎保持 Apache-2.0 授权,同时让商业部署享有可直接替换、且无需变更 API 的升级。
- 契约 / Document——PDF 文件、工厂与 registry 契约。
- 契约 / Signing——signer、HSM、时间戳与 LTV 契约。
- 契约 / Security Policy——加密、HTML 与资源策略契约。
- 契约 / Typography——字体 registry 与文本预处理契约。
- 契约 / Extraction——inspector、PDF/A、嵌入与电子发票契约。
- Core——满足这些契约的具体类。
- Event——与
Contracts一起发布的事件 SPI 对应项。