Support:共享工具 + Clock + Sleeper
Support 模块存放引擎内部使用的横切工具。它还提供一小组对外接口:PSR-20 system clock、warning 收集流程,以及 PDF 序列化 primitives。该命名空间的大部分内容属于内部基础设施。本页记录外部调用方可以依赖的部分,并标明仅供内部使用的部分。
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、一个 WarningSeverity(warning 或 degraded)、页码、元素类型、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 层会共用这些字符串。
BinaryBuffer、PdfOperators,以及 NextPDF\Support\ 中的其他大部分内容,都属于 内部基础设施。writer 层和 drawing 层会使用它们。这里记录它们,是为了完整性和审计。它们并不是受支持的公开 API 的一部分。请改为依赖 Document 外观与 Contracts 命名空间。SystemClock、WarningCollector、Warning、WarningCode、WarningSeverity,以及 DegradationImpact 才是对外公开的成员。
API 接口
标题为“API 接口”的章节| 符号 | 种类 | 可见性 | 主要成员 |
|---|---|---|---|
NextPDF\Support\SystemClock | final class | public | now(): DateTimeImmutable(PSR-20 ClockInterface) |
NextPDF\Support\WarningCollector | final class | public | add()、emit()、addWithPolicy()、getWarnings()、hasWarnings()、hasDegradedParity()、clear() |
NextPDF\Support\Warning | final readonly class | public | $code、$severity、$page、$elementType、$featureId、$degradedParity、$message、$impact、$capabilityId |
NextPDF\Support\WarningCode | string enum | public | 稳定的 NEXTPDF_W_* 识别码 |
NextPDF\Support\WarningSeverity | string enum | public | Warning、Degraded |
NextPDF\Support\DegradationImpact | string enum | public | Cosmetic、LayoutRisk、SemanticLoss、ComplianceRisk、Blocking |
NextPDF\Support\PdfStringEscaper | final readonly class | 内部 | escapeLiteral()、escapeName()(static) |
NextPDF\Support\BinaryBuffer | final class | 内部 | write()、writeStream()、replaceAt()、extract()、enableStreaming()、getContents() |
NextPDF\Support\PdfOperators | final 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 接口对齐。两者返回相同的字节计数。- 请把
PdfStringEscaper、BinaryBuffer,以及PdfOperators都视为内部用途。它们不在公开 API 稳定性承诺的覆盖范围内。
SystemClock::now() 会构建一个对象,O(1)。WarningCollector 的追加操作是摊销 O(1) 的 list push。getWarnings() 返回底层的 list。BinaryBuffer 在 streaming 模式下,会先将内存控制在其 maxmemory 阈值(默认 2 MB)以下,再溢出到磁盘,这样可以让大型文件的峰值内存保持平稳。本参考页的默认 performance_budget 为 wall_ms: 1500、peak_mb: 64。
安全注意事项
标题为“安全注意事项”的章节clock 接口让你可以配合 Config::deterministic 注入一个固定的 clock,从而移除已签章与已加时间戳输出中的墙钟时间非确定性。PdfStringEscaper 是 PDF 字符串与 name 输出唯一可审计的转义器。让所有字符串输出都经过它,可防止用户提供的文本中未转义的括号或分隔字符造成内容注入。warning 可能包含派生自文档的细节(元素类型、feature id)。在把 warning 输出转发到低信任的 log sink 之前,请先清理。
符合性
标题为“符合性”的章节| 规格 | 条款 | 主题 |
|---|---|---|
| PSR-20(PHP-FIG) | psr_20_clock#2.1 | clock 读取操作返回一个不可变的 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/—— 抛出DegradedException的addWithPolicy()/modules/core/contracts/——DegradationPolicy、Capability/modules/core/observability/—— warning 转发与 metrics/modules/core/config/——Config::deterministic与 clock 配合使用/modules/core/writer/—— 内部使用的BinaryBuffer与PdfOperators
术语表:PSR-20 · 降级策略 · value object