跳转到内容

符规验证:进程内预检查配合外部 oracle

本示例先运行 NextPDF 的纯 PHP 进程内符规验证器,将其作为快速的结构性预检查,再把决定性的符规判定交给独立验证器。进程内检查是必要条件,而非充分条件:通过只说明一个结构性事实,并非符规裁决。本示例由 examples/33-validate-conformance.php 及其 tests/Cookbook/Php/ValidateConformanceRecipeTest.php 测试文件支撑。

Terminal window
composer require nextpdf/core:^3

进程内验证器不需要任何外部工具链。决定性的 gate 步骤需要在 PATH 中准备好一个外部验证器。本示例使用 veraPDF。你不需要 Pro 或 Enterprise 包。

NextPDF 在 \NextPDF\Compliance\Validator 下提供进程内验证器。它们会验证特定的规范性不变量,无需执行任何外部进程:

  • PdfRValidator — ISO 23504-1(PDF/R-1)§5/§6 的字节流检查:文件头允许清单、generation-0 对象、§6.5.7 页面内容运算符允许清单(仅限 q/Q/cm/Do),以及 §6.4.3 Info 字典键允许清单。它会返回一个扁平的 PdfRValidationFinding[];空清单表示每一项受控的 §6 检查都通过了。
  • ArlingtonValidator — 以仅报告模式驱动 PDF Association 的机器可读 Arlington 文法:它绝不会充当构建 gate,并会在每一条 finding 上记录已固定的文法提交 SHA,方便审计端对照已知的上游快照。

这些检查的范围有意收窄。它们能发现输出契约与规范之间的偏移,但并不为 PDF/A-4 或 PDF/UA-2 之类的配置文件建立 ISO 符规。这一判定属于独立验证器;它的裁决才应作为构建 gate(ISO 19005-4 §6.7.3 对 PDF/A 明确说明了这一点)。本示例清楚划定边界:先在进程内运行预检查,再打印并执行起决定作用的外部 oracle 命令。

下图展示了这道两阶段 gate。它只受一条规则约束:唯有外部 oracle 的裁决才能报告为符规。

Findings

Clean

Pass

Fail

Produced PDF bytes

In-process pre-check

PdfRValidator / Arlington

Structural drift?

Fail fast — cheap reject

NOT a conformance verdict

Necessary, not sufficient

never report as conformance

Independent external validator

the authoritative oracle

Oracle verdict

May report file conforming

Not conforming — do not ship

Diagram

API 接口由 PHPDoc 生成。以下是主要入口点:

  • \NextPDF\Compliance\Validator\PdfRValidator::validate(string $pdfBytes): list<PdfRValidationFinding>
  • \NextPDF\Compliance\Validator\PdfRValidationFinding(readonly:clauseseveritymessage
  • \NextPDF\Compliance\Validator\ArlingtonValidator::validateReportOnly(string $pdfPath): list<ArlingtonFinding>
  • \NextPDF\Core\Document::output(?string $filename, OutputDestination $dest): string(用 OutputDestination::String 取得原始字节)
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Compliance\Validator\PdfRValidator;
use NextPDF\Contracts\OutputDestination;
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->addPage();
$doc->setFont('helvetica', '', 12);
$doc->cell(0, 10, 'Document under conformance review.', newLine: true);
$bytes = $doc->output(dest: OutputDestination::String);
$findings = (new PdfRValidator())->validate($bytes);
// A finding list is a structural fact, not a conformance verdict.
echo $findings === []
? "No in-process PDF/R-1 findings (necessary, not sufficient).\n"
: count($findings) . " in-process finding(s); not a conformance verdict.\n";

生产环境的调用端会把进程内验证器作为一道低成本 gate,用来在出现明显结构性偏移时快速失败,随后再执行外部 oracle,获得决定性的符规判定。唯有 oracle 的裁决才能报告为符规。

examples/33-validate-conformance.php (gate core)
$bytes = $doc->output(dest: OutputDestination::String);
$doc->save($out);
// 1. In-process pre-check — necessary, not sufficient.
$findings = (new PdfRValidator())->validate($bytes);
foreach ($findings as $finding) {
fwrite(STDERR, sprintf("[%s] §%s — %s\n",
$finding->severity, $finding->clause, $finding->message));
}
// 2. The authoritative gate — the external validator decides.
$exitCode = 0;
$report = [];
exec('verapdf --flavour 4 ' . escapeshellarg($out), $report, $exitCode);
if ($exitCode !== 0) {
fwrite(STDERR, "veraPDF FAILED — not reported conforming\n");
fwrite(STDERR, implode("\n", $report) . "\n");
exit(1);
}
echo "veraPDF PASS — the validator reports the file conforming\n";

php examples/33-validate-conformance.php 运行这个示例。它会构建一份普通 PDF,然后打印进程内 findings。普通 PDF 按预期会产生 PDF/R-1 findings,这个结果正是教学重点。随后,本示例会打印决定性的外部 oracle 命令。

  • 必要而非充分。 PdfRValidator 返回空 finding 清单只表示受控的 §6 检查已通过,仅此而已。它不是 PDF/A-4 或 PDF/UA-2 的符规主张。绝不可仅凭进程内结果报告符规。
  • 普通 PDF 按设计无法通过 PDF/R-1。 PDF/R-1 是仅含图像的栅格配置文件;普通文本 PDF 产生 §6.5.7 与 §6.4.3 findings 是合理且符合预期的。本示例刻意展示这一点,用来说明进程内输出是一项结构性事实,而非裁决。
  • Arlington 是仅报告的。 ArlingtonValidator::validateReportOnly() 绝不会抛出异常,也绝不会充当 gate。在仅文法模式下,它会发出一条 info finding,证明已固定的文法 SHA 已加载;当文法尚未具现化时,它会返回一个空清单。切勿基于它构建 pass/fail gate — 它是一项交叉检查产物。
  • 字节 vs. 文件。 PdfRValidator::validate() 接收原始字节字符串(OutputDestination::String);外部 oracle 则需要一个文件路径。请用 save() 持久化,供 oracle 步骤使用。
  • 空输入。 将空字符串或不含文件头的字符串传给 PdfRValidator::validate() 会返回一条 §6.2.2 错误 finding,而不是抛出异常。请检查 finding 清单;不要假定一定会抛出异常。

进程内验证器会对 PDF 进行单趟正则表达式与字节扫描。对一般文档而言,它们速度快、内存分配负担轻,并保持在 2000 ms / 128 MB 的预算之内。使用外部 oracle 时,它会主导整体耗时,但执行位置在进程外。本示例适用语义可重现性配置文件。本示例的价值在于可观察的验证行为;测试文件通过结构化 AST 与元数据比对来检查该行为。

验证器会在进程内读取文档字节,不会有任何内容离开该进程。然而,外部 oracle 会接收该文件。如果你运行的是托管验证器,文档内容就会离开你的边界。对于敏感内容,请优先使用本机验证器可执行文件,或先脱敏再验证。

Findings 可能引用对象路径与运算符片段。本示例将 findings 写入 STDERR,并将一行固定的进度信息写入 STDOUT。对于敏感文档,请避免让 finding 日志进入共用接收端。绝不可记录原始 PDF 字节。

干净的进程内结果并不代表完整性或真实性。恶意生成端可以制作一份能通过有限范围的进程内检查、却通不过完整验证器的文件,也可以制作一份格式正确但具有误导性的文件。请把进程内通过视为一道快速过滤,绝不可视为信任依据。

本示例不执行任何密码学运算。FIPS 模式不会改变其行为。它不会执行签名、加密,也不会对信任素材做摘要。

陈述规范条款参考 ID
PDF/R-1 的页面内容只使用 q/Q/cm/Do 这些允许的运算符。ISO 23504-1§6.5.7
PDF/R-1 的页面是仅含图像的栅格内容。ISO 23504-1§6.5.5
PDF/R-1 会限制文档信息字典键。ISO 23504-1§6.4.4
Arlington 文法是一项机器可读的对象模型交叉检查。Arlington PDF 模型文法
符规由验证器决定,而非生成端。ISO 19005-4§6.7.3

NextPDF 的进程内验证器会验证特定的规范性不变量。支持不等于符规;验证不等于认证。 干净的进程内结果并不能建立 ISO 符规;这一判定必须由独立验证器(例如 veraPDF)作出。请将它的裁决作为构建 gate。