Fehler als Feature
Spec: ISO 9241-110, §5.6.4 ISO 9241-110 §5.6.4 Evidence: Code-backed
Auf einen Blick
Abschnitt betitelt „Auf einen Blick“NextPDF behandelt seine Ausnahmehierarchie als API-Oberfläche, die mit derselben Sorgfalt entworfen ist wie die Methoden, die sie auslösen. Ein Fehler ist spezifisch, typisiert, in der von Ihnen benötigten Granularität abfangbar und trägt strukturierten Kontext für Ihre Logs.
Diese Seite zeigt diese Oberfläche im Quellcode der Engine selbst: den Basistyp, die typisierten Unterklassen, die benannten Konstruktoren, die eine Grundursache mit der Meldung verknüpfen, und den strukturierten Kontext, den jede NextPDF-Ausnahme bereitstellt.
Warum das wichtig ist
Abschnitt betitelt „Warum das wichtig ist“Eine Fehlermeldung ist die Engine, die zum denkbar schlechtesten Zeitpunkt zu Ihnen spricht: in der Produktion, um 2 a.m., und mit einem Dokument, das längst hätte ausgeliefert sein sollen. Was die Meldung in diesem Moment aussagt, entscheidet, ob der nächste Schritt ein Fix oder eine lange Untersuchung ist.
Eine generische RuntimeException: something went wrong lässt Ihnen keinen Ansatzpunkt. Sie sagt Ihnen, dass die Engine fehlgeschlagen ist, aber nicht, was fehlgeschlagen ist, nicht, wo es fehlgeschlagen ist, und schon gar nicht, was zu tun ist. Die Leitlinien zu menschlichen Faktoren sind in diesem Punkt eindeutig. Ein Fehler sollte sich gut genug selbst erklären, sodass seine Behebung der naheliegende nächste Schritt ist und kein Forschungsprojekt ( Spec: ISO 9241-110, §5.6.4.3 ISO 9241-110 §5.6.4.3 ). Eine Ausnahme, die Ursache und Abhilfe benennt, ist keine Nettigkeit. Sie ist der Unterschied zwischen einem Fünf-Minuten-Fix und einem Fünf-Stunden-Fix.
Die Kurzfassung
Abschnitt betitelt „Die Kurzfassung“- Jeder NextPDF-Fehler erbt von einer abstrakten Basis,
NextPdfException, sodass Sie alle Bibliotheksfehler mit einem einzigen Typ abfangen können. - Darunter stehen spezifische, typisierte Unterklassen — eine Schriftart, die nicht gefunden werden kann, eine Konfiguration, die ungültig ist, eine Signaturoperation, die fehlgeschlagen ist — sodass Sie genau den Fehler abfangen können, den Sie behandeln können.
- Jede NextPDF-Ausnahme implementiert
ContextAwareExceptionInterfaceund stelltgetContext()bereit: eine strukturierte, log-sichere Map, damit Sie nie eine Meldungszeichenkette parsen müssen, um Diagnosedaten zurückzugewinnen. - Meldungen sind handlungsleitend: benannte Konstruktoren binden die tatsächliche Grundursache (und oft den Fix) an die Meldung, statt einer generischen Vorlage.
- Jede Ausnahmeklasse dokumentiert, wer auf sie reagieren kann — Entwickler, Infrastruktur oder Bibliotheksaufrufer —, sodass die Triage beginnt, bevor Sie den Stack-Trace lesen.
Wie NextPDF es angeht
Abschnitt betitelt „Wie NextPDF es angeht“Die Hierarchie ist flach und bewusst gewählt. Es gibt eine Basis, eine Schicht domänenspezifischer Typen und einen Vertrag, den jeder einzelne von ihnen einhält.
Eine Basis, bewusst als Catch-all konzipiert. NextPdfException ist abstrakt, erweitert RuntimeException und implementiert ContextAwareExceptionInterface:
abstract class NextPdfException extends RuntimeException implements ContextAwareExceptionInterface{ /** @return array<string, mixed> */ public function getContext(): array { return []; }}Dass sie abstrakt ist, ist eine bewusste Entscheidung. Sie fangen den unspezifischen Basistyp nie versehentlich, weil er nie direkt ausgelöst wird. Sie fangen ihn bewusst als Auffangnetz, und Sie fangen eine spezifische Unterklasse, wenn Sie etwas Spezifisches tun können.
Spezifische, typisierte Unterklassen. Eine fehlende Schriftart ist kein generischer Fehler; sie ist eine FontNotFoundException und trägt die Daten, die Sie für die Behebung benötigen:
final class FontNotFoundException extends NextPdfException{ public function __construct( private readonly string $fontName, private readonly array $searchPaths, private readonly bool $fallbackAttempted, ?Throwable $previous = null, ) { parent::__construct( \sprintf('Font "%s" not found. Searched: [%s].', $fontName, \implode(', ', $searchPaths)), 0, $previous, ); } // getFontName(), getSearchPaths(), wasFallbackAttempted(), getContext()}Die Meldung benennt die Schriftart und die exakt durchsuchten Pfade. Sie raten nicht, welches Verzeichnis gefehlt hat. Die Ausnahme sagt es Ihnen.
Strukturierter Kontext, kein Auslesen von Zeichenketten. Jede Ausnahme liefert eine snake_case-Map zurück, die ausschließlich Primitive enthält und sich gefahrlos direkt in ein Log oder eine APM-Payload serialisieren lässt:
public function getContext(): array{ return [ 'config_key' => $this->configKey, 'given_value' => $this->givenValue, 'expected_type' => $this->expectedType, ];}Der Grund für diesen Vertrag ist explizit. Eine Logging-Middleware kann $logger->error($e->getMessage(), $e->getContext()) für jede NextPDF-Ausnahme aufrufen, ohne die Meldung jemals zu parsen. Die Meldung ist für Menschen. Der Kontext ist für Maschinen. Keines von beiden muss die Rolle des anderen übernehmen.
Handlungsleitende Meldungen über benannte Konstruktoren. Hier sind Fehler nicht mehr beiläufig, sondern bewusst gestaltet. SignatureException sagt nicht nur „Signieren auf Stufe B-LT fehlgeschlagen“. Sie bietet benannte Konstruktoren an, die die tatsächliche Grundursache und häufig die exakte Abhilfe mit der Meldung verknüpfen:
public static function tsaUrlEmpty(string $signatureLevel): self{ return new self('', $signatureLevel, null, 'TSA endpoint URL is empty: pass a non-empty `tsaUrl` to the TsaClient ' . 'constructor (e.g. "https://timestamp.example.com/tsa") or remove the ' . 'TSA client wiring if no timestamping is required at this signature level');}Die Meldung gibt an, was falsch ist, und was dagegen zu tun ist. Es gibt verwandte Konstruktoren für ein fehlendes Capability-Paket, einen fehlenden HTTP-Client, einen versehentlich gewählten reinen Digest-Algorithmus, einen Schlüsseltyp, der nicht zum Algorithmus passt, und weitere. Jeder davon verwandelt eine Fehlerklasse in einen Satz, auf den ein Entwickler reagieren kann, ohne den Quellcode der Engine zu lesen.
Fehler, die bewusst laut sind. Manche Ausnahmen existieren genau dazu, aus einer stillen Lücke eine laute zu machen. NotImplementedException trägt ein maschinell durchsuchbares feature-Label und eine followUp-Referenz:
final class NotImplementedException extends NextPdfException{ public function __construct( public readonly string $feature, public readonly string $followUp, ?Throwable $previous = null, ) { parent::__construct( \sprintf('%s is not implemented in this release. %s', $feature, $followUp), 0, $previous, ); }}Ein erreichter, aber nicht angebundener Pfad löst diese Ausnahme aus, statt einen plausiblen No-op zurückzugeben. Derselbe Gedanke steckt hinter StrictModeViolation, deren Unterklassen ein kurzes, durchsuchbares Label für das abweichende Konstrukt sowie optionalen Standort- und Zitatkontext tragen. Eine Spec-Abweichung wird zu einem typisierten, kontextbezogenen Stopp statt zu einem still falschen Rendering.
Triage-Metadaten in der Klasse selbst. Jede Ausnahmeklasse benennt in ihrem Docblock, wer auf sie reagieren kann. Zum Beispiel ist FontNotFoundException „Entwickler (Schriftartpfad prüfen) oder Infrastruktur (Dateiberechtigungen korrigieren)“. InvalidConfigException ist „Entwickler (Konfiguration korrigieren, bevor NextPDF aufgerufen wird)“. NotImplementedException ist „Bibliotheksaufrufer — entweder den Aufruf entfernen oder auf ein künftiges Release festlegen“. Die Triage beginnt vor dem Stack-Trace, denn die Frage „Liegt das bei mir oder bei den Ops?“ hat bereits eine dokumentierte Antwort.
Die Tabelle fasst das Design zusammen und zeigt, was Ihnen jede Eigenschaft bringt.
| Design-Eigenschaft | Im Quellcode | Was es Ihnen einbringt |
|---|---|---|
| Eine abstrakte Basis | NextPdfException (abstrakt, implementiert das Kontext-Interface) | Fangen Sie jeden Bibliotheksfehler mit einem Typ ab, ohne versehentlich den unspezifischen Basistyp zu verwenden |
| Spezifische, typisierte Unterklassen | FontNotFoundException, InvalidConfigException, SignatureException, … | Fangen Sie genau den Fehler ab, den Sie behandeln können |
| Strukturierter Kontext | getContext() — ausschließlich snake_case-Primitive | Direkt ins Log schreiben oder an APM senden, ohne eine Meldungszeichenkette zu parsen |
| Handlungsleitende Meldungen | Benannte Konstruktoren binden Grundursache + Abhilfe | Ein Satz, auf den Sie reagieren können, statt einer Vorlage |
| Bewusst laut | NotImplementedException, StrictModeViolation | Eine stille Lücke wird zu einem typisierten, durchsuchbaren Stopp |
| Triage-Metadaten | „Actionable by:” in jedem Klassen-Docblock | Wissen, wessen Problem es ist, bevor Sie den Trace lesen |
Was die Evidenz sagt
Abschnitt betitelt „Was die Evidenz sagt“Diese Seite ist Evidence: Code-backed : Jede Klasse, jede Signatur und jede Meldungsform ist aus dem Ausnahme-Namespace der Engine zitiert, nicht paraphrasiert.
- Die abstrakte Basis und ihr
ContextAwareExceptionInterface-Vertrag, die typisierten Unterklassen, diegetContext()-Form und die benannten Konstruktoren vonSignatureExceptionsind wörtlich aus dem Quellcode zitiert. - Die Triage-Zeilen „Actionable by:” sind Klassen-Docblock-Verträge in genau diesen Dateien.
- Der Bezug zu menschlichen Faktoren ist Spec: ISO 9241-110 ISO 9241-110 — §5.6.4.3, zu Fehlern, die sich gut genug selbst erklären, um behoben zu werden, und das Prinzip der Robustheit gegenüber Benutzungsfehlern aus §6. Die Engine behandelt den Entwickler als den Benutzer und die Ausnahme als die Schnittstelle, die diese Klauseln erfüllen muss.
Praktisches Beispiel
Abschnitt betitelt „Praktisches Beispiel“Fangen Sie breit als Auffangnetz, spezifisch dort, wo Sie handeln können, und übergeben Sie den strukturierten Kontext direkt an Ihren Logger — ohne Parsen der Meldung.
<?php
declare(strict_types=1);
use NextPDF\Core\Document;use NextPDF\Exception\FontNotFoundException;use NextPDF\Exception\NextPdfException;use Psr\Log\LoggerInterface;
function renderInvoice(LoggerInterface $logger): ?string{ try { $document = Document::createStandalone(); $document->setTitle('Invoice 2026-0042'); $document->addPage(); $document->setFont('BrandSans', '', 12); $document->cell(0, 10, 'Thank you for your business.', newLine: true);
return $document->getPdfData(); } catch (FontNotFoundException $e) { // Specific: we can recover — fall back to a built-in font. // getContext() is log-safe structured data, not a parsed string. $logger->warning($e->getMessage(), $e->getContext());
return null; // caller re-renders with 'helvetica' } catch (NextPdfException $e) { // Backstop: any other NextPDF failure, still with structured context. $logger->error($e->getMessage(), $e->getContext());
return null; }}Das spezifische catch kann wiederherstellen, weil der Ausnahmetyp signalisiert, dass eine Wiederherstellung möglich ist. Das Auffangnetz protokolliert strukturierten Kontext für alles Übrige. Zu keinem Zeitpunkt liest die Anwendung die Meldung, um herauszufinden, was passiert ist.
Verbreitetes Missverständnis
Abschnitt betitelt „Verbreitetes Missverständnis“Die übliche Fehldeutung ist, dass ein tiefer Ausnahmebaum Over-Engineering sei und dass ein einziger Fehlertyp einfacher wäre. Er wäre einfacher für die Engine und schlechter für Sie. Ein Typ bedeutet, dass jeder Fehler ein generischer Stack-Trace ist und Wiederherstellungslogik auf String-Abgleich hinausläuft. Dieser Abgleich ist fragil, und die nächste Umformulierung der Meldung bricht ihn. Eine kleine, spezifische Hierarchie verlagert dieses Wissen ins Typsystem, wo der Compiler und Ihre catch-Blöcke es nutzen können.
Ein zweites Missverständnis ist, dass die Meldung und der Kontext redundant seien. Das sind sie nicht. Die Meldung ist Prosa für einen Menschen, der eine Log-Zeile liest. Der Kontext ist eine typisierte Map für Code-Routing, Alerting oder Dashboards. Beides zu vermengen ist genau die String-Parsing-Falle, zu deren Beseitigung der getContext()-Vertrag existiert.
Grenzen und Abgrenzungen
Abschnitt betitelt „Grenzen und Abgrenzungen“Die Hierarchie ist bewusst flach. NextPDF erstellt nicht für jeden denkbaren Fehler eine eigene Ausnahmeklasse. Sie entsteht dort, wo ein Aufrufer diesen Fehler vernünftigerweise gezielt abfangen würde. Eine Übersegmentierung würde das String-Parsing-Problem gegen ein wucherndes Catch-Listen-Problem eintauschen.
getContext() ist für Logs und APM als strukturierter Kontext gedacht und gibt daher per Vertrag ausschließlich Primitive und Listen von Primitiven zurück, ohne verschachtelte Objekte. Es ist Diagnosekontext, kein serialisierter Schnappschuss von Engine-Interna. Es ist auch kein stabiles Wire-Format, gegen das externe Schemata gebaut werden sollten.
Diese Seite beschreibt die Designoberfläche der Ausnahmen. Der genaue Bestand an Ausnahmen und ihren Feldern entwickelt sich mit der Engine. Die hier zitierten Klassen und Formen sind zum Zeitpunkt dieser Prüfung aktuell und veranschaulichen den Vertrag, sie sind kein eingefrorener Katalog. Der Vertrag — eine Basis, typisierte Unterklassen, strukturierter Kontext, handlungsleitende Meldungen — ist der stabile Teil.
Verwandte Dokumentation
Abschnitt betitelt „Verwandte Dokumentation“- Eine API, die das Raten verweigert — die Fail-Fast-Wächter, die diese Ausnahmen überhaupt erst auslösen.
- Die NextPDF-Designphilosophie — warum „Fehler sind eine API-Oberfläche” ein erstklassiges Prinzip ist.
- Das Pipeline-Modell — wo diese Fehler an die Oberfläche treten, während ein Dokument die Engine durchläuft, und wie sie beobachtet werden.
Glossar
Abschnitt betitelt „Glossar“- Code-gestützt (Evidenzstufe) — eine Seite, deren Aussagen am Quellcode der Engine selbst geprüft und zitiert statt paraphrasiert werden.
- Kontextbewusste Ausnahme — eine NextPDF-Ausnahme, die
ContextAwareExceptionInterfaceimplementiert undgetContext()bereitstellt. Diese Methode liefert eine snake_case-Map mit primitiven Diagnosefeldern zurück, die sich gefahrlos in ein Log oder eine APM-Payload serialisieren lässt, ohne die Meldungszeichenkette zu parsen. - Benannter Konstruktor — eine statische Factory-Methode (zum Beispiel
SignatureException::tsaUrlEmpty()), die eine Ausnahme mit einer Meldung erstellt, die an eine spezifische Grundursache und oft an deren Abhilfe gebunden ist. - PAdES — PDF Advanced Electronic Signatures, die ETSI-Profilfamilie für das PDF-Signieren. Bei der ersten Verwendung ausgeschrieben; ausführlich auf den Signierseiten behandelt.
- TSA — Time-Stamping Authority, der vertrauenswürdige Dienst, der die von den höheren PAdES-Profilen verwendeten RFC 3161-Zeitstempel ausstellt.