NextPDF 的設計哲學
Spec: ISO/IEC 25010 ISO/IEC 25010 Spec: ISO 32000-2 ISO 32000-2 Evidence: Design principle
本頁說明每一項 NextPDF API 決策所依循的檢驗原則。它們刻意保持精簡,因為一條你記不住的原則,就是你在壓力下無法套用的原則。
這是你應該最先閱讀的一頁。其他 Insider_ 頁面會在具體場景中展現這些原則如何運作。本頁則先為它們命名,讓後續內容能夠串連起來。
為何這很重要
標題為「為何這很重要」的區段PDF 已足夠成熟,值得擁有明確主張;也足夠嚴格,會懲罰臆測。簽章涵蓋的,恰好就是它所涵蓋的那些位元組。字型要嘛內嵌,要嘛沒有。一份封存設定檔要嘛成立,要嘛在數個月後的稽核中,敗在某位笑不出來的人面前。
當輸入有歧義時,函式庫必須做出選擇。它可以默默臆測,也可以停下來說清楚。前者在展示時看起來比較友善。但它也可能讓你付出一場生產事故的代價,而且沒有留下任何線索可追查問題出在哪裡。NextPDF 選擇後者。它接受第一印象少一點安心感,換取一個站得住腳的結果。軟體品質標準直接為這項取捨命名。容錯安全(Fail-safe) 行為,是指產品在發生失效時能回復到安全狀態,而非繼續處於未定義狀態的能力( Spec: ISO/IEC 25010, §3 ISO/IEC 25010 §3 )。
精簡版
標題為「精簡版」的區段NextPDF 建立在五項原則之上,依優先順序排列:
- 明確優於隱含。 若意圖很重要,就必須說清楚。引擎不會從上下文推斷簽章層級、輸出模式或符合性目標。
- 快速失敗、明顯失敗、提早失敗。 無效輸入會在寫下任何一個位元組之前就被拒絕,並附上明確指出病因的訊息。
- 錯誤是一種 API 介面。 失效是具體且有型別的,並會攜帶結構化上下文;這是刻意設計的結果,而不是附帶產物。
- 邊界是被陳明的,而非被發現的。 每一項主張都會說明自己的終點在哪裡。「必要、但不充分」是 NextPDF 刻意明說的一句話。
- 沒有任何東西會悄悄降級。 引擎不會回傳一份看似完成、實則只對了一半的產物。
其餘的一切——流暢式建構器、用後即棄的文件、嚴格型別——都由這些原則衍生而來。
NextPDF 如何看待它
標題為「NextPDF 如何看待它」的區段這些原則不是貼在牆上的海報。它們以具體形式存在於原始碼中,並且彼此強化。
下表將每一項原則對應到你能在引擎中看見它的位置,以及它缺席時要付出的代價。
| 原則 | 它如何呈現在 NextPDF 中 | 相反做法的代價 |
|---|---|---|
| 明確優於隱含 | setSignature(certInfo:, level:) 將 PAdES 層級作為必填的具名引數——沒有「自動」層級 | 一份文件以原本不該需要的設定檔簽署,直到驗證時才被發現 |
| 快速失敗、明顯失敗 | save() 會在算繪之前就拒絕串流包裝器或含空位元組的路徑;setSignature() 接著呼叫 save() 會擲出例外,而非產出一份未簽署的檔案 | 一次路徑遍歷寫入,或封存庫中一份「未簽署卻被誤認為已簽署」的 PDF |
| 錯誤是一種 API 介面 | 一個抽象基底例外,搭配具體的有型別子類別;每一個都對外提供結構化的 getContext(),供日誌與 APM 使用 | 一段籠統的堆疊追蹤,加上一整個下午的猜謎 |
| 邊界被陳明 | 行程內的符合性檢查會回傳發現項,並在文字中明說最終裁定屬於獨立的驗證器 | 一個「沒有例外,所以它必定符合」的結論,卻被稽核員推翻 |
| 沒有任何東西會悄悄降級 | 封存時間戳路徑寧可拒絕回傳一份只寫了一半的設定檔,也不會產出一份缺少必要字典的設定檔 | 一份號稱長期驗證、其實並不是的設定檔 |
把這些原則合起來讀,可以看出一個一致的立場:引擎寧可給你一個誠實的「不行」,也不要給你一個自信滿滿的「也許」。這不是悲觀,而是理解 PDF 往往是一份具法律效力的產物。錯誤的法律產物,比從未產出的法律產物更糟。
證據怎麼說
標題為「證據怎麼說」的區段本頁是一份 Evidence: Design principle 陳述:這些原則是經過審慎權衡的決定,是透過論證得出的,而非量測得出的。凡是某項原則在外部學科中已有名稱者,本頁便錨定於該名稱,避免推理只是一家之言。
- 「寧可拒絕,也不在未定義狀態下繼續」這個立場,正是 Spec: ISO/IEC 25010 ISO/IEC 25010 §3 中的 容錯安全(fail-safe) 品質屬性:產品在失效時讓自身處於安全狀態。容錯(Fault tolerance) 屬於同一族系,是指系統在發生故障時仍能依預期行為運作的程度。NextPDF 將這份心力用在偵測與停止,而不是掩蓋故障。
- 「在採用之前先陳明邊界」這個立場,就是 適切性可辨識性(appropriateness recognizability)( Spec: ISO/IEC 25010, §3.26 ISO/IEC 25010 §3.26 ):能從文件與第一印象判斷契合度的能力。
- PDF 專屬的理由則是 Spec: ISO 32000-2, §12.8 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_ 頁面(連同該頁的證據層級)。
這些原則也不是絕對的物理定律。它們是優先順序,必須以判斷力套用。當兩項原則相互衝突時(較嚴格的拒絕對上較寬容的預設),上述優先順序就是決勝依據。特定模組仍可記載一個有理據的例外。當它這麼做時,該例外會被寫下來,而不是被假定存在。
最後,「設計原則」在此是刻意作為證據基礎。本頁進行論證,不進行基準測試。凡需要數字、測試或條款支撐的主張,都在具備該證據的頁面上提出,而不是在此處。
相關文件
標題為「相關文件」的區段- 一個拒絕臆測的 API——以真實 API 呈現明確意圖與快速失敗原則。
- 把錯誤當成一項功能——把有型別的例外階層當成刻意設計出的介面。
- PHP 8.4 的基石——讓這些原則得以被強制執行,而不只是寄望成真。
詞彙表
標題為「詞彙表」的區段- 設計原則(證據層級)——一種頁面,其主張來自審慎的設計決定,並由意圖與佐證標準論證而來,而非透過基準測試或單一測試量測。
- 容錯安全(Fail-safe)——一項軟體品質屬性:失效時,產品會回復到安全狀態,而不是在未定義狀態下繼續運作。這正是 NextPDF 寧可拒絕也不臆測的原因。
- 快速失敗(Fail fast)——在最早可能的時點拒絕無效輸入,並附上清楚的病因,而不是繼續進行,稍後才以晦澀方式失敗。
- PAdES——PDF Advanced Electronic Signatures,是用於簽署 PDF 文件的 ETSI 設定檔族系(B-B、B-T、B-LT、B-LTA)。此處在首次使用時展開說明;簽署相關頁面會深入涵蓋。
- 必要、但不充分——一種刻意措辭,用於表示一項行程內檢查是真實訊號,但並不是符合性裁定;具權威性的決定屬於獨立的驗證器。