跳到內容

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。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