跳转到内容

扩展开发:公开 SPI 概览

NextPDF 对外开放一组精简且经过刻意设计的公开契约,全部位于 NextPDF\ContractsNextPDF\Event 这两个命名空间(namespace)中。实现这些契约后,你就能添加字体、拦截文本、观察文档生命周期,或提供自己的签名后端(backend),而无需 fork 整个引擎。

Terminal window
composer require nextpdf/core:^3

NextPDF 将其**公开服务提供方接口(SPI)**与内部代码分离。SPI 由一组类型构成,供你实现或观察。其余内容均为私有,可能在不另行通知的情况下变动。

公开 SPI 有三种形态:

  • **注册表契约。**这是进程生命周期级别的服务,你需要在创建文档之前完成配置;FontRegistryInterfaceImageRegistryInterface 是主要例子。你负责注册这些资源,引擎随后读取它们。
  • **策略契约。**这是引擎在渲染过程中调用的一次性任务钩子。TextPreprocessorInterface 负责布局阶段的文本拦截,而 HtmlSecurityPolicyInterface 用于管控 HTML 功能。你提供行为,引擎负责驱动它。
  • **签名契约。**这是密码学后端(backend)。SignerInterfaceHsmSignerInterfaceDeferredSignerInterface 让你自行提供密钥托管与签名生成能力。引擎负责构建 CMS 结构,而你的代码持有密钥。

观察能力则由 NextPDF\Event 中一套独立、兼容于 PSR-14 的事件系统负责。生命周期事件让你能对文档创建、新页面、字体加载、签名与写出做出响应。它们不会改变引擎的行为。

每个契约在其源代码的 PHPDoc 中都带有一个 @stability 标签:stable(稳定)、experimental(实验性)或 deprecated(已弃用)。这个标签会结合每个契约各自的向后兼容承诺,说明你应预期多大程度的变动。完整策略请见 SPI 稳定性规则一节。

功能公开契约稳定性
字体注册与查找NextPDF\Contracts\FontRegistryInterface稳定(自 1.7.0 起)
图像缓存与解码NextPDF\Contracts\ImageRegistryInterface稳定(自 2.0.0 起)
布局阶段的文本拦截NextPDF\Contracts\TextPreprocessorInterface稳定(自 1.9.0 起)
HTML 功能管控NextPDF\Contracts\HtmlSecurityPolicyInterface稳定(自 3.1.0 起)
文档工厂装配NextPDF\Contracts\DocumentFactoryInterface稳定(自 1.7.0 起)
同步签名NextPDF\Contracts\SignerInterface稳定(自 1.0.0 起)
硬件后端签名NextPDF\Contracts\HsmSignerInterface稳定(自 1.0.0 起)
延后签名与批量签名NextPDF\Contracts\DeferredSignerInterface实验性(自 3.0.0 起)
RFC 3161 时间戳NextPDF\Contracts\TimestampProviderInterface实验性(自 3.0.0 起)
生命周期观察NextPDF\Event\*(兼容于 PSR-14)调度器稳定;载荷为实验性

以下内容均属于内部。请勿导入、继承或依赖它们:

  • 任何位于 NextPDF\ContractsNextPDF\Event 命名空间以外的类,除非其 PHPDoc 带有 @stability 标签。
  • 具体的引擎代码,例如 HTML 解析器、写出器、布局流水线与字体子集化器。
  • NextPDF Pro 与 NextPDF Enterprise 包。它们的内部类并不属于开源的对外范围。当付费版本随附一份 SPI 实现时,你使用的是公开契约,而不是它的内部类型。

最具权威性的清单是自动生成的契约对照表。它会在每次发布时从源代码重新生成。请把每个接口文件中的 @stability PHPDoc 标签当作唯一可信来源。上表仅用于辅助阅读。

先注册字体,再观察它何时加载。这两个步骤都只使用公开类型。

<?php
declare(strict_types=1);
use NextPDF\Contracts\FontRegistryInterface;
use NextPDF\Event\Content\FontLoadedEvent;
use NextPDF\Event\EventDispatcher;
use NextPDF\Event\ListenerProvider;
/** @var FontRegistryInterface $fonts */
$fonts->register('/srv/fonts/Inter-Regular.ttf', 'Inter');
$listeners = new ListenerProvider();
$listeners->addListener(
FontLoadedEvent::class,
static function (FontLoadedEvent $event): void {
\error_log("Font loaded: {$event->family} {$event->style}");
},
);
$dispatcher = new EventDispatcher($listeners);

对于长时间运行的 worker(工作进程),请在启动时一次性创建并锁定这些注册表,再通过文档工厂注入共享的调度器。

<?php
declare(strict_types=1);
use NextPDF\Contracts\DocumentFactoryInterface;
use NextPDF\Contracts\FontRegistryInterface;
use NextPDF\Event\EventDispatcher;
use NextPDF\Event\ListenerProvider;
use Psr\Log\LoggerInterface;
final class DocumentBootstrap
{
public function __construct(
private readonly FontRegistryInterface $fonts,
private readonly DocumentFactoryInterface $factory,
private readonly LoggerInterface $logger,
) {}
public function warmup(): EventDispatcher
{
$this->fonts->warmup([
'/srv/fonts/Inter-Regular.ttf',
'/srv/fonts/Inter-Bold.ttf',
]);
$this->fonts->lock();
$listeners = new ListenerProvider();
$listeners->addListener(
\NextPDF\Event\Security\SignatureAppliedEvent::class,
fn (object $event): mixed => $this->logger->info('Signature applied'),
);
return new EventDispatcher($listeners);
}
}
  • **注册表锁定。**在调用 FontRegistryInterface::lock() 之后,会变更状态的方法会抛出 LogicException。请仅在预热完成后锁定。
  • **稳定性不一致。**标注为 experimental 的契约可能在次要版本中变动。在生产环境依赖某个契约之前,请先确认它声明的稳定性。
  • **命名空间纪律。**位于 NextPDF\ContractsNextPDF\Event 以外、且没有 @stability 标签的类型,都属于内部。即使它在技术上是 public,这一点仍然成立。

SPI 未使用时不会产生额外成本。当某个事件类没有绑定任何监听器时,事件调度器会在单次 hasListeners() 检查后立即返回。注册表只保存纯 PHP 数据,而且支持启动时预热,以分摊首次请求的延迟。

签名契约属于安全敏感的接口范围。HsmSignerInterface 要求私钥绝不离开硬件边界。你的实现必须满足这一要求。关于第三方签名后端契约及其威胁模型,请见 KMS 提供方契约一节。

本总览页面不作任何规范性主张。各契约的符合性(PAdES、密钥管理)记录在相关的 SPI 页面中。

NextPDF Pro 与 NextPDF Enterprise 为数项签名与验证契约提供生产环境级别的实现,包括以密钥管理系统为后端的签名。你依赖的是公开契约,具体实现由这些版本提供,因此你的代码能在各版本之间保持可移植。

词汇表定义了 SPI扩展点稳定性标签向后兼容承诺;正式定义请见已发布的词汇表。