跳到內容

Exception:型別化例外階層

NextPDF 拋出的每個例外都繼承同一個抽象基底類別 NextPdfException,因此只用一個 catch 就能處理任何引擎錯誤。每個領域例外都實作 ContextAwareExceptionInterface,並提供結構化診斷欄位,讓記錄與 APM 不必剖析訊息字串就能取用。

Terminal window
composer require nextpdf/core:^3

此階層分為三層:

RuntimeException (PHP SPL)
└── NextPdfException (abstract; implements ContextAwareExceptionInterface)
├── InvalidConfigException
├── FontNotFoundException
├── FontParsingException
├── ImageProcessingException
├── SignatureException
├── EncryptionException
├── WriterException
├── PageLayoutException
├── HtmlParsingException
├── CompressionException
├── NotImplementedException
├── … (23 domain exceptions total)
└── Strict\StrictModeViolation (abstract)
├── Strict\IncompatibleRenderingModeException
├── Strict\OracleConformanceFailure
└── Strict\UnregisteredCssDeviation

NextPdfException 繼承自 SPL 的 RuntimeException。攔截 RuntimeException 也會一併攔截 NextPDF 錯誤。攔截 NextPdfException 則會縮小到引擎錯誤。若要進行針對性復原,請攔截特定葉節點類別。Strict\ 子樹會將符合性模式(conformance-mode)的違規歸到抽象的 StrictModeViolation 之下,而後者本身也繼承自 NextPdfException

用情境,而非字串代碼。 NextPDF 透過錯誤的 PHP 型別辨識錯誤,而不是使用字串錯誤代碼。例外類別裡並沒有 NPDF-#### 這類代碼常數。取而代之的是,ContextAwareExceptionInterface::getContext() 會回傳一個 array<string, mixed>,內含 snake_case 的原始型別欄位,可安全地序列化到記錄或 APM payload。NextPdfException::getContext() 預設回傳 []。每個領域例外都會覆寫它,填入與該失敗相關的欄位。舉例來說,FontNotFoundException::getContext() 會回傳 font_namesearch_paths,以及 fallback_attemptedWriterException 會回傳 output_pathwriter_stateInvalidConfigException 會回傳 config_keygiven_value,以及 expected_type。另一組獨立且穩定的 NEXTPDF_W_* 字串識別碼,屬於 Support 模組中非致命的 WarningCode 列舉,並不屬於例外。

可採取的行動。 每個領域例外的類別說明文件都會註明哪一方能對它採取行動——開發者、基礎設施,或函式庫呼叫端。InvalidConfigException 屬於開發者錯誤(請修正組態)。FontNotFoundException 屬於開發者或基礎設施錯誤(請確認路徑或檔案權限)。WriterException 屬於基礎設施錯誤(磁碟、權限、輸出串流)。NotImplementedException 屬於呼叫端錯誤(請移除該呼叫,或鎖定在會實作所指名後續功能的版本)。有數個例外帶有具名建構式,用來精確標示根本原因——例如 SignatureException::ltvCapabilityMissing()::tsaRequired() 等——讓維運人員看到實際原因,而不是泛用訊息。

符號種類主要成員
NextPDF\Contracts\ContextAwareExceptionInterface介面getContext(): array<string, mixed>
NextPDF\Exception\NextPdfException抽象類別繼承 RuntimeExceptiongetContext()(預設 []
NextPDF\Exception\InvalidConfigExceptionfinal 類別getConfigKey()getGivenValue()getExpectedType()getContext()
NextPDF\Exception\FontNotFoundExceptionfinal 類別getFontName()getSearchPaths()wasFallbackAttempted()getContext()
NextPDF\Exception\SignatureExceptionfinal 類別getCertInfo()getSignatureLevel()getDetail()getContext();具名建構式 ltvCapabilityMissing()tsaRequired()httpClientMissing(),…
NextPDF\Exception\WriterExceptionfinal 類別getOutputPath()getWriterState()getContext()
NextPDF\Exception\PageLayoutExceptionfinal 類別getPageNumber()getContext()
NextPDF\Exception\NotImplementedExceptionfinal 類別$feature$followUp
NextPDF\Exception\Strict\StrictModeViolation抽象繼承 NextPdfException
NextPDF\Exception\Strict\IncompatibleRenderingModeExceptionfinal 類別繼承 StrictModeViolation

完整葉節點清單(23 個)如下:BarcodeEncoderNotFoundExceptionBarcodeExceptionCompressionExceptionContentStreamBalanceExceptionCssParserLimitExceededExceptionCssResolutionBudgetExceededExceptionEncryptionExceptionFontNotFoundExceptionFontParsingExceptionGraphicsStateBalanceExceptionHtmlParsingExceptionImageProcessingExceptionInvalidConfigExceptionLinearizationInvariantExceptionLinearizationUnimplementedExceptionMissingShadingResourceExceptionNotImplementedExceptionPageLayoutExceptionPdfRViolationExceptionSignatureExceptionTemplateExceptionUnsupportedAlgorithmException,以及 WriterException

使用基底型別攔截所有引擎錯誤。

<?php
declare(strict_types=1);
use NextPDF\Core\Document;
use NextPDF\Exception\NextPdfException;
try {
$doc = Document::createStandalone();
$doc->addPage();
$doc->setFont('helvetica', '', 12);
$doc->cell(0, 10, 'Hello');
$doc->save('out.pdf');
} catch (NextPdfException $e) {
\error_log($e->getMessage());
}

這個模式可在 examples/15-exception-handling.php 看到實際示範。

在葉節點層級復原,並將結構化情境轉送到記錄管線。

<?php
declare(strict_types=1);
use NextPDF\Contracts\ContextAwareExceptionInterface;
use NextPDF\Core\Document;
use NextPDF\Exception\FontNotFoundException;
use NextPDF\Exception\NextPdfException;
use Psr\Log\LoggerInterface;
function render(Document $doc, LoggerInterface $logger): void
{
try {
$doc->setFont('Brand-Sans', '', 12);
$doc->cell(0, 10, 'Invoice');
$doc->save('invoice.pdf');
} catch (FontNotFoundException $e) {
// Targeted recovery: fall back to a built-in font.
$logger->warning($e->getMessage(), $e->getContext());
$doc->setFont('helvetica', '', 12);
$doc->save('invoice.pdf');
} catch (NextPdfException $e) {
// Any other engine error: structured context, then rethrow.
$context = $e instanceof ContextAwareExceptionInterface
? $e->getContext()
: [];
$logger->error($e->getMessage(), $context);
throw $e;
}
}
  • 攔截順序很重要。請把葉節點類別列在 NextPdfException 之前。一開始就使用 catch (NextPdfException) 會吞掉每一個子類別。
  • getContext() 的鍵為 snake_case,值為原始型別或原始型別清單——沒有巢狀物件——因此這份 payload 一律可安全轉成 JSON。
  • 基底類別的 NextPdfException::getContext() 會回傳 []。未覆寫它的子類別不帶任何結構化欄位。此時請改用 getMessage()
  • NextPdfException 是抽象類別——你無法直接將它實例化。請拋出一個具體的葉節點類別。
  • NotImplementedException 是刻意設計為醒目的訊號:它代表的是有意未實作的功能,而不是暫時性失敗。請勿重試它。
  • Strict\* 違規代表的是符合性模式(conformance-mode)的合約違反,而不是可復原的執行階段錯誤。請將它們視為組態或輸入上的缺陷來處理。
  • 沒有字串錯誤代碼常數。請使用例外型別進行比對。請將 getContext() 轉送給機器端消費者。

建構例外只需要一次物件配置,以及一次用來建立訊息的 sprintf——複雜度為 O(1)。getContext() 會回傳由既有欄位組成的小型關聯陣列,相對於欄位數量也是 O(1)。例外屬於失敗路徑,而不是熱路徑。相較於失敗的那項工作,這點成本微不足道。本參考頁面預設的 performance_budgetwall_ms: 1500peak_mb: 64

情境欄位可能包含來自文件的細節:FontNotFoundException 含有檔案系統的搜尋路徑、WriterException 含有輸出路徑、InvalidConfigException 含有所提供的值。在轉送到低信任的記錄輸出端之前,請先清除情境鍵,或改用允許清單,因為這些路徑與值可能洩漏部署組態或使用者輸入。例外訊息供人閱讀,可能包含相同細節——在涉及安全性的情境下,請勿把原始訊息直接呈現給終端使用者。SignatureException 會刻意把具體的根本原因(缺少套件、空的 TSA URL)寫入訊息,讓維運人員不必逐一搜尋呼叫位置就能分類處理。這些細節面向維運人員,而不是終端使用者。

本模組描述引擎的錯誤模型,不含任何規範性標準引用。因標準違規而拋出的例外——例如 PdfRViolationExceptionStrict\OracleConformanceFailure——相關規範條款請參考偵測該違規的模組,而不是本頁。

  • /modules/core/contracts/ —— ContextAwareExceptionInterface 的定義
  • /modules/core/observability/ —— 將 getContext() 轉送到 APM
  • /modules/core/config/ —— InvalidConfigExceptionNotImplementedException
  • /modules/core/support/ —— DegradedExceptionWarningCodeNEXTPDF_W_*
  • /modules/core/event/ —— InvalidConfigException,源自 addListener()

詞彙表:具情境感知的例外 · 降級政策