Zum Inhalt springen

Fehler mit der NextPDF-Ausnahmehierarchie behandeln

NextPDF wirft bei Ausnahmezuständen typisierte Ausnahmen. Es versteckt Fehler nie hinter einem Rückgabewert false oder null. Jede Domänenausnahme erweitert eine abstrakte Basisklasse, NextPdfException, und stellt strukturierten Diagnosekontext über ContextAwareExceptionInterface bereit. Diese Anleitung zeigt Ihnen, wie Sie in der richtigen Granularität abfangen und den strukturierten Kontext für eine APM-Pipeline (Application Performance Monitoring) loggen. Sie weist außerdem darauf hin, welche Fehler ein einzelner pauschaler Catch-Block nicht abdeckt.

Terminal-Fenster
composer require nextpdf/core:^3

Eine zusätzliche Extension ist nicht erforderlich.

Die Hierarchie sieht so aus:

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\OracleConformanceFailure

Diese Hierarchie hat zwei praktische Konsequenzen, die beide anhand des Quellcodes geprüft wurden:

  1. catch (NextPdfException $e) fängt jede Ausnahme unter NextPDF\Exception ab, einschließlich der Strict-Mode-Verstöße. Sie alle erweitern die abstrakte Basis.
  2. Es fängt nicht alles ab, was die Bibliothek werfen kann. NextPDF\Support\DegradedException erweitert RuntimeException direkt, nicht NextPdfException. Ein catch (NextPdfException $e) fängt eine Ablehnung durch die Degradation-Policy also nicht ab. Um diesen Fall zu behandeln, fangen Sie DegradedException (oder das breitere RuntimeException) explizit ab. Diese Anleitung benennt diese Grenze ausdrücklich, statt vorzugeben, ein einzelner pauschaler Catch-Block decke alles ab.

NextPdfException::getContext() gibt ein array<string, mixed> mit snake_case-Schlüsseln und ausschließlich primitiven Werten (oder Listen von Primitiven) zurück, sodass es sich direkt sicher in das Kontext-Array eines PSR-3-Loggers serialisieren lässt. PSR-3 §1.3 legt eine Ausnahme unter den Kontext-Schlüssel 'exception' ab. Die Methode getContext() von NextPDF ergänzt daneben Domänendetails, nicht das Ausnahmeobjekt selbst.

Diese API-Oberfläche wird aus dem PHPDoc von NextPDF\Exception\NextPdfException, NextPDF\Contracts\ContextAwareExceptionInterface, den konkreten Domänenausnahmen (zum Beispiel NextPDF\Exception\FontNotFoundException, mit getFontName() / getSearchPaths() / wasFallbackAttempted()) und NextPDF\Support\DegradedException (die Capability und DegradationPolicy trägt) generiert. Die unten verwendeten Member sind NextPdfException::getContext() und die ausnahmespezifischen Accessor-Methoden.

<?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());
}

Das vollständige Beispiel zeigt granulares Abfangen, das Logging strukturierten Kontextes und die Grenze der DegradedException. Es respektiert den Ausgabekanal des 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 bleibt für das Harness frei; das PDF geht ausschließlich an NEXTPDF_COOKBOOK_OUTPUT.

  • Ordnen Sie Catch-Blöcke vom Spezifischen zum Allgemeinen → an. PHP trifft den ersten kompatiblen catch. Ein catch (NextPdfException $e), der vor catch (FontNotFoundException $e) steht, macht den spezifischen Block zu totem Code.
  • DegradedException ist keine NextPdfException. Anhand des Quellcodes geprüft, erweitert sie RuntimeException. Ein einzelnes catch (NextPdfException $e) lässt eine Strict-Degradation-Ablehnung ungefangen durchlaufen. Fangen Sie sie (oder RuntimeException) explizit ab, wenn eine Degradation-Policy aktiv ist.
  • getContext() ist laut Vertrag APM-sicher. Schlüssel sind snake_case. Werte sind Primitive oder Listen von Primitiven, ohne verschachtelte Objekte und ohne Ressourcen. Sie können es direkt serialisieren, und es enthält nie Dokument-Bytes.
  • Parsen Sie keine Ausnahme-Nachrichten. Nachrichten sind menschenlesbar und können sich ändern. Die typisierten Accessoren (getFontName(), capability->id und so weiter) und getContext() sind die stabile maschinelle Oberfläche.
  • Vorsicht bei veralteten Anzahlen. Älteres Material nennt womöglich eine feste „N Domänen-Ausnahmen“-Zahl. Die Hierarchie wächst über die Releases hinweg. Verlassen Sie sich auf den Basistyp NextPdfException und instanceof, nie auf eine fest codierte Anzahl.
  • PSR-3-Placeholder (Platzhalter) bleiben Strings. Halten Sie die Nachricht beim Loggen als String mit {placeholder}-Tokens und legen Sie die Werte in das Kontext-Array (PSR-3 §1.2). Interpolieren Sie das Ausnahmeobjekt nicht in die Nachricht.

Die Ausnahmebehandlung verursacht im Normalbetrieb keine Kosten. NextPDF wirft nur bei Ausnahmezuständen, und getContext() baut bei Bedarf ein kleines Array auf. Das performance_budget (wall_ms: 2000, peak_mb: 96) begrenzt den Harness-Lauf dieser Anleitung, nicht beliebige Dokumente.

  • getContext() ist so entworfen, dass es für Logs sicher ist: nur Primitive, kein Dokument-Payload, keine Datei-Bytes. Für Werte, die Sie einem Log-Kontext hinzufügen, sind Sie dennoch selbst verantwortlich. Bereinigen Sie alles, was Benutzer liefern (zum Beispiel einen Dateipfad), gemäß Ihrer Logging-Policy, bevor es einen Sink erreicht.
  • Geben Sie rohe Ausnahme-Nachrichten nicht so an Endnutzer aus, dass sie das Dateisystem-Layout preisgeben. Zeigen Sie eine generische Nachricht an und loggen Sie den strukturierten Kontext serverseitig.
AussageSpezifikationAbschnittreference_id
Eine Ausnahme gehört im PSR-3-Log-Kontext unter den Schlüssel exception.PSR-3§1.3
Log-Nachrichten bleiben Strings; Placeholder-Namen werden auf Kontext-Schlüssel abgebildet.PSR-3§1.2
Das Änderungsdatum wird bei jedem Speichern neu erzeugt, sodass die Ausgabe strukturell (nicht bytegenau) stabil ist.ISO 32000-2§14.3

Diese Anleitung wird mit dem strukturellen Reproduzierbarkeitsprofil geprüft. Die Ausgabe trägt ein Trailer-/ID und ein Änderungsdatum, die bei jedem Speichern neu erzeugt werden, sodass Byteidentität nicht erreichbar ist. Die mit qpdf normalisierte Struktur ist stabil.