跳转到内容

NextPDF Laravel 包故障排查

本页将该包每一种可观察到的故障模式映射到源码中已验证的根本原因。每个条目都列出了症状、原因和修复方法。

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

大多数已报告问题可归为五类:发现、容器解析、签名、队列任务和 HTTP 文件名。该包按设计会显式报错:未配置的可选功能返回 null,不安全输入则抛出带类型的异常。因此,症状通常会直接指向原因。

症状已验证的原因修复方法
安装后服务提供者未注册应用已通过 extra.laravel.dont-discover 选择退出自动发现将该包从 dont-discover 中移除,或将 NextPdfServiceProvider 手动注册到 bootstrap/providers.php
config('nextpdf') 为空配置未合并,因为尚未解析任何已声明的绑定(延迟服务提供者)解析任意一个 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 扩展安装或启用 mbstringzlib,并在 php.ini 中配置它们
症状已验证的原因修复方法
app(SignerInterface::class) 返回 nullnextpdf.signature 中,签名被禁用或证书为空设置 signature.enabled = true 和有效的 signature.certificate;安装 nextpdf/premium 以获得签名器实现
app(TsaClient::class) 返回 nullnextpdf.tsa.url 为空配置 tsa.url(并按需配置 credentials/pins)
某个 PDF/A 版本类型找不到对应的类nextpdf.pdfa 非 null,但 nextpdf/premium 未安装安装 nextpdf/premium,或将 pdfa 改回 null
解析电子发票契约时找不到对应的类绑定已注册,但缺少 Premium 的具体实现安装 nextpdf/premium;电子发票契约会延迟解析,只有在没有 Premium 时,首次解析才会报错
同一文档在两个逻辑操作之间被修改文档绑定是工厂;你复用了同一个已解析实例为每个文档解析一个全新的 PdfDocumentInterface 实例

当容器中没有对应条目时,get() 会抛出未找到异常(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 字节在派发前对路径进行净化
InvalidArgumentException: Output path must end with .pdf extension该路径不以 .pdf 结尾(不区分大小写)使用 .pdf(或 .PDF)后缀
任务运行了,但文件为空或内容有误构建器闭包没有返回已配置的文档从构建器中返回该文档;被保存的正是返回值
任务使用了错误的队列或超时设置nextpdf.queue.* 未按预期设置设置 queue.queuequeue.connectionqueue.timeouttriesbackoff 需要通过子类设置

路径检查在工作进程的 handle() 内部运行,因此错误路径会在执行时失败,而不是在派发时失败。这是有意设计:队列传输中的序列化载荷会在被消费的位置进行校验。

症状已验证的原因修复方法
下载文件名意外地变成了 document.pdf 这个默认名传入了空文件名;工厂为其设置了默认值传入一个非空文件名
文件名丢失了路径或特殊字符文件名净化逻辑会剥离路径分隔符、控制字符和空字节只传入基础文件名;这是预期的加固措施
非 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.
}
  • 延迟服务提供者意味着,全新安装在首次相关解析之前可能看起来像是「坏掉了」。正确的成功信号是 package:discover 列出了该包。
  • image_cache_mb = null 会回退为 50 MB;只有 0 才会禁用缓存。报告「缓存未禁用」的情况通常用的是 null
  • signature.level = null 会静默回退为 PAdES B-B。报告「意外的 B-B」的情况通常是没有设置 level。

如果长生命周期工作进程上的首批请求很慢,原因是字体注册表在按需解析。配置 nextpdf.preload_fonts,让预热在工作进程启动时只运行一次。详情请参阅 /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/ — 为什么存在这些路径检查