การจัดการข้อผิดพลาดด้วยลำดับชั้นของ exception ใน NextPDF
ภาพรวมโดยย่อ
หัวข้อที่มีชื่อว่า “ภาพรวมโดยย่อ”NextPDF โยน exception แบบมีชนิดเมื่ออยู่ในสถานะผิดปกติ และไม่ซ่อนข้อผิดพลาดไว้หลังค่าคืน false หรือ null domain exception ทุกประเภทขยายมาจากคลาสฐานแบบ abstract เดียวกันคือ NextPdfException และเปิดเผยบริบทการวินิจฉัยแบบมีโครงสร้างผ่าน ContextAwareExceptionInterface สูตรนี้แสดงตำแหน่งที่ควรดักจับ และวิธีบันทึกบริบทแบบมีโครงสร้างลงในไปป์ไลน์การตรวจสอบประสิทธิภาพแอปพลิเคชัน (APM) นอกจากนี้ยังระบุด้วยว่าการดักจับแบบครอบคลุมทั้งหมดเพียงรายการเดียวไม่ครอบคลุมความล้มเหลวใดบ้าง
การติดตั้ง
หัวข้อที่มีชื่อว่า “การติดตั้ง”composer require nextpdf/core:^3ไม่ต้องติดตั้ง extension เพิ่มเติมใดๆ
ภาพรวมเชิงแนวคิด
หัวข้อที่มีชื่อว่า “ภาพรวมเชิงแนวคิด”ลำดับชั้นมีดังนี้:
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ลำดับชั้นนี้มีผลเชิงปฏิบัติสองประการ และทั้งสองประการตรวจสอบเทียบกับซอร์สแล้ว:
catch (NextPdfException $e)ดักจับทุก exception ภายใต้NextPDF\Exceptionรวมถึงการละเมิดโหมด strict ด้วย เพราะทั้งหมดขยายมาจากคลาสฐานแบบ abstract- ไม่ดักจับทุกอย่างที่ไลบรารีอาจโยนออกมา
NextPDF\Support\DegradedExceptionขยายมาจากRuntimeExceptionโดยตรง ไม่ใช่NextPdfExceptionดังนั้นcatch (NextPdfException $e)จึงไม่ดักจับกรณีที่นโยบายการลดทอนความสามารถปฏิเสธการทำงาน หากต้องการจัดการกรณีดังกล่าว ให้ดักจับDegradedException(หรือRuntimeExceptionที่กว้างกว่า) อย่างชัดเจน สูตรนี้ทำให้ขอบเขตดังกล่าวชัดเจน แทนที่จะถือว่าการดักจับแบบครอบคลุมทั้งหมดเพียงรายการเดียวให้ความครอบคลุมอย่างสมบูรณ์
NextPdfException::getContext() คืนค่าเป็น array<string, mixed> ที่มีคีย์รูปแบบ snake_case และมีเฉพาะค่าพื้นฐานหรือรายการของค่าพื้นฐานเท่านั้น คุณสามารถ serialize ค่านี้ลงในอาร์เรย์บริบทของ logger ตาม PSR-3 ได้โดยตรง PSR-3 §1.3 กำหนดให้วาง exception ไว้ใต้คีย์บริบท 'exception' ส่วน getContext() ของ NextPDF จะเพิ่มรายละเอียดเชิง domain ไว้ร่วมกับคีย์นั้น ไม่ใช่ใส่ตัวออบเจ็กต์ exception เอง
พื้นผิว API
หัวข้อที่มีชื่อว่า “พื้นผิว API”พื้นผิว API นี้อ้างอิงจาก PHPDoc ของ NextPDF\Exception\NextPdfException, NextPDF\Contracts\ContextAwareExceptionInterface, domain exception ที่เป็นรูปธรรม (เช่น NextPDF\Exception\FontNotFoundException ซึ่งมี getFontName() / getSearchPaths() / wasFallbackAttempted()) และ NextPDF\Support\DegradedException (ซึ่งมี Capability และ DegradationPolicy แนบมาด้วย) ตัวอย่างด้านล่างใช้ NextPdfException::getContext() และ accessor เฉพาะของแต่ละ exception
ตัวอย่างโค้ด — เริ่มต้นอย่างรวดเร็ว
หัวข้อที่มีชื่อว่า “ตัวอย่างโค้ด — เริ่มต้นอย่างรวดเร็ว”<?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());}ตัวอย่างโค้ด — สำหรับใช้งานจริง
หัวข้อที่มีชื่อว่า “ตัวอย่างโค้ด — สำหรับใช้งานจริง”ตัวอย่างฉบับเต็มแสดงการดักจับแบบละเอียด การบันทึกบริบทแบบมีโครงสร้าง และขอบเขตของ DegradedException นอกจากนี้ยังคงรักษาช่องทางเอาต์พุตของ 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 ยังคงปล่อยว่างไว้สำหรับ harness ส่วนไฟล์ PDF จะถูกส่งไปยัง NEXTPDF_COOKBOOK_OUTPUT เท่านั้น
กรณีขอบและข้อควรระวัง
หัวข้อที่มีชื่อว่า “กรณีขอบและข้อควรระวัง”- จัดเรียงบล็อก catch จากเฉพาะเจาะจง → ทั่วไป PHP จะจับคู่กับบล็อก
catchแรกที่เข้ากันได้catch (NextPdfException $e)ที่วางไว้ก่อนcatch (FontNotFoundException $e)จะทำให้บล็อกที่เฉพาะเจาะจงกว่านั้นกลายเป็นโค้ดที่ไม่มีทางถูกเรียกใช้ DegradedExceptionไม่ใช่NextPdfExceptionจากการตรวจสอบเทียบกับซอร์ส คลาสนี้ขยายมาจากRuntimeExceptioncatch (NextPdfException $e)เพียงรายการเดียวจะปล่อยให้กรณีปฏิเสธจากการลดทอนความสามารถแบบ strict แพร่กระจายต่อไป ให้ดักจับ exception นั้น (หรือRuntimeException) อย่างชัดเจนเมื่อมีนโยบายการลดทอนความสามารถเข้ามาเกี่ยวข้องgetContext()ปลอดภัยต่อ APM ตามสัญญา คีย์อยู่ในรูปแบบ snake_case ค่าเป็นค่าพื้นฐานหรือรายการของค่าพื้นฐาน โดยไม่มีออบเจ็กต์ซ้อนและไม่มี resource คุณสามารถ serialize ค่านี้ได้โดยตรง ค่านี้ไม่มีไบต์ของเอกสาร- อย่าแยกวิเคราะห์ข้อความของ exception ข้อความเหล่านี้มีไว้ให้มนุษย์อ่านและอาจเปลี่ยนแปลงได้ ให้ใช้ accessor แบบมีชนิด (
getFontName(),capability->idเป็นต้น) และgetContext()เป็นพื้นผิวที่เสถียรสำหรับการประมวลผลโดยเครื่อง - ข้อควรระวังเรื่องจำนวนที่ล้าสมัย เอกสารเก่าอาจอ้างถึงค่าคงที่ “N domain exceptions” ลำดับชั้นนี้เติบโตขึ้นในแต่ละรุ่นที่ออกใหม่ ให้พึ่งพาชนิดฐาน
NextPdfExceptionและinstanceofอย่าพึ่งพาจำนวนที่ฮาร์ดโค้ดไว้เด็ดขาด - placeholder ของ PSR-3 ยังคงเป็นสตริง เมื่อบันทึก ให้คงข้อความไว้เป็นสตริงที่มีโทเค็น
{placeholder}และวางค่าไว้ในอาร์เรย์บริบท (PSR-3 §1.2) อย่าแทรกออบเจ็กต์ exception เข้าไปในข้อความ
ประสิทธิภาพ
หัวข้อที่มีชื่อว่า “ประสิทธิภาพ”การจัดการ exception ไม่เพิ่มต้นทุนในสภาวะคงตัว NextPDF โยน exception เฉพาะเมื่ออยู่ในสถานะผิดปกติเท่านั้น และ getContext() สร้างอาร์เรย์ขนาดเล็กเฉพาะเมื่อถูกเรียกใช้ performance_budget (wall_ms: 2000, peak_mb: 96) กำหนดขอบเขตการรัน harness สำหรับสูตรนี้ ไม่ใช่สำหรับเอกสารทั่วไปใดๆ
หมายเหตุด้านความปลอดภัย
หัวข้อที่มีชื่อว่า “หมายเหตุด้านความปลอดภัย”getContext()ออกแบบมาให้ปลอดภัยต่อการบันทึก: มีเฉพาะค่าพื้นฐาน ไม่มีเพย์โหลดของเอกสาร และไม่มีไบต์ของไฟล์ อย่างไรก็ตาม ยังต้องรับผิดชอบต่อค่าที่เพิ่มเข้าไปในบริบทของบันทึกเอง ให้ล้างข้อมูลทุกอย่างที่ผู้ใช้ป้อนเข้ามา (เช่น เส้นทางไฟล์) ตามนโยบายการบันทึกที่ใช้อยู่ ก่อนที่ข้อมูลจะไปถึงปลายทาง- อย่าแสดงข้อความ exception แบบดิบให้ผู้ใช้ปลายทางเห็นในลักษณะที่เปิดเผยโครงสร้างของระบบไฟล์ ให้แสดงข้อความทั่วไป และบันทึกบริบทแบบมีโครงสร้างไว้ที่ฝั่งเซิร์ฟเวอร์
ความสอดคล้อง
หัวข้อที่มีชื่อว่า “ความสอดคล้อง”| ข้อความยืนยัน | ข้อกำหนด | ข้อ | รหัสอ้างอิง (reference_id) |
|---|---|---|---|
ตาม PSR-3 ควรวาง exception ไว้ในบริบทของบันทึกภายใต้ exception ในฐานะคีย์บริบท | PSR-3 | §1.3 | |
| ข้อความบันทึกยังคงเป็นสตริง และชื่อ placeholder จะถูกแมปไปยังคีย์บริบท | PSR-3 | §1.2 | |
| วันที่แก้ไขจะถูกสร้างใหม่ทุกครั้งที่บันทึก ดังนั้นเอาต์พุตจึงเสถียรในเชิงโครงสร้าง (ไม่ใช่ระดับไบต์) | ISO 32000-2 | §14.3 |
สูตรนี้ผ่านการตรวจสอบด้วยโปรไฟล์ความสามารถในการทำซ้ำแบบโครงสร้าง เอาต์พุตมีค่า /ID ใน trailer และวันที่แก้ไขที่ถูกสร้างใหม่ทุกครั้งที่บันทึก ดังนั้นจึงไม่สามารถบรรลุความเหมือนกันในระดับไบต์ได้ โครงสร้างที่ปรับให้เป็นมาตรฐานด้วย qpdf ยังคงเสถียร