Gestire gli errori con la gerarchia delle eccezioni di NextPDF
In sintesi
Sezione intitolata “In sintesi”NextPDF solleva eccezioni tipizzate per gli stati eccezionali. Non nasconde mai un errore dietro un valore restituito false o null. Ogni eccezione di dominio estende un’unica classe base astratta, NextPdfException, ed espone un contesto diagnostico strutturato tramite ContextAwareExceptionInterface. Questa ricetta mostra come intercettarle con la granularità corretta e come registrare il contesto strutturato per una pipeline di application performance monitoring (APM). Chiarisce inoltre quali errori il singolo blocco catch generale non copre.
Installazione
Sezione intitolata “Installazione”composer require nextpdf/core:^3Non sono richieste estensioni aggiuntive.
Panoramica concettuale
Sezione intitolata “Panoramica concettuale”La gerarchia è questa:
RuntimeException └── NextPdfException (abstract, implements ContextAwareExceptionInterface) ├── InvalidConfigException ├── FontNotFoundException ├── FontParsingException ├── ImageProcessingException ├── WriterException ├── SignatureException ├── EncryptionException ├── HtmlParsingException ├── … (every domain exception under NextPDF\Exception) └── Strict\StrictModeViolation (abstract) ├── Strict\IncompatibleRenderingModeException └── Strict\OracleConformanceFailureDa questa gerarchia derivano due conseguenze pratiche, entrambe verificate sul codice sorgente:
catch (NextPdfException $e)intercetta ogni eccezione sottoNextPDF\Exception, comprese le violazioni della modalità strict. Tutte estendono la classe base astratta.- Non intercetta tutto ciò che la libreria può generare.
NextPDF\Support\DegradedExceptionestendeRuntimeExceptiondirettamente, nonNextPdfException. Pertanto uncatch (NextPdfException $e)non intercetta un rifiuto dovuto alla policy di degradazione. Per gestire questa condizione, intercettare esplicitamenteDegradedException(o la più genericaRuntimeException). Questa ricetta rende esplicito tale confine, invece di suggerire che un singolo blocco catch generale copra tutto.
NextPdfException::getContext() restituisce un array<string, mixed> con chiavi in snake_case e valori esclusivamente primitivi (o liste di primitivi); è quindi sicuro serializzarlo direttamente nell’array di contesto di un logger PSR-3. PSR-3 §1.3 colloca un’eccezione sotto la chiave di contesto 'exception'. In NextPDF, getContext() affianca a questa chiave i dettagli di dominio, non l’oggetto eccezione stesso.
Superficie API
Sezione intitolata “Superficie API”La superficie API descritta qui deriva dal PHPDoc di NextPDF\Exception\NextPdfException, NextPDF\Contracts\ContextAwareExceptionInterface, delle eccezioni di dominio concrete (per esempio NextPDF\Exception\FontNotFoundException, con getFontName() / getSearchPaths() / wasFallbackAttempted()) e di NextPDF\Support\DegradedException (che trasporta Capability e DegradationPolicy). I membri utilizzati di seguito sono NextPdfException::getContext() e gli accessor specifici di ciascuna eccezione.
Esempio di codice — Avvio rapido
Sezione intitolata “Esempio di codice — Avvio rapido”<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
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(getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/out.pdf');} catch (NextPdfException $e) { // Every NextPDF\Exception\* (and strict-mode violation) lands here. // $e->getContext() is APM-safe structured detail. error_log($e->getMessage());}Esempio di codice — Produzione
Sezione intitolata “Esempio di codice — Produzione”L’esempio completo mostra l’intercettazione granulare, la registrazione del contesto strutturato e il confine di DegradedException. Mantiene il canale di output richiesto dall’harness.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;use NextPDF\Contracts\ContextAwareExceptionInterface;use NextPDF\Exception\FontNotFoundException;use NextPDF\Exception\NextPdfException;use NextPDF\Support\DegradedException;
/** * A minimal PSR-3-shaped sink. In production this is your real logger; * the exception goes under the 'exception' key (PSR-3 §1.3) and the * NextPDF structured context is merged in as domain detail. * * @param array<string, mixed> $context */function logError(string $message, array $context): void{ fwrite(STDERR, $message . ' ' . json_encode($context, JSON_THROW_ON_ERROR) . "\n");}
$doc = Document::createStandalone();$doc->setTitle('Exception handling patterns');
try { $doc->addPage(); $doc->setFont('helvetica', 'B', 16); $doc->cell(0, 12, 'Exception-aware error handling', newLine: true);
// This call succeeds; the catch blocks below show the SHAPE of handling. $doc->setFont('helvetica', '', 11); $doc->cell(0, 8, 'Catch specifically, then fall back to the base.', newLine: true);} catch (FontNotFoundException $e) { // Most specific first: actionable, typed accessors. logError('Font missing — using a fallback face', [ 'exception' => $e::class, 'font_name' => $e->getFontName(), 'searched' => $e->getSearchPaths(), 'fallback' => $e->wasFallbackAttempted(), ]);} catch (NextPdfException $e) { // Catch-all for every NextPDF\Exception\* including strict violations. $context = ['exception' => $e::class]; if ($e instanceof ContextAwareExceptionInterface) { $context += $e->getContext(); } logError($e->getMessage(), $context);} catch (DegradedException $e) { // BOUNDARY: DegradedException extends RuntimeException directly, NOT // NextPdfException. The catch above would NOT have caught it. This // explicit block (or a broader RuntimeException) is required. logError('Capability degraded under the active policy', [ 'exception' => $e::class, 'capability' => $e->capability->id, 'policy' => $e->policy->value, ]);}
$doc->save(getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/out.pdf');
fwrite(STDERR, "Document built; handlers wired.\n");STDOUT rimane libero per l’harness; il PDF viene scritto solo in NEXTPDF_COOKBOOK_OUTPUT.
Casi limite e insidie
Sezione intitolata “Casi limite e insidie”- Ordinare i blocchi catch da specifico → generale. PHP individua la prima clausola
catchcompatibile. Uncatch (NextPdfException $e)posto prima dicatch (FontNotFoundException $e)rende irraggiungibile il blocco specifico. DegradedExceptionnon è unaNextPdfException. Come verificato sul codice sorgente, estendeRuntimeException. Un singolocatch (NextPdfException $e)lascia propagare silenziosamente un rifiuto dovuto alla degradazione strict. Intercettarla esplicitamente (o tramiteRuntimeException) quando è attiva una policy di degradazione.getContext()è sicuro per l’uso con gli APM per contratto. Le chiavi sono in snake_case. I valori sono primitivi o liste di primitivi, senza oggetti annidati e senza risorse. È possibile serializzarlo direttamente e non contiene mai byte del documento.- Non analizzare i messaggi delle eccezioni. I messaggi sono destinati alla lettura umana e possono cambiare. Gli accessor tipizzati (
getFontName(),capability->ide così via) egetContext()costituiscono la superficie stabile per l’elaborazione automatica. - Avvertenza sui conteggi obsoleti. Il materiale più datato potrebbe citare un numero fisso di «N eccezioni di dominio». La gerarchia si amplia da una release all’altra. Affidarsi al tipo base
NextPdfExceptione ainstanceof, mai a un conteggio cablato nel codice. - I segnaposto PSR-3 restano stringhe. In fase di registrazione, mantenere il messaggio come stringa con token
{placeholder}e inserire i valori nell’array di contesto (PSR-3 §1.2). Non interpolare l’oggetto eccezione nel messaggio.
Prestazioni
Sezione intitolata “Prestazioni”La gestione delle eccezioni non introduce costi nel flusso normale. NextPDF genera eccezioni solo negli stati eccezionali e getContext() costruisce un piccolo array su richiesta. Il performance_budget (wall_ms: 2000, peak_mb: 96) delimita l’esecuzione di questa ricetta nell’harness, non documenti arbitrari.
Note sulla sicurezza
Sezione intitolata “Note sulla sicurezza”getContext()è progettato per essere sicuro per il logging: solo primitivi, nessun payload del documento, nessun byte di file. L’utente resta comunque responsabile dei valori che aggiunge a un contesto di log. Ripulire tutto ciò che è fornito dall’utente (un percorso di file, per esempio) in base alla propria policy di logging prima che raggiunga un sink.- Non restituire agli utenti finali i messaggi grezzi delle eccezioni quando potrebbero rivelare la struttura del filesystem. Mostrare un messaggio generico e registrare il contesto strutturato lato server.
Conformità
Sezione intitolata “Conformità”| Dichiarazione | Specifica | Clausola | reference_id |
|---|---|---|---|
Un’eccezione appartiene al contesto di log PSR-3 sotto la chiave exception. | PSR-3 | §1.3 | |
| I messaggi di log restano stringhe; i nomi dei segnaposto corrispondono alle chiavi di contesto. | PSR-3 | §1.2 | |
| La data di modifica viene rigenerata a ogni salvataggio, pertanto l’output è stabile a livello strutturale (non di byte). | ISO 32000-2 | §14.3 |
Questa ricetta è verificata con il profilo di riproducibilità strutturale. L’output contiene un trailer /ID e una data di modifica rigenerati a ogni salvataggio, quindi l’identità a livello di byte non è ottenibile. La struttura normalizzata da qpdf è stabile.