跳转到内容

Support:共享工具 + Clock + Sleeper

Support 模块存放引擎内部使用的横切工具。它还提供一小组对外接口:PSR-20 system clock、warning 收集流程,以及 PDF 序列化 primitives。该命名空间的大部分内容属于内部基础设施。本页记录外部调用方可以依赖的部分,并标明仅供内部使用的部分。

Terminal window
composer require nextpdf/core:^3

Clock(PSR-20)。 SystemClock 实现 Psr\Clock\ClockInterface。它的 now()DateTimeImmutable 形式返回当前时间。PSR-20 模型定义了一个 clock 接口,其中只有一个读取操作。该操作以不可变的 date-time 值返回当前时间(PSR-20 psr_20_clock#2.1)。SystemClock 是默认实现。未注入任何 clock 时,引擎会使用它。测试中如需固定时间,请改为注入一个 frozen clock。它必须实现相同的接口。将 clock 与 Config::deterministic 配合使用,即可得到完全相同的输出。

Warning 流程。 WarningCollector 是非致命渲染问题在内存中的主要传递通道。每次发生确定性降级时,引擎都会追加一个 Warning。例如表格列被压缩、font 无法 resolve(解析),或缺少 glyph。调用方在生成后通过 Document::getWarnings() 读取它们。Warning 是不可变的 value object。它包含一个 WarningCode、一个 WarningSeveritywarningdegraded)、页码、元素类型、feature id、降级对等标志、消息、一个 DegradationImpact,以及一个可选的 capability id。WarningCode 是一个底层为字符串、由稳定标识符组成的 enum。它们以 NEXTPDF_W_ 为前缀(例如 NEXTPDF_W_FONT_UNRESOLVED)。该前缀便于在测试中安全比对。addWithPolicy() 会强制应用当前生效的 DegradationPolicy。在 strict 策略下,compliance、semantic 或 blocking 影响会抛出 DegradedException。在 balanced 策略下,只有 blocking 影响会抛出异常。permissive 策略永远不会抛出异常。

PDF primitives。 PdfStringEscaper 是 PDF 字符串与 name 转义的唯一事实来源。escapeLiteral() 会对 PDF 字面字符串所需转义的字符进行转义(反斜杠、括号、CR、LF、HT、BS、FF),并移除 NUL。escapeName() 会把可打印 ASCII 范围以外的字节,以及 PDF 分隔字符集合,以十六进制编码成 name 对象。BinaryBuffer 是面向写入优化的二进制累加器,用来构建 PDF 对象与 stream。在 streaming 模式下,它会把大型文件溢出到 php://temp 所控制的临时存储。它也支持 signature 嵌入所需的 byte-range 操作。PdfOperators 存放 content-stream 运算符的格式字符串(path、text、graphics-state、font)。drawing 层和 parser 层会共用这些字符串。

BinaryBufferPdfOperators,以及 NextPDF\Support\ 中的其他大部分内容,都属于 内部基础设施。writer 层和 drawing 层会使用它们。这里记录它们,是为了完整性和审计。它们并不是受支持的公开 API 的一部分。请改为依赖 Document 外观与 Contracts 命名空间。SystemClockWarningCollectorWarningWarningCodeWarningSeverity,以及 DegradationImpact 才是对外公开的成员。

符号种类可见性主要成员
NextPDF\Support\SystemClockfinal classpublicnow(): DateTimeImmutable(PSR-20 ClockInterface
NextPDF\Support\WarningCollectorfinal classpublicadd()emit()addWithPolicy()getWarnings()hasWarnings()hasDegradedParity()clear()
NextPDF\Support\Warningfinal readonly classpublic$code$severity$page$elementType$featureId$degradedParity$message$impact$capabilityId
NextPDF\Support\WarningCodestring enumpublic稳定的 NEXTPDF_W_* 识别码
NextPDF\Support\WarningSeveritystring enumpublicWarningDegraded
NextPDF\Support\DegradationImpactstring enumpublicCosmeticLayoutRiskSemanticLossComplianceRiskBlocking
NextPDF\Support\PdfStringEscaperfinal readonly class内部escapeLiteral()escapeName()(static)
NextPDF\Support\BinaryBufferfinal class内部write()writeStream()replaceAt()extract()enableStreaming()getContents()
NextPDF\Support\PdfOperatorsfinal class内部content-stream 运算符格式字符串常量

生成后读取收集到的 warning。

<?php
declare(strict_types=1);
use NextPDF\Support\WarningCollector;
$collector = new WarningCollector();
// The engine appends warnings during rendering. After generation:
if ($collector->hasWarnings()) {
foreach ($collector->getWarnings() as $warning) {
\printf(
"[%s] page %d: %s\n",
$warning->code->value,
$warning->page,
$warning->message,
);
}
}

注入一个 clock 以获得确定性时间,并将降级对等 warning 作为构建失败处理。

<?php
declare(strict_types=1);
use NextPDF\Support\SystemClock;
use NextPDF\Support\WarningCollector;
use Psr\Clock\ClockInterface;
// Production uses the real system clock.
$clock = new SystemClock();
$now = $clock->now(); // DateTimeImmutable
$epoch = $now->getTimestamp(); // int
// In tests, swap in any ClockInterface that returns a fixed instant
// (the parameter is typed to the PSR-20 interface, not SystemClock).
function buildReport(ClockInterface $clock): \DateTimeImmutable
{
return $clock->now();
}
$collector = new WarningCollector();
// ... run generation ...
if ($collector->hasDegradedParity()) {
throw new \RuntimeException('Output parity degraded; failing the build.');
}
  • SystemClock::now() 每次调用都会返回一个新的 DateTimeImmutable。不要假设两次调用会返回相同的时刻。如需固定时间,请注入一个 frozen clock。
  • WarningCollector 保存在内存中,且每个实例相互独立。这是主要通道。JSON sidecar 和 CLI STDERR 会在输出边界发出,而不是由 collector 本身发出。
  • addWithPolicy() 在 strict 策略下可能于渲染中途抛出 DegradedException。如果你希望保留部分输出,请在生成边界处拦截它。
  • WarningCode 的值是稳定字符串——请比对 enum case,而不是消息文本,因为消息文本面向人阅读,可能会变化。
  • BinaryBuffer::getLength()getOffset() 有意设置的别名,用于与 stream 接口对齐。两者返回相同的字节计数。
  • 请把 PdfStringEscaperBinaryBuffer,以及 PdfOperators 都视为内部用途。它们不在公开 API 稳定性承诺的覆盖范围内。

SystemClock::now() 会构建一个对象,O(1)。WarningCollector 的追加操作是摊销 O(1) 的 list push。getWarnings() 返回底层的 list。BinaryBuffer 在 streaming 模式下,会先将内存控制在其 maxmemory 阈值(默认 2 MB)以下,再溢出到磁盘,这样可以让大型文件的峰值内存保持平稳。本参考页的默认 performance_budgetwall_ms: 1500peak_mb: 64

clock 接口让你可以配合 Config::deterministic 注入一个固定的 clock,从而移除已签章与已加时间戳输出中的墙钟时间非确定性。PdfStringEscaper 是 PDF 字符串与 name 输出唯一可审计的转义器。让所有字符串输出都经过它,可防止用户提供的文本中未转义的括号或分隔字符造成内容注入。warning 可能包含派生自文档的细节(元素类型、feature id)。在把 warning 输出转发到低信任的 log sink 之前,请先清理。

规格条款主题
PSR-20(PHP-FIG)psr_20_clock#2.1clock 读取操作返回一个不可变的 date-time
ISO 32000-2:2020§7.3.4.2 / §7.3.5字面字符串与 name 对象的转义(为改写版;未引用 ISO 原文,未固定任何 chunk)

SystemClock 实现 PSR-20 ClockInterface。该转义器遵循 PDF 字面字符串与 name 对象的字符规则。ISO 文字依本站引用策略为改写版,且未固定任何 verbatim chunk。

  • /modules/core/exception/ —— 抛出 DegradedExceptionaddWithPolicy()
  • /modules/core/contracts/ —— DegradationPolicyCapability
  • /modules/core/observability/ —— warning 转发与 metrics
  • /modules/core/config/ —— Config::deterministic 与 clock 配合使用
  • /modules/core/writer/ —— 内部使用的 BinaryBufferPdfOperators

术语表:PSR-20 · 降级策略 · value object