跳到內容

NextPDF 的設計哲學

Spec: ISO/IEC 25010 Spec: ISO 32000-2 Evidence: Design principle

本頁說明每一項 NextPDF API 決策所依循的檢驗原則。它們刻意保持精簡,因為一條你記不住的原則,就是你在壓力下無法套用的原則。

這是你應該最先閱讀的一頁。其他 Insider_ 頁面會在具體場景中展現這些原則如何運作。本頁則先為它們命名,讓後續內容能夠串連起來。

PDF 已足夠成熟,值得擁有明確主張;也足夠嚴格,會懲罰臆測。簽章涵蓋的,恰好就是它所涵蓋的那些位元組。字型要嘛內嵌,要嘛沒有。一份封存設定檔要嘛成立,要嘛在數個月後的稽核中,敗在某位笑不出來的人面前。

當輸入有歧義時,函式庫必須做出選擇。它可以默默臆測,也可以停下來說清楚。前者在展示時看起來比較友善。但它也可能讓你付出一場生產事故的代價,而且沒有留下任何線索可追查問題出在哪裡。NextPDF 選擇後者。它接受第一印象少一點安心感,換取一個站得住腳的結果。軟體品質標準直接為這項取捨命名。容錯安全(Fail-safe) 行為,是指產品在發生失效時能回復到安全狀態,而非繼續處於未定義狀態的能力( Spec: ISO/IEC 25010, §3 )。

NextPDF 建立在五項原則之上,依優先順序排列:

  1. 明確優於隱含。 若意圖很重要,就必須說清楚。引擎不會從上下文推斷簽章層級、輸出模式或符合性目標。
  2. 快速失敗、明顯失敗、提早失敗。 無效輸入會在寫下任何一個位元組之前就被拒絕,並附上明確指出病因的訊息。
  3. 錯誤是一種 API 介面。 失效是具體且有型別的,並會攜帶結構化上下文;這是刻意設計的結果,而不是附帶產物。
  4. 邊界是被陳明的,而非被發現的。 每一項主張都會說明自己的終點在哪裡。「必要、但不充分」是 NextPDF 刻意明說的一句話。
  5. 沒有任何東西會悄悄降級。 引擎不會回傳一份看似完成、實則只對了一半的產物。

其餘的一切——流暢式建構器、用後即棄的文件、嚴格型別——都由這些原則衍生而來。

這些原則不是貼在牆上的海報。它們以具體形式存在於原始碼中,並且彼此強化。

下表將每一項原則對應到你能在引擎中看見它的位置,以及它缺席時要付出的代價。

原則它如何呈現在 NextPDF 中相反做法的代價
明確優於隱含setSignature(certInfo:, level:) 將 PAdES 層級作為必填的具名引數——沒有「自動」層級一份文件以原本不該需要的設定檔簽署,直到驗證時才被發現
快速失敗、明顯失敗save() 會在算繪之前就拒絕串流包裝器或含空位元組的路徑;setSignature() 接著呼叫 save() 會擲出例外,而非產出一份未簽署的檔案一次路徑遍歷寫入,或封存庫中一份「未簽署卻被誤認為已簽署」的 PDF
錯誤是一種 API 介面一個抽象基底例外,搭配具體的有型別子類別;每一個都對外提供結構化的 getContext(),供日誌與 APM 使用一段籠統的堆疊追蹤,加上一整個下午的猜謎
邊界被陳明行程內的符合性檢查會回傳發現項,並在文字中明說最終裁定屬於獨立的驗證器一個「沒有例外,所以它必定符合」的結論,卻被稽核員推翻
沒有任何東西會悄悄降級封存時間戳路徑寧可拒絕回傳一份只寫了一半的設定檔,也不會產出一份缺少必要字典的設定檔一份號稱長期驗證、其實並不是的設定檔

把這些原則合起來讀,可以看出一個一致的立場:引擎寧可給你一個誠實的「不行」,也不要給你一個自信滿滿的「也許」。這不是悲觀,而是理解 PDF 往往是一份具法律效力的產物。錯誤的法律產物,比從未產出的法律產物更糟。

本頁是一份 Evidence: Design principle 陳述:這些原則是經過審慎權衡的決定,是透過論證得出的,而非量測得出的。凡是某項原則在外部學科中已有名稱者,本頁便錨定於該名稱,避免推理只是一家之言。

  • 「寧可拒絕,也不在未定義狀態下繼續」這個立場,正是 Spec: ISO/IEC 25010 §3 中的 容錯安全(fail-safe) 品質屬性:產品在失效時讓自身處於安全狀態。容錯(Fault tolerance) 屬於同一族系,是指系統在發生故障時仍能依預期行為運作的程度。NextPDF 將這份心力用在偵測與停止,而不是掩蓋故障。
  • 「在採用之前先陳明邊界」這個立場,就是 適切性可辨識性(appropriateness recognizability) Spec: ISO/IEC 25010, §3.26 ):能從文件與第一印象判斷契合度的能力。
  • PDF 專屬的理由則是 Spec: ISO 32000-2, §12.8 :一份簽章的位元組範圍所保護的,恰好就是它所涵蓋的那些位元組、再無其他,因此一個「熱心地」改寫或在已簽署文件周邊臆測的引擎,根本幫不上忙。

各項原則都在各自頁面上,對照真實的引擎原始碼加以佐證——一個拒絕臆測的 API把錯誤當成一項功能 承載的就是這類 Evidence: Code-backed 證明。本頁談的是「為何」;那些頁面談的是「是什麼」。

在幾行尋常用法裡,就能看見這些原則。簽章呼叫明確陳述意圖;引擎寧可提早拒絕,也不產出某種具誤導性的東西。

<?php
declare(strict_types=1);
use NextPDF\Core\Document;
use NextPDF\Exception\NotImplementedException;
use NextPDF\Security\Signature\CertificateInfo;
use NextPDF\Security\Signature\SignatureLevel;
$document = Document::createStandalone();
$document->setTitle('Service Agreement 2026-0042');
$document->addPage();
$document->setFont('helvetica', '', 12);
$document->cell(0, 10, 'This agreement is configured for a PAdES signature.', newLine: true);
// Explicit beats implicit: the PAdES level is a required, named argument.
// There is no inferred or "auto" level.
$document->setSignature(
certInfo: new CertificateInfo(
certificate: $certificatePem,
privateKey: $privateKeyPem,
),
level: SignatureLevel::PAdES_B_B,
);
try {
// Fail fast, no silent degradation: rather than emit an UNSIGNED file
// that the caller believes setSignature() signed, the high-level path
// refuses and names the supported route.
$document->save('/srv/output/agreement.pdf');
} catch (NotImplementedException $e) {
// The message identifies the feature and the follow-up, not a stack
// trace: "... is not implemented in this release. <actionable follow-up>"
error_log($e->getMessage());
}

重點不在於簽章機制,而在於你能在同一段程式碼片段中觀察到三項原則:意圖被陳明(level:)、失敗既提早又被點名,而引擎拒絕產出一份會對自身狀態說謊的文件。

最常見的誤讀,是以為這些原則會讓 NextPDF「更難用」。其實它們是讓它更難 用錯。一個必填引數,就是少一個讓你措手不及的隱含預設值。一個提早的例外,就是封存庫中少一份毀損產物。這份摩擦被刻意放在犯錯成本低的地方——呼叫點、開發階段——而不是代價高昂的地方:生產環境、稽核中、法庭上。

第二種誤讀,是以為「有主張」就代表「不靈活」。並非如此。引擎對 正確性與意圖 有主張,但對你的文件沒有。版面、內容、字型與結構仍完全由你掌控。這些主張只關乎一件事:在臆測會不安全之處,不替你臆測。

本頁陳述的是設計意圖,本身並不是行為規格。這些原則描述的是決策如何作成,而非對任何一個方法做出保證。每個方法的確切契約,存在於參考文件中,以及它各自的 Insider_ 頁面(連同該頁的證據層級)。

這些原則也不是絕對的物理定律。它們是優先順序,必須以判斷力套用。當兩項原則相互衝突時(較嚴格的拒絕對上較寬容的預設),上述優先順序就是決勝依據。特定模組仍可記載一個有理據的例外。當它這麼做時,該例外會被寫下來,而不是被假定存在。

最後,「設計原則」在此是刻意作為證據基礎。本頁進行論證,不進行基準測試。凡需要數字、測試或條款支撐的主張,都在具備該證據的頁面上提出,而不是在此處。

  • 設計原則(證據層級)——一種頁面,其主張來自審慎的設計決定,並由意圖與佐證標準論證而來,而非透過基準測試或單一測試量測。
  • 容錯安全(Fail-safe)——一項軟體品質屬性:失效時,產品會回復到安全狀態,而不是在未定義狀態下繼續運作。這正是 NextPDF 寧可拒絕也不臆測的原因。
  • 快速失敗(Fail fast)——在最早可能的時點拒絕無效輸入,並附上清楚的病因,而不是繼續進行,稍後才以晦澀方式失敗。
  • PAdES——PDF Advanced Electronic Signatures,是用於簽署 PDF 文件的 ETSI 設定檔族系(B-B、B-T、B-LT、B-LTA)。此處在首次使用時展開說明;簽署相關頁面會深入涵蓋。
  • 必要、但不充分——一種刻意措辭,用於表示一項行程內檢查是真實訊號,但並不是符合性裁定;具權威性的決定屬於獨立的驗證器。