跳转到内容

NextPDF 的设计哲学

Spec: ISO/IEC 25010 Spec: ISO 32000-2 Evidence: Design principle

本页说明用于检验每一项 NextPDF API 决策的原则。它们刻意保持精简,因为一条背不出来的原则,也就是你在压力下用不上的原则。

这是你应该最先阅读的一页。其他 Insider_ 页面会在具体场景中展示这些原则如何运作;本页则先为它们命名,让后续内容更容易理解。

PDF 这项技术足够成熟,理应有明确立场;也足够严格,足以惩罚猜测。一份签名覆盖的,恰好就是它覆盖的那些字节。字体要么已经内嵌,要么没有内嵌。一份归档配置文件要么成立,要么会在数个月后的审计中,在需要证据的人面前失败。

当输入存在歧义时,库必须作出选择。它可以猜测并保持沉默,也可以停下来把问题说清楚。前者在演示时看起来更友好,但也可能让你付出一次生产事故的代价,而且完全没有线索说明哪里出了错。NextPDF 选择后者。它接受一个没那么令人安心的第一印象,以换取一个站得住脚的结果。软件质量标准直接为这项取舍命名。容错安全(Fail-safe) 行为,是指产品在发生失效时能恢复到安全状态,而不是继续处于未定义状态的能力( Spec: ISO/IEC 25010, §3 )。

NextPDF 建立于五项原则之上,按优先级排列:

  1. 明确优于隐含。 如果意图很重要,就要把它说清楚。引擎不会根据上下文推断签名级别、输出模式或符合性目标。
  2. 快速失败、明显失败、提早失败。 无效输入会在写入任何一个字节之前被拒绝,并附带明确点出原因的消息。
  3. 错误是一种 API 接口。 失败具体、有类型,并携带结构化上下文——这是设计出来的,而不是顺带产生的。
  4. 边界要明确说明,而不是等到事后发现。 每一项主张都会说明它在哪里终止。「必要、但不充分」是 NextPDF 有意写明的一句话。
  5. 没有任何东西会悄悄降级。 引擎不会返回一份看起来已经完成、实际只做对一半的产物。

其余一切——流畅式构建器、用后即弃的文档、严格类型——都由这些原则衍生而来。

这些原则不是贴在墙上的口号。它们以具体形态呈现在源代码中,并且彼此相互强化。

下表将每项原则对应到你能在引擎中看到它的地方,以及它缺席时会付出的代价。

原则它在 NextPDF 中如何体现相反做法的代价
明确优于隐含setSignature(certInfo:, level:) 将 PAdES 级别作为必填具名参数——没有「自动」级别一份文档按义务并不要求的配置文件签署,直到验证时才被发现
快速失败、明显失败save() 会在渲染之前拒绝流包装器或含空字节的路径;setSignature() 接着调用 save() 会抛出异常,而不是输出一份未签名文件一次路径遍历写入,或归档库中一份「未签名却被认为已签名」的 PDF
错误是一种 API 接口一个抽象基类异常,搭配具体的有类型子类,每个子类都对外提供结构化的 getContext() 供日志与 APM 使用一段泛用堆栈跟踪,以及一整个下午的猜测
边界明确说明进程内符合性检查会返回发现项,并用文字说明最终裁定属于独立验证器一个「没有异常,所以它一定符合」的结论,却被审计员推翻
没有任何东西会悄悄降级归档时间戳路径宁可拒绝返回一份只写了一半的配置文件,也不会输出一份缺少必要字典的配置文件一份长期验证配置文件悄悄地并不成立

把这些原则合起来看,一个一致立场便会浮现:引擎宁可给你一个诚实的「不行」,也不会给你一个自信满满的「也许」。这不是悲观,而是承认 PDF 往往是一份具备法律效力的产物。一份错误的法律产物,比一份从未生成的法律产物更糟。

本页是一份 Evidence: Design principle 陈述:这些原则是经过审慎权衡的决定,是通过论证得出的,而不是测量得出的。凡是某项原则在外部学科中已有名称,本页都会将它锚定到该名称,让推理不只是内部观点。

  • 「宁可拒绝,也不在未定义状态下继续」这一立场,正是 Spec: ISO/IEC 25010 §3 中的 容错安全(fail-safe) 质量属性——产品在失效时将自身置于安全状态。容错(Fault tolerance) 属于同一族系,指系统在发生故障时仍能按预期行为运作的程度。NextPDF 将这份努力用于检测并停止,而不是掩盖故障。
  • 「采用之前先说明边界」这一立场,即是 适切性可辨识性(appropriateness recognizability) Spec: ISO/IEC 25010, §3.26 ):能够从文档与第一印象判断是否契合需求的能力。
  • 这一切之所以重要,有一个 PDF 专属理由: Spec: ISO 32000-2, §12.8 :一份签名的字节范围所保护的,恰好就是它涵盖的那些字节,除此之外再无其他。因此,一个「热心地」改写或围绕已签名文档猜测的引擎,根本没有提供帮助。

各项原则都在各自页面中,对照真实引擎源代码加以证明——一个拒绝臆测的 API把错误当成一项功能 承载着该 Evidence: Code-backed 证明。本页谈的是「为什么」;那些页面谈的是「是什么」。

几行普通用法就能体现这些原则。签名调用明确陈述了意图。引擎宁可提早拒绝,也不会输出具有误导性的东西。

<?php
declare(strict_types=1);
use NextPDF\Core\Document;
use NextPDF\Exception\NotImplementedException;
use NextPDF\Security\Signature\CertificateInfo;
use NextPDF\Security\Signature\SignatureLevel;
$document = Document::createStandalone();
$document->setTitle('Service Agreement 2026-0042');
$document->addPage();
$document->setFont('helvetica', '', 12);
$document->cell(0, 10, 'This agreement is configured for a PAdES signature.', newLine: true);
// Explicit beats implicit: the PAdES level is a required, named argument.
// There is no inferred or "auto" level.
$document->setSignature(
certInfo: new CertificateInfo(
certificate: $certificatePem,
privateKey: $privateKeyPem,
),
level: SignatureLevel::PAdES_B_B,
);
try {
// Fail fast, no silent degradation: rather than emit an UNSIGNED file
// that the caller believes setSignature() signed, the high-level path
// refuses and names the supported route.
$document->save('/srv/output/agreement.pdf');
} catch (NotImplementedException $e) {
// The message identifies the feature and the follow-up, not a stack
// trace: "... is not implemented in this release. <actionable follow-up>"
error_log($e->getMessage());
}

重点不在签名机制本身,而在于同一段代码片段中可以观察到三项原则:意图被明确陈述(level:)、失败发生得早且原因明确,而引擎拒绝生成一份会对自身状态说谎的文件。

最常见的误读,是以为这些原则会让 NextPDF「更难用」。实际上,它们是在让 NextPDF 更难 用错。一个必填参数,就少一个可能让你措手不及的隐含默认值。一个提早出现的异常,就少一份进入归档库的损坏产物。这份摩擦被刻意放在犯错成本低的地方——调用点、开发阶段——而不是放在代价高昂的地方:生产环境、审计、法庭。

第二种误读,是以为「有主张」意味着「不灵活」。并非如此。引擎对 正确性与意图 有主张,但不替你的文档作主。版面、内容、字体与结构仍完全由你掌控。这些主张只关乎一件事:在猜测会带来不安全的地方,不替你猜测。

本页陈述的是设计意图,本身并不是行为规格。这些原则描述的是决策如何作出,而不是对任何单个方法作出保证。每个方法的确切契约,都存在于参考文档中,以及它各自的 Insider_ 页面中(连同该页的证据级别)。

这些原则也不是绝对的物理定律。它们是优先级,需要结合判断来应用。当两项原则相互冲突时(较严格的拒绝对上较宽容的默认),上述优先级就是裁决依据。某个特定模块仍可记录一个有充分理由的例外。当它这么做时,该例外会被写下来,而不是被默认存在。

最后,「设计原则」在这里是刻意选择的证据基础。本页提供论证,不做基准测试。凡是需要数字、测试或条款支撑的主张,都会在拥有相应证据的页面上提出,而不是在这里提出。

  • 设计原则(证据级别)——页面中的主张来自经过审慎权衡的设计决定,并由意图与佐证标准论证而来,而不是通过基准测试或单一测试测量得出。
  • 容错安全(Fail-safe)——一项软件质量属性:失效时,产品会恢复到安全状态,而不是在未定义状态下继续运行。这正是 NextPDF 宁可拒绝也不猜测的原因。
  • 快速失败(Fail fast)——在最早可能的时点拒绝无效输入,并给出清楚的原因,而不是继续执行、稍后才以晦涩方式失败。
  • PAdES——PDF Advanced Electronic Signatures,是用于签署 PDF 文档的 ETSI 配置文件族系(B-B、B-T、B-LT、B-LTA)。此处在首次使用时展开说明;签署相关页面会深入涵盖。
  • 必要、但不充分——一种刻意措辞,用于说明一项进程内检查是真实信号,但并非符合性裁定;权威决定属于独立验证器。