跳到內容

NextPDF Laravel 套件疑難排解

本頁會將套件中每一種可觀察的故障模式,對應到原始碼裡經查證的根本原因。每個條目都會列出症狀、原因與修正方式。

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

多數回報的問題可歸為五類:探索、容器 resolve(解析)、簽章、佇列任務與 HTTP 檔名。此套件刻意設計成會明確報錯。未設定的選用功能會回傳 null,不安全的輸入則會丟出具型別的例外。因此,症狀通常會直接指向原因。

症狀經查證的原因修正
安裝後 provider 未註冊應用程式透過 extra.laravel.dont-discover 選擇退出自動探索dont-discover 中移除此套件,或手動將 NextPdfServiceProvider 註冊於 bootstrap/providers.php
config('nextpdf') 是空的因為尚未解析任何已宣告的繫結(延遲載入的 provider),組態尚未被合併解析任一個 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」(執行階段擲出例外,表示缺少必要的 PHP 擴充功能)執行階段缺少必要的 PHP 擴充功能安裝或啟用 mbstringzlib,並在 php.ini 中設定
症狀經查證的原因修正
app(SignerInterface::class) 回傳 null簽章被停用,或 nextpdf.signature 中的憑證為空設定 signature.enabled = true 並提供有效的 signature.certificate;安裝 nextpdf/premium 以取得簽章器的具體實作
app(TsaClient::class) 回傳 nullnextpdf.tsa.url 是空的設定 tsa.url(並視需要設定 credentials/pins)
某個 PDF/A 版本型別找不到類別(Class-not-found)nextpdf.pdfa 非 null,但未安裝 nextpdf/premium 套件安裝 nextpdf/premium,或將 pdfa 設回 null
解析某個電子發票合約時找不到類別(Class-not-found)繫結雖已註冊,但 Premium 的具體實作不存在安裝 nextpdf/premium;電子發票合約會延遲解析,只有在缺少 Premium 且首次解析時才會報錯
同一份文件在兩個邏輯操作之間被變動文件繫結是一個工廠(factory);你重複使用了同一個已解析的實例為每份文件解析一個全新的 PdfDocumentInterface 實例

當容器沒有對應項目時,會在呼叫 get() 時丟出 not-found 例外(PSR-11 §1.1.2)。電子發票合約是已繫結的,所以容器的 has() 會回傳 true。錯誤會在建構時因缺少 Premium 具體實作而浮現,而不是來自容器本身。

症狀經查證的原因修正
InvalidArgumentException: Path traversal sequences are not allowed輸出路徑含有 .. 片段在你的儲存目錄下,使用絕對且不含路徑穿越的路徑
InvalidArgumentException: Stream wrappers are not allowed路徑符合協定前綴,例如 php://使用單純的檔案系統路徑
InvalidArgumentException: Output path contains null bytes路徑含有一個 \0 位元組在 dispatch 之前先清理路徑
InvalidArgumentException: Output path must end with .pdf extension路徑結尾並非 .pdf(不分大小寫)使用 .pdf(或 .PDF)作為副檔名
任務已執行,但檔案為空或內容錯誤builder 閉包未回傳已設定好的文件從 builder 回傳該文件;回傳值就是會被儲存的內容
任務使用了錯誤的佇列或逾時設定nextpdf.queue.* 未依預期設定設定 queue.queuequeue.connectionqueue.timeouttriesbackoff 則需要透過子類別覆寫

這些路徑檢查會在 worker 上的 handle() 內執行,所以錯誤路徑會在執行時失敗,而不是在 dispatch 時失敗。這是刻意設計:佇列傳輸中的序列化負載會在被消費的位置進行驗證。

症狀經查證的原因修正
下載檔名意外變成 document.pdf傳入空白檔名;工廠(factory)會套用這個預設值請傳入一個非空白的檔名
檔名遺失了路徑或特殊字元檔名清理器會去除路徑分隔字元、控制字元與 null 位元組只傳入基底檔名;這是預期內的強化措施
非 ASCII 檔名在某些用戶端會顯示為亂碼對於非 ASCII 名稱會輸出 RFC 5987 的 filename*=;舊版用戶端則會讀取 ASCII 後備值這是預期行為;若特定舊版用戶端必須完全相符,請提供一個 ASCII 安全的名稱
串流回應沒有 Content-Length串流回應依設計省略 Content-Length(分塊輸出)這是預期行為;若必須有長度標頭,請改用非串流的 inline()/download(),即可取得該標頭
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.
}
  • 延遲載入的 provider 代表全新安裝在第一次進行相關解析前,可能看起來像是「壞掉了」。正確的成功訊號是 package:discover 列出了此套件。
  • image_cache_mb = null 會退回 50 MB;只有 0 才會停用快取。回報「快取停用無效」的情況通常都是使用了 null
  • signature.level = null 會靜默退回為 PAdES B-B。回報「出現非預期的 B-B」的情況通常都是未設定 level。

如果長時間執行的 worker 在前幾個請求特別慢,代表字型登錄表正在隨需剖析。請填入 nextpdf.preload_fonts,讓暖機只在 worker 啟動時執行一次。詳情請見 /integrations/laravel/configuration/ 與 /integrations/laravel/boot-and-discovery/ 兩節。

系統拒絕特定路徑與檔名是安全控制,並非錯誤。請勿透過預先解碼或放寬檢查來繞過它們。請改為將檔案輸出導向受控的儲存路徑。請見 /integrations/laravel/security-and-operations/。

主張來源條款參考 ID
缺少容器項目時,會在 get() 時丟出 not-foundPSR-11 容器§1.1.2
  • /integrations/laravel/install/ — 探索與 publish 步驟
  • /integrations/laravel/configuration/ — 每個鍵與其預設值
  • /integrations/laravel/production-usage/ — DI 與佇列模式
  • /integrations/laravel/security-and-operations/ — 這些路徑檢查存在的原因