การแก้ไขปัญหาแพ็กเกจ NextPDF Laravel
ภาพรวมโดยย่อ
หัวข้อที่มีชื่อว่า “ภาพรวมโดยย่อ”ใช้หน้านี้เพื่อเทียบปัญหาของแพ็กเกจที่พบกับสาเหตุจริงระดับซอร์สโค้ดที่ตรวจสอบยืนยันแล้ว แต่ละรายการระบุอาการ สาเหตุ และวิธีแก้ไข
การติดตั้ง
หัวข้อที่มีชื่อว่า “การติดตั้ง”composer require nextpdf/laravelphp artisan vendor:publish --tag=nextpdf-configภาพรวมเชิงแนวคิด
หัวข้อที่มีชื่อว่า “ภาพรวมเชิงแนวคิด”ปัญหาส่วนใหญ่ที่รายงานเข้ามาอยู่ในห้ากลุ่ม ได้แก่ การค้นพบ การ resolve ใน container การลงลายเซ็น งานคิว และชื่อไฟล์ของ Hypertext Transfer Protocol (HTTP) แพ็กเกจนี้ออกแบบมาให้แสดงความล้มเหลวอย่างชัดเจน ฟีเจอร์ทางเลือกที่ไม่ได้กำหนดค่าไว้จะคืนค่า null และอินพุตที่ไม่ปลอดภัยจะทำให้เกิด exception แบบระบุชนิด โดยทั่วไป อาการจะชี้ไปยังสาเหตุโดยตรง
พื้นผิว API — จากอาการสู่สาเหตุ
หัวข้อที่มีชื่อว่า “พื้นผิว API — จากอาการสู่สาเหตุ”การค้นพบและการบูต
หัวข้อที่มีชื่อว่า “การค้นพบและการบูต”| อาการ | สาเหตุที่ตรวจสอบยืนยันแล้ว | วิธีแก้ไข |
|---|---|---|
| provider ไม่ถูกลงทะเบียนหลังติดตั้ง | แอปพลิเคชันปิดการค้นพบด้วย extra.laravel.dont-discover | นำแพ็กเกจออกจาก dont-discover หรือลงทะเบียน NextPdfServiceProvider ด้วยตนเองใน bootstrap/providers.php |
config('nextpdf') ว่างเปล่า | การกำหนดค่าไม่ได้ถูกผสานเข้าด้วยกัน เนื่องจากยังไม่มี binding ที่ประกาศไว้ถูก resolve (deferred provider) | resolve รายการใดก็ได้ใน provides() หรือยืนยันการค้นพบด้วย php artisan package:discover --ansi |
config/nextpdf.php ไม่ถูกสร้างเมื่อ publish | แท็ก publish ไม่ตรงกัน | ใช้แท็กให้ตรงกันทุกตัวอักษร: php artisan vendor:publish --tag=nextpdf-config |
| เกิดข้อยกเว้น RuntimeException: “NextPDF requires the ext-mbstring/ext-zlib PHP extension” | ไม่มีส่วนขยาย Hypertext Preprocessor (PHP) ที่จำเป็นขณะรันไทม์ | ติดตั้งหรือเปิดใช้งาน mbstring และ zlib ใน php.ini |
การ resolve ใน container
หัวข้อที่มีชื่อว่า “การ resolve ใน container”| อาการ | สาเหตุที่ตรวจสอบยืนยันแล้ว | วิธีแก้ไข |
|---|---|---|
app(SignerInterface::class) คืนค่า null | การลงลายเซ็นถูกปิดใช้งาน หรือค่าใบรับรองว่างเปล่าใน nextpdf.signature | ตั้งค่า signature.enabled = true พร้อม signature.certificate ที่ใช้งานได้ และติดตั้ง nextpdf/premium เพื่อให้มี concrete ของ signer |
app(TsaClient::class) คืนค่า null | nextpdf.tsa.url ว่างเปล่า | กำหนดค่า tsa.url (และ credentials/pins ตามความจำเป็น) |
| Class-not-found สำหรับชนิดของเวอร์ชัน PDF/A | nextpdf.pdfa ไม่ใช่ null แต่ nextpdf/premium ยังไม่ได้ติดตั้ง | ติดตั้ง nextpdf/premium หรือตั้งค่า pdfa กลับเป็น null |
| Class-not-found ขณะ resolve contract ของ e-invoice | binding ถูกลงทะเบียนแล้ว แต่ไม่มี concrete ของ Premium | ติดตั้ง nextpdf/premium เนื่องจาก contract ของ e-invoice จะ resolve แบบ lazy และจะล้มเหลวเฉพาะตอน resolve ครั้งแรกเมื่อไม่มี Premium |
| เอกสารเดียวกันถูกเปลี่ยนแปลงข้ามการดำเนินการเชิงตรรกะสองครั้ง | binding ของเอกสารเป็น factory แต่มีการนำ instance หนึ่งตัวที่ resolve แล้วกลับมาใช้ซ้ำ | resolve PdfDocumentInterface ใหม่สำหรับเอกสารแต่ละฉบับ |
container ที่ไม่มี entry จะโยน not-found exception เมื่อเรียก get() (PHP Standard Recommendation 11 (PSR-11) §1.1.2) ส่วน contract ของ e-invoice ถูก bound ไว้แล้ว ดังนั้น has() ของ container จึงคืนค่า true ข้อผิดพลาดเกิดจาก concrete ของ Premium ที่ขาดหายไประหว่างการสร้างอ็อบเจกต์ ไม่ใช่จากตัว container เอง
| อาการ | สาเหตุที่ตรวจสอบยืนยันแล้ว | วิธีแก้ไข |
|---|---|---|
InvalidArgumentException: Path traversal sequences are not allowed | เส้นทางเอาต์พุตมี .. เป็นเซกเมนต์ | ใช้เส้นทางแบบสัมบูรณ์ที่ปราศจาก traversal ภายใต้ไดเรกทอรีจัดเก็บที่ควบคุมได้ |
InvalidArgumentException: Stream wrappers are not allowed | เส้นทางใช้ scheme เช่น php:// | ใช้เส้นทางไฟล์ระบบแบบธรรมดา |
InvalidArgumentException: Output path contains null bytes | เส้นทางมี \0 หนึ่งไบต์ | ทำความสะอาดเส้นทางก่อน dispatch |
InvalidArgumentException: Output path must end with .pdf extension | เส้นทางไม่ได้ลงท้ายด้วย .pdf (ไม่คำนึงถึงตัวพิมพ์เล็ก-ใหญ่) | ใช้ส่วนต่อท้าย .pdf (หรือ .PDF) |
| งานทำงานได้ แต่ไฟล์ว่างเปล่าหรือไม่ถูกต้อง | closure ของ builder ไม่ได้คืนเอกสารที่กำหนดค่าไว้ | คืนค่าเอกสารจาก builder งานจะบันทึกค่าที่คืนกลับมา |
| งานใช้คิวหรือ timeout ที่ไม่ถูกต้อง | nextpdf.queue.* ไม่ได้ตั้งค่าตามที่คาดไว้ | ตั้งค่า queue.queue, queue.connection และ queue.timeout ส่วน tries และ backoff ต้องทำผ่าน subclass |
การตรวจสอบเส้นทางเกิดขึ้นภายใน handle() บน worker ดังนั้นเส้นทางที่ไม่ถูกต้องจะล้มเหลวระหว่างการทำงาน ไม่ใช่ตอน dispatch ลักษณะนี้เป็นการออกแบบโดยตั้งใจ: worker จะตรวจสอบความถูกต้องของ payload คิวที่ถูก serialize ณ จุดที่ใช้งาน payload นั้น
การตอบกลับ HTTP และชื่อไฟล์
หัวข้อที่มีชื่อว่า “การตอบกลับ HTTP และชื่อไฟล์”| อาการ | สาเหตุที่ตรวจสอบยืนยันแล้ว | วิธีแก้ไข |
|---|---|---|
ชื่อไฟล์ดาวน์โหลดกลายเป็น document.pdf โดยไม่คาดคิด | มีการส่งชื่อไฟล์ว่างเปล่า factory จึงใช้ค่าเริ่มต้น | ส่งชื่อไฟล์ที่ไม่ว่างเปล่า |
| ชื่อไฟล์สูญเสียส่วนเส้นทางหรืออักขระพิเศษไป | ตัวทำความสะอาดชื่อไฟล์จะตัดตัวคั่นเส้นทาง อักขระควบคุม และ null byte ออก | ส่งเฉพาะชื่อไฟล์พื้นฐานเท่านั้น การเพิ่มความแข็งแกร่งด้านความปลอดภัยนี้เป็นพฤติกรรมที่คาดหมายไว้ |
| ชื่อไฟล์ที่ไม่ใช่ ASCII แสดงเป็นอักขระเพี้ยน (mojibake) ในไคลเอนต์บางตัว | การตอบกลับจะส่ง Request for Comments 5987 (RFC 5987) filename*= สำหรับชื่อที่ไม่ใช่ ASCII ส่วนไคลเอนต์รุ่นเก่าจะอ่านค่าสำรองแบบ ASCII | เป็นพฤติกรรมที่คาดหมายไว้ ให้ระบุชื่อที่ปลอดภัยแบบ ASCII หากต้องให้ไคลเอนต์รุ่นเก่าตรงกันทุกตัวอักษร |
การตอบกลับแบบสตรีมไม่มี Content-Length | การตอบกลับแบบสตรีมจะละ Content-Length ไว้ตามการออกแบบ (เอาต์พุตแบบ chunk) | เป็นพฤติกรรมที่คาดหมายไว้ ใช้ inline()/download() แบบไม่ใช่สตรีมหากจำเป็นต้องมี header ความยาว |
ตัวอย่างโค้ด — การวินิจฉัย
หัวข้อที่มีชื่อว่า “ตัวอย่างโค้ด — การวินิจฉัย”# Confirm the provider is discoveredphp artisan package:discover --ansi
# Inspect merged configurationphp artisan tinker --execute="dump(config('nextpdf.queue'));"<?php
declare(strict_types=1);
use NextPDF\Contracts\SignerInterface;
$signer = app(SignerInterface::class);
if ($signer === null) { // Signing not configured, or nextpdf/premium not installed. // Continue without a signature, or fail with a clear message.}กรณี edge case และข้อควรระวัง
หัวข้อที่มีชื่อว่า “กรณี edge case และข้อควรระวัง”- เมื่อใช้ deferred provider การติดตั้งใหม่อาจดูเหมือน “เสีย” จนกว่าจะเกิดการ resolve ที่เกี่ยวข้องครั้งแรก ให้ถือว่าการที่
package:discoverแสดงรายการแพ็กเกจเป็นสัญญาณว่าสำเร็จ - เมื่อ
image_cache_mb = nullแพ็กเกจจะใช้ค่าสำรอง 50 MB มีเฉพาะ0เท่านั้นที่ปิดใช้งานแคช รายงานเรื่อง “แคชไม่ยอมปิดใช้งาน” มักเกิดจากการตั้งค่าเป็นnullนั่นเอง - เมื่อ
signature.level = nullแพ็กเกจจะใช้ค่าสำรองเป็น PDF Advanced Electronic Signatures (PAdES) B-B โดยไม่แจ้งเตือน รายงานเรื่อง “B-B ที่ไม่คาดคิด” มักเกิดจากการไม่ได้ตั้งค่าระดับไว้
ประสิทธิภาพ
หัวข้อที่มีชื่อว่า “ประสิทธิภาพ”หากคำขอช่วงแรกบน worker ที่ทำงานระยะยาวช้า แสดงว่า font registry กำลัง parse ตามความต้องการ กำหนดค่า nextpdf.preload_fonts เพื่อให้การ warmup ทำงานครั้งเดียวตอนบูต worker ดูรายละเอียดได้ที่ /integrations/laravel/configuration/ และ /integrations/laravel/boot-and-discovery/
หมายเหตุด้านความปลอดภัย
หัวข้อที่มีชื่อว่า “หมายเหตุด้านความปลอดภัย”การปฏิเสธค่าเส้นทางและชื่อไฟล์เป็นมาตรการควบคุมด้านความปลอดภัย ไม่ใช่บั๊ก ไม่ควรเลี่ยงมาตรการเหล่านี้ด้วยการ decode ล่วงหน้าหรือผ่อนคลายการตรวจสอบ ให้ส่งเอาต์พุตไฟล์ผ่านเส้นทางจัดเก็บที่ควบคุมได้แทน ดู /integrations/laravel/security-and-operations/
ความสอดคล้องตามมาตรฐาน
หัวข้อที่มีชื่อว่า “ความสอดคล้องตามมาตรฐาน”| ข้อกล่าวอ้าง | แหล่งที่มา | ข้อ | รหัสอ้างอิง (reference_id) |
|---|---|---|---|
| entry ของ container ที่ไม่มีอยู่จะโยน not-found เมื่อเรียก get() | PSR-11 Container | §1.1.2 |
ดูเพิ่มเติม
หัวข้อที่มีชื่อว่า “ดูเพิ่มเติม”- /integrations/laravel/install/ — ขั้นตอนการค้นพบและการ publish
- /integrations/laravel/configuration/ — ทุกคีย์และค่าเริ่มต้น
- /integrations/laravel/production-usage/ — รูปแบบ dependency injection (DI) และคิว
- /integrations/laravel/security-and-operations/ — เหตุผลที่มีการตรวจสอบเส้นทาง