İçeriğe geç

Bir özellik olarak hatalar

Spec: ISO 9241-110, §5.6.4 Evidence: Code-backed

NextPDF, istisna hiyerarşisini bir API yüzeyi olarak ele alır ve onu, istisnayı fırlatan yöntemlerle aynı özenle tasarlar. Her başarısızlık belirlidir, türlenmiştir, ihtiyaç duyduğunuz ayrıntı düzeyinde yakalanabilir ve günlükleriniz için yapılandırılmış bağlam taşır.

Bu sayfa, söz konusu yüzeyi motorun kendi kaynak kodunda gösterir: temel tür, türlenmiş alt sınıflar, kök nedeni mesaja bağlayan adlandırılmış kurucular ve her NextPDF istisnasının açığa çıkardığı yapılandırılmış bağlam.

Bir hata mesajı, motorun sizinle olası en kötü anda konuşmasıdır: üretim ortamında, gece 2 a.m.‘de ve teslim edilmesi gereken bir belge varken. O mesajın o anda söylediği şey, bir sonraki adımın bir düzeltme mi yoksa uzun bir araştırma mı olacağını belirler.

Genel bir RuntimeException: something went wrong size hiçbir çıkış yolu bırakmaz. Motorun başarısız olduğunu söyler, ancak neyin nerede başarısız olduğunu veya tam olarak ne yapılması gerektiğini söylemez. İnsan faktörleri rehberliği bu konuda açıktır. Bir hata, onu düzeltmek araştırma projesi değil, bariz bir sonraki adım olacak kadar iyi açıklanmalıdır ( Spec: ISO 9241-110, §5.6.4.3 ). Nedeni ve çözümü adlandıran bir istisna, hoş bir ayrıntıdan ibaret değildir. Beş dakikalık bir düzeltme ile beş saatlik bir araştırma arasındaki farktır.

  • Her NextPDF başarısızlığı tek bir soyut temelden, NextPdfException sınıfından türer; böylece tüm kitaplık hatalarını tek bir türle yakalayabilirsiniz.
  • Bu temelin altında belirli, türlenmiş alt sınıflar yer alır — bulunamayan bir yazı tipi, geçersiz bir yapılandırma, başarısız olan bir imza işlemi — böylece tam olarak ele alabileceğiniz başarısızlığı yakalayabilirsiniz.
  • Her NextPDF istisnası ContextAwareExceptionInterface arabirimini uygular ve getContext() yöntemini açığa çıkarır: yapılandırılmış, günlüğe güvenle yazılabilecek bir eşleme; böylece tanılama bilgilerini geri kazanmak için asla bir mesaj dizesini ayrıştırmazsınız.
  • Mesajlar eyleme dönüştürülebilirdir: adlandırılmış kurucular, genel bir şablon yerine gerçek kök nedeni (ve çoğu zaman düzeltmeyi) mesaja bağlar.
  • Her istisna sınıfı hangi rolün işlem yapabileceğini belgeler — geliştirici, altyapı veya kitaplık çağıranı — böylece önceliklendirme, yığın izini okumadan önce başlar.

Hiyerarşi sığdır ve bilinçli tasarlanmıştır. Tek bir temel, alana özgü türlerden oluşan bir katman ve her birinin uyduğu bir sözleşme vardır.

Tek temel, tasarım gereği toplu yakalama sağlar. NextPdfException soyuttur, RuntimeException sınıfını genişletir ve ContextAwareExceptionInterface arabirimini uygular:

abstract class NextPdfException extends RuntimeException implements ContextAwareExceptionInterface
{
/** @return array<string, mixed> */
public function getContext(): array
{
return [];
}
}

Soyut olması bilinçli bir karardır. Belirsiz temeli asla kazara yakalamazsınız, çünkü doğrudan hiç fırlatılmaz. Onu bir güvenlik ağı olarak bilinçli şekilde yakalarsınız; belirli bir işlem yapabildiğinizde de belirli bir alt sınıfı yakalarsınız.

Belirli, türlenmiş alt sınıflar. Eksik bir yazı tipi genel bir hata değildir; o bir FontNotFoundException nesnesidir ve işlem yapmak için ihtiyaç duyduğunuz verileri taşır:

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

Mesaj, yazı tipini ve aranan tam yolları adlandırır. Hangi dizinin eksik olduğunu tahmin etmeniz gerekmez; istisna size söyler.

Yapılandırılmış bağlam, dize taraması değil. Her istisna, doğrudan bir günlüğe veya bir APM yüküne serileştirmek için güvenli olan, snake_case kullanan ve yalnızca ilkel değerlerden oluşan bir eşleme döndürür:

public function getContext(): array
{
return [
'config_key' => $this->configKey,
'given_value' => $this->givenValue,
'expected_type' => $this->expectedType,
];
}

Sözleşme amacını açıkça gösterir. Bir günlükleme ara yazılımı, $logger->error($e->getMessage(), $e->getContext()) çağrısını, mesajı hiç ayrıştırmadan herhangi bir NextPDF istisnası için yapabilir. Mesaj insanlar içindir. Bağlam makineler içindir. Hiçbirinin diğerinin işini yapması gerekmez.

Adlandırılmış kurucular aracılığıyla eyleme dönüştürülebilir mesajlar. Hataların rastlantısal olmaktan çıkıp tasarlanmış hale geldiği yer burasıdır. SignatureException yalnızca “B-LT düzeyinde imzalama başarısız oldu” demez. Gerçek kök nedeni ve çoğu zaman tam çözümü mesaja bağlayan adlandırılmış kurucular sunar:

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');
}

Mesaj neyin yanlış olduğunu ve bu konuda ne yapılması gerektiğini belirtir. Eksik bir yetenek paketi, mevcut olmayan bir HTTP istemcisi, yanlışlıkla seçilmiş yalnızca özet üreten bir algoritma, algoritmayla eşleşmeyen bir anahtar türü ve daha fazlası için kardeş kurucular vardır. Her biri, bir başarısızlık sınıfını, geliştiricinin motorun kaynak kodunu okumadan üzerinde işlem yapabileceği bir cümleye dönüştürür.

Bilinçli olarak gürültülü olan başarısızlıklar. Bazı istisnalar tam da sessiz bir boşluğun gürültülü hale gelmesi için vardır. NotImplementedException, grep ile aranabilir bir feature etiketi ve bir followUp başvurusu taşır:

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,
);
}
}

Ulaşılan ancak bağlanmamış bir yol, makul bir işlemsiz değer döndürmek yerine bunu fırlatır. Aynı fikir StrictModeViolation nesnesi için de geçerlidir: alt sınıfları, sapan yapı için kısa, grep ile aranabilir bir etiketin yanı sıra isteğe bağlı konum ve alıntı bağlamı taşır. Bir spesifikasyon sapması, sessizce yanlış bir işleme değil, türlenmiş, bağlamsal bir durdurmaya dönüşür.

Sınıfın kendisinde önceliklendirme meta verisi. Her istisna sınıfı, belge bloğunda kimin işlem yapabileceğini adlandırır. Örneğin, FontNotFoundException için bu “Geliştirici (yazı tipi yolunu doğrulayın) veya Altyapı (dosya izinlerini düzeltin)” şeklindedir. InvalidConfigException için bu “Geliştirici (NextPDF çağrılmadan önce yapılandırmayı düzeltin)” şeklindedir. NotImplementedException için bu “Kitaplık çağıranları — ya çağrıyı kaldırın ya da gelecekteki bir sürüme sabitleyin” şeklindedir. Önceliklendirme yığın izinden önce başlar, çünkü “bu benim sorunum mu yoksa operasyon ekibinin mi?” sorusunun yazılı yanıtı zaten vardır.

Tablo, tasarımı ve her özelliğin size ne kazandırdığını özetler.

Tasarım özelliğiKaynak koddaSize ne kazandırır
Tek bir soyut temelNextPdfException (soyut, bağlam arabirimini uygular)Tüm kitaplık hatalarını tek bir türle yakalayın; belirsiz temeli kazara yakalamayın
Belirli türlenmiş alt sınıflarFontNotFoundException, InvalidConfigException, SignatureException, …Tam olarak ele alabileceğiniz başarısızlığı yakalayın
Yapılandırılmış bağlamgetContext() — yalnızca snake_case ilkel değerlerBir mesaj dizesini ayrıştırmadan günlüğe yazın veya APM’ye gönderin
Eyleme dönüştürülebilir mesajlarAdlandırılmış kurucular kök neden + çözümü bağlarİşlem yapabileceğiniz bir cümle, bir şablon değil
Bilinçli olarak gürültülüNotImplementedException, StrictModeViolationSessiz bir boşluk, türlenmiş, grep ile aranabilir bir durdurmaya dönüşür
Önceliklendirme meta verisiHer sınıfın belge bloğunda “Actionable by:“Yığın izini okumadan önce bunun kimin sorunu olduğunu bilin

Bu sayfa Evidence: Code-backed niteliğindedir: her sınıf, imza ve mesaj biçimi, yeniden ifade edilmeden motorun istisna ad alanından alıntılanmıştır.

  • Soyut temel ve onun ContextAwareExceptionInterface sözleşmesi, türlenmiş alt sınıflar, getContext() biçimi ve SignatureException adlandırılmış kurucuları, kaynak koddan kelimesi kelimesine alıntılanmıştır.
  • “Actionable by:” önceliklendirme satırları, aynı dosyalardaki sınıf belge bloğu sözleşmeleridir.
  • İnsan faktörleri dayanağı Spec: ISO 9241-110 standardıdır — §5.6.4.3 düzeltilebilecek kadar açık hataları, §6 ise kullanım hatasına dayanıklılık ilkesini ele alır. Motor, geliştiriciyi kullanıcı olarak ve istisnayı söz konusu maddeleri karşılaması gereken arabirim olarak ele alır.

Bir güvenlik ağı olarak genel düzeyde yakalayın, işlem yapabileceğiniz yerde belirli düzeyde yakalayın ve yapılandırılmış bağlamı doğrudan günlükleme katmanınıza aktarın — mesaj ayrıştırması yok.

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

Belirli catch bloğu kurtarma yapar, çünkü istisna türü kurtarmanın mümkün olduğunu ona söyler. Güvenlik ağı, diğer tüm durumlar için yapılandırılmış bağlamı günlüğe yazar. Uygulama, ne olduğunu öğrenmek için hiçbir noktada mesajı ayrıştırmaz.

Yaygın yanlış yorum, derin bir istisna ağacının aşırı mühendislik olduğu ve tek bir hata türünün daha basit olacağıdır. Bu, motor için daha basit ve sizin için daha kötü olurdu. Tek bir tür, her başarısızlığın genel bir yığın izi olduğu ve kurtarma mantığının bir dize eşleşmesi olduğu anlamına gelir. O eşleşme kırılgandır; bir sonraki mesajın yeniden ifade edilmesi onu bozar. Küçük, belirli bir hiyerarşi, o bilgiyi tür sistemine taşır; orada derleyici ve catch bloklarınız onu kullanabilir.

İkinci bir yanlış anlama, mesajın ve bağlamın gereksiz olduğudur. Gereksiz değillerdir. Mesaj, bir günlük satırını okuyan bir insan için düz metindir. Bağlam, kodu yönlendirme, uyarı verme veya panolar için türlenmiş bir eşlemedir. Bunları birbirine karıştırmak, tam olarak getContext() sözleşmesinin ortadan kaldırmak için var olduğu dize ayrıştırma tuzağıdır.

Hiyerarşi bilinçli olarak sığdır. NextPDF, akla gelebilecek her başarısızlık için ayrı bir istisna sınıfı oluşturmaz. O başarısızlığı özellikle yakalamak, bir çağıranın makul biçimde yapacağı bir şey olduğunda bir tane oluşturur. Aşırı bölmek, dize ayrıştırma sorununu giderek büyüyen bir yakalama listesi sorunuyla değiş tokuş ederdi.

getContext(), günlükler ve APM için tasarlanmıştır; bu nedenle sözleşme gereği yalnızca ilkel değerler ve ilkel değer listeleri döndürür, iç içe nesneler döndürmez. Bu, tanılama bağlamıdır; motorun iç bileşenlerinin serileştirilmiş bir anlık görüntüsü değildir. Ayrıca, üzerine harici şemalar inşa edilecek kararlı bir tel biçimi de değildir.

Bu sayfa istisna tasarım yüzeyini açıklar. İstisnaların eksiksiz kümesi ve alanları motorla birlikte gelişir. Burada alıntılanan sınıflar ve biçimler bu inceleme itibarıyla günceldir; dondurulmuş bir katalog değil, sözleşmeyi gösteren örneklerdir. Sözleşme — tek temel, türlenmiş alt sınıflar, yapılandırılmış bağlam, eyleme dönüştürülebilir mesajlar — kararlı olan kısımdır.

  • Koda dayalı (kanıt düzeyi) — iddiaları, yeniden ifade edilmek yerine alıntılanarak motorun kendi kaynak koduyla denetlenen bir sayfa.
  • Bağlama duyarlı istisna — NextPDF istisnası olup ContextAwareExceptionInterface arabirimini uygulayan ve getContext() yöntemini açığa çıkaran bir istisna. Söz konusu yöntem, mesaj dizesini ayrıştırmadan bir günlüğe veya APM yüküne serileştirmek için güvenli, ilkel tanılama alanlarından oluşan bir snake_case eşleme döndürür.
  • Adlandırılmış kurucu — bir mesajı belirli bir kök nedene ve çoğu zaman onun çözümüne bağlayarak bir istisna oluşturan statik bir fabrika yöntemi (örneğin SignatureException::tsaUrlEmpty()).
  • PAdES — PDF Advanced Electronic Signatures, PDF imzalama için ETSI profil ailesi. İlk kullanımda açılımı verilir; imzalama sayfalarında ayrıntılı olarak ele alınır.
  • TSA — Time-Stamping Authority, daha yüksek PAdES profilleri tarafından kullanılan RFC 3161 zaman damgalarını veren güvenilir hizmet.