跳到內容

Config:不可變文件設定

Config 是不可變的文件設定物件。每次變更設定都會透過具型別的 wither 回傳新實例,因此可以在多份文件與多個 worker 之間安全共用。NextPdfConfig 則是獨立的靜態類別,負責保存整個行程共用的 CSS parser 安全限制。

Terminal window
composer require nextpdf/core:^3

這兩個類別都隨 core 套件發行。Config 自 1.7.0 起提供。NextPdfConfig 自 2.2.0 起提供。

Config 是一個 final readonly 類別,並標註 #[DisallowDynamicProperties]。它只持有純量與 final readonly 值物件(value object),因此整個物件圖都是深度不可變的。它沒有通用的 with(string, mixed) setter;每一項變更都有專屬、具型別的 wither。共有 19 個 — 從 withPageSize()withMargins()withCompress() 一直到 withOutputColorProfile() — 每一個都會以具名引數重新建構整個物件。採用具名引數重新建構是刻意的設計。新增建構式參數時,永遠不會悄悄改變既有 wither 中某個位置引數的對應。一個相容性測試會透過反射(reflection)鎖定公開方法清單與參數數量;簽章一旦偏移,就會讓 CI 失敗。

建構式以安全的預設值公開完整設定面:

參數型別預設值
pageSizePageSizePageSize(595.276, 841.890, 'A4')
marginsMarginMargin(10.0, 10.0, 10.0, 10.0)
compressbooltrue
autoPageBreakbooltrue
breakMarginfloat20.0
langstring(BCP-47)''
fontsDirectorystring''
imageCacheBytesint52_428_800
deterministic?DeterministicSettingsnull
cryptoPolicy?CryptoPolicyInterfacenull
branding?BrandingConfignull
degradationPolicyDegradationPolicyBalanced
degradationOverridesarray<string, DegradationPolicy>[]
cssRenderingModeCssRenderingModeNormal
auditCollector?AuditCollectornull
cssFeatureFlags?CssFeatureFlagsnull
cssLayoutModeCssLayoutModeStreaming
retainedNodeBudgetint50_000
layoutTelemetryCollector?LayoutTelemetryCollectornull
telemetryEnabledboolfalse
outputColorProfileOutputColorProfileDeviceRGB

有三項設定帶有數值範圍限制。retainedNodeBudget 接受閉區間 [5_000, 100_000](常數為 RETAINED_NODE_BUDGET_MINRETAINED_NODE_BUDGET_MAX,預設值為 RETAINED_NODE_BUDGET_DEFAULT)。若超出該範圍,withRetainedNodeBudget() 會丟出 InvalidArgumentException。在串流(streaming)模式下,effectiveRetainedNodeBudget() 會回傳 0。否則,它會把值夾限回有效範圍內,作為直接以位置引數建構物件時的縱深防禦檢查。validate() 會強制執行各個單欄位 wither 無法各自察覺的跨欄位不變式。CssLayoutMode::Auto 為保留值,會引發 NotImplementedExceptionCssRenderingMode::SafeCssLayoutMode::Retained 並用時,會引發 IncompatibleRenderingModeExceptionauditCollector 是由呼叫端提供的稽核報告收集器。只有當 cssRenderingModeCssRenderingMode::Audit 時才會被取用;此時它會驅動一個私有的 nextpdfAudit XMP 區塊。在其他渲染模式下則會被忽略。

NextPdfConfig 處理的是另一個面向。它是一個無法實例化的靜態工具類別,負責保存整個行程共用的 CSS parser 限制。這些限制分別是樣式表位元組數上限(預設 512 KB)與 CSS 巢狀深度上限(預設 8)。它們是用來抵禦惡意輸入造成資源耗盡的安全邊界,而非各份文件的偏好設定。正因如此,它們才設計成靜態。setter 會夾限到下限(max(1024, …) 位元組、max(1, …) 深度)。resetDefaults() 標註為 @internal,僅供測試使用。

符號種類主要成員
NextPDF\Core\Configfinal readonly class21 個建構式參數;19 個具型別的 wither;validate()isSafeMode()isRetainedMode()effectiveRetainedNodeBudget()resolveFeatureFlags()
NextPDF\Core\Config::RETAINED_NODE_BUDGET_MINconst int5_000
NextPDF\Core\Config::RETAINED_NODE_BUDGET_MAXconst int100_000
NextPDF\Core\Config::RETAINED_NODE_BUDGET_DEFAULTconst int50_000
NextPDF\Core\NextPdfConfigfinal classsetMaxCssBytes()getMaxCssBytes()setMaxCssNestingDepth()getMaxCssNestingDepth()resetDefaults()@internal

具型別的 wither 方法:withPageSizewithMarginswithCompresswithAutoPageBreakwithLangwithFontsDirectorywithImageCacheByteswithDeterministicwithCryptoPolicywithBrandingwithDegradationPolicywithDegradationOverridewithCssRenderingModewithCssFeatureFlagswithCssLayoutModewithRetainedNodeBudgetwithLayoutTelemetryCollectorwithTelemetryEnabledwithOutputColorProfile

從預設值建立一份設定,並調整其中兩項。

<?php
declare(strict_types=1);
use NextPDF\Core\Config;
use NextPDF\ValueObjects\Margin;
use NextPDF\ValueObjects\PageSize;
$config = (new Config())
->withPageSize(PageSize::Letter())
->withMargins(Margin::uniform(18.0));
// Each wither returns a new instance; the original is unchanged.

建立一份具決定性且已通過驗證的設定,並針對不受信任的輸入,收緊整個行程共用的 CSS 限制。

<?php
declare(strict_types=1);
use NextPDF\Core\Config;
use NextPDF\Core\CssRenderingMode;
use NextPDF\Core\NextPdfConfig;
use NextPDF\ValueObjects\Margin;
use NextPDF\ValueObjects\PageSize;
$config = (new Config())
->withPageSize(PageSize::A4())
->withMargins(Margin::symmetric(vertical: 20.0, horizontal: 15.0))
->withCompress(true)
->withCssRenderingMode(CssRenderingMode::Safe);
// Reject mutually exclusive modes before generation starts.
$config->validate();
// Process-wide hardening for hostile stylesheet input.
NextPdfConfig::setMaxCssBytes(262_144); // 256 KB
NextPdfConfig::setMaxCssNestingDepth(4);
  • Config 在你呼叫 validate() 之前,不會套用任何跨欄位規則。單一 wither 無法自行偵測 Safe + Retained 的衝突;請在產生文件前呼叫 validate()
  • withRetainedNodeBudget() 是唯一會因自身輸入而丟出 InvalidArgumentException 的 wither(當值落在 [5_000, 100_000] 之外時)。
  • effectiveRetainedNodeBudget() 在串流模式下會回傳 0,代表「不套用 Tier-1 預算」,而不是「不允許任何節點」。
  • withLayoutTelemetryCollector()withTelemetryEnabled() 彼此獨立。在未啟用 telemetry 時設定 collector,等於先將它備妥,供日後 canary 使用。啟用 telemetry 但沒有 collector 是合法的,且不會有任何作用。
  • NextPdfConfig 是整個行程共用的靜態設定。一次變更會影響該行程中的每一份文件,直到被重設為止。它是一道安全邊界,而非各份文件的偏好設定。請避免把它放在各個請求的變更路徑上。
  • NextPdfConfig::resetDefaults() 標註為 @internal。請勿在正式環境程式碼中呼叫它。

每個 wither 會配置一個新的 Config,並複製對既有各個值物件的參照。就設定數量而言,這是 O(1),且不會進行深層複製,因為它所持有的每個值本身都是不可變的。NextPdfConfig 的存取子都是靜態欄位讀取,為 O(1)。本參考頁面的 performance_budget 預設為 wall_ms: 1500peak_mb: 64

NextPdfConfig 的存在是為了限制 parser 的資源用量。預設 512 KB 的樣式表上限與深度 8 的巢狀上限,可抵禦異常龐大或巢狀過深 CSS 造成的阻斷服務攻擊。當樣式表來源不受信任時,請同時調低這兩項限制。Config::cryptoPolicy 帶有一個 CryptoPolicyInterface,用於強制執行密碼學政策(例如 FIPS profile)。它預設為 null,並透過 withCryptoPolicy() 設定。Config::deterministic 控制固定的時間戳記與識別碼,以產生位元組完全相同的輸出,也是可重現建置的必要條件。

本模組屬於引擎設定,不引用任何規範性標準。會驅動標準行為的設定 — outputColorProfile(OutputIntent 輸出)、cryptoPolicy(FIPS)、deterministic(可重現建置)— 都由實際輸出它們的各個模組,對照其相關條款記錄說明。

  • /modules/core/document/ — 使用 Config 的模組
  • /modules/core/valueobjects/Config 使用的 PageSizeMargin
  • /modules/core/contracts/CryptoPolicyInterfaceDegradationPolicy
  • /modules/core/event/Config 隨附於 DocumentCreatedEvent
  • /modules/core/exception/InvalidConfigExceptionNotImplementedException

詞彙表:具型別的 wither · 降級政策 · 值物件