ข้ามไปยังเนื้อหา

การแก้ไขปัญหาแพ็กเกจ NextPDF Laravel

ใช้หน้านี้เพื่อเทียบปัญหาของแพ็กเกจที่พบกับสาเหตุจริงระดับซอร์สโค้ดที่ตรวจสอบยืนยันแล้ว แต่ละรายการระบุอาการ สาเหตุ และวิธีแก้ไข

Terminal window
composer require nextpdf/laravel
php artisan vendor:publish --tag=nextpdf-config

ปัญหาส่วนใหญ่ที่รายงานเข้ามาอยู่ในห้ากลุ่ม ได้แก่ การค้นพบ การ resolve ใน container การลงลายเซ็น งานคิว และชื่อไฟล์ของ Hypertext Transfer Protocol (HTTP) แพ็กเกจนี้ออกแบบมาให้แสดงความล้มเหลวอย่างชัดเจน ฟีเจอร์ทางเลือกที่ไม่ได้กำหนดค่าไว้จะคืนค่า null และอินพุตที่ไม่ปลอดภัยจะทำให้เกิด exception แบบระบุชนิด โดยทั่วไป อาการจะชี้ไปยังสาเหตุโดยตรง

อาการสาเหตุที่ตรวจสอบยืนยันแล้ววิธีแก้ไข
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
อาการสาเหตุที่ตรวจสอบยืนยันแล้ววิธีแก้ไข
app(SignerInterface::class) คืนค่า nullการลงลายเซ็นถูกปิดใช้งาน หรือค่าใบรับรองว่างเปล่าใน nextpdf.signatureตั้งค่า signature.enabled = true พร้อม signature.certificate ที่ใช้งานได้ และติดตั้ง nextpdf/premium เพื่อให้มี concrete ของ signer
app(TsaClient::class) คืนค่า nullnextpdf.tsa.url ว่างเปล่ากำหนดค่า tsa.url (และ credentials/pins ตามความจำเป็น)
Class-not-found สำหรับชนิดของเวอร์ชัน PDF/Anextpdf.pdfa ไม่ใช่ null แต่ nextpdf/premium ยังไม่ได้ติดตั้งติดตั้ง nextpdf/premium หรือตั้งค่า pdfa กลับเป็น null
Class-not-found ขณะ resolve contract ของ e-invoicebinding ถูกลงทะเบียนแล้ว แต่ไม่มี 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 นั้น

อาการสาเหตุที่ตรวจสอบยืนยันแล้ววิธีแก้ไข
ชื่อไฟล์ดาวน์โหลดกลายเป็น 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 ความยาว
Terminal window
# Confirm the provider is discovered
php artisan package:discover --ansi
# Inspect merged configuration
php artisan tinker --execute="dump(config('nextpdf.queue'));"
resource: src/Laravel/NextPdfServiceProvider.php (null-check pattern)
<?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.
}
  • เมื่อใช้ 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/ — เหตุผลที่มีการตรวจสอบเส้นทาง