Solução de problemas do pacote Laravel para NextPDF
Visão geral
Seção intitulada “Visão geral”Use esta página para mapear cada falha observável no pacote até sua causa raiz verificada no código-fonte. Cada entrada traz o sintoma, a causa e a correção.
Instalação
Seção intitulada “Instalação”composer require nextpdf/laravelphp artisan vendor:publish --tag=nextpdf-configVisão geral conceitual
Seção intitulada “Visão geral conceitual”A maioria dos problemas relatados se encaixa em cinco grupos: descoberta, resolução no container, assinatura, jobs de fila e nomes de arquivo do Hypertext Transfer Protocol (HTTP). Por design, o pacote falha de forma explícita. Recursos opcionais que não estão configurados retornam null, e entradas inseguras lançam exceções tipadas. O sintoma costuma apontar diretamente para a causa.
Superfície da API — do sintoma à causa
Seção intitulada “Superfície da API — do sintoma à causa”Descoberta e boot
Seção intitulada “Descoberta e boot”| Sintoma | Causa verificada | Correção |
|---|---|---|
| O provider não é registrado após a instalação | A aplicação desativa a descoberta com extra.laravel.dont-discover | Remova o pacote de dont-discover ou registre NextPdfServiceProvider manualmente em bootstrap/providers.php |
config('nextpdf') está vazio | A configuração não foi mesclada porque nenhum binding anunciado foi resolvido (provider adiado) | Resolva qualquer entrada de provides(), ou confirme a descoberta com php artisan package:discover --ansi |
config/nextpdf.php não foi criado pela publicação | Tag de publicação incompatível | Use a tag exata: php artisan vendor:publish --tag=nextpdf-config |
| RuntimeException: “NextPDF requires the ext-mbstring/ext-zlib PHP extension” | Uma extensão obrigatória do Hypertext Preprocessor (PHP) não está disponível em tempo de execução | Instale ou habilite mbstring e zlib em php.ini |
Resolução no container
Seção intitulada “Resolução no container”| Sintoma | Causa verificada | Correção |
|---|---|---|
app(SignerInterface::class) retorna null | A assinatura está desativada ou o certificado está vazio em nextpdf.signature | Defina signature.enabled = true e um signature.certificate válido; instale nextpdf/premium para fornecer o concreto do signatário |
app(TsaClient::class) retorna null | nextpdf.tsa.url está vazio | Configure tsa.url (e credentials/pins conforme necessário) |
| Class-not-found para um tipo de versão PDF/A | nextpdf.pdfa não é null, mas nextpdf/premium não está instalado | Instale nextpdf/premium ou defina pdfa novamente como null |
| Class-not-found ao resolver um contrato de e-invoice | Os bindings estão registrados, mas os concretos do Premium estão ausentes | Instale nextpdf/premium; os contratos de e-invoice são resolvidos de forma lazy e falham apenas na primeira resolução sem o Premium |
| Mesmo documento mutado em duas operações lógicas | O binding do documento é uma factory; você reutilizou uma única instância já resolvida | Resolva um novo PdfDocumentInterface para cada documento |
Um container sem determinada entrada lança uma exceção not-found em get() (PHP Standard Recommendation 11 (PSR-11) §1.1.2). Os contratos de e-invoice estão vinculados, portanto o has() do container retorna true. O concreto ausente do Premium gera o erro no momento da construção, não no próprio container.
Jobs de fila
Seção intitulada “Jobs de fila”| Sintoma | Causa verificada | Correção |
|---|---|---|
InvalidArgumentException: Path traversal sequences are not allowed | O caminho de saída contém um segmento .. de travessia | Use um caminho absoluto, sem segmentos de travessia, dentro do diretório de armazenamento |
InvalidArgumentException: Stream wrappers are not allowed | O caminho usa um esquema como php:// | Use um caminho simples do sistema de arquivos |
InvalidArgumentException: Output path contains null bytes | O caminho contém um byte \0 nulo | Sanitize o caminho antes do dispatch |
InvalidArgumentException: Output path must end with .pdf extension | O caminho não termina em .pdf (sem diferenciar maiúsculas de minúsculas) | Use um sufixo .pdf (ou .PDF) |
| O job executa, mas o arquivo está vazio ou incorreto | A closure de construção não retornou o documento configurado | Retorne o documento pela closure de construção; o job salva o valor retornado |
| O job usa a fila ou o timeout errado | nextpdf.queue.* não está definido conforme esperado | Defina queue.queue, queue.connection e queue.timeout; tries e backoff exigem subclassing |
As verificações de caminho são executadas dentro de handle() no worker; portanto, um caminho inválido falha durante a execução, e não no dispatch. Isso é intencional: o worker valida o payload serializado da fila no momento em que o consome.
Respostas HTTP e nomes de arquivo
Seção intitulada “Respostas HTTP e nomes de arquivo”| Sintoma | Causa verificada | Correção |
|---|---|---|
O nome de arquivo de download é document.pdf inesperadamente | Você passou um nome de arquivo vazio; a factory usa o padrão | Passe um nome de arquivo não vazio |
| O nome de arquivo perdeu o caminho ou os caracteres especiais | O sanitizador de nome de arquivo remove separadores de caminho, caracteres de controle e bytes nulos | Passe apenas o nome de arquivo base; esse reforço de segurança é esperado |
| O nome de arquivo não ASCII aparece como mojibake em alguns clientes | A resposta emite o Request for Comments 5987 (RFC 5987) filename*= para nomes não ASCII; clientes antigos leem o fallback ASCII | Esperado; forneça um nome compatível com ASCII se um cliente legado precisar corresponder exatamente |
A resposta em streaming não tem Content-Length | As respostas em streaming omitem Content-Length por design (saída em chunks) | Esperado; use as variantes não-streaming inline()/download() se for necessário um cabeçalho de comprimento |
Exemplo de código — diagnósticos
Seção intitulada “Exemplo de código — diagnósticos”# 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.}Casos extremos e pegadinhas
Seção intitulada “Casos extremos e pegadinhas”- Com o provider adiado, uma instalação recém-feita pode parecer “quebrada” até a primeira resolução relevante. Considere o fato de
package:discoverlistar o pacote como o sinal de sucesso. - Quando
image_cache_mb = null, o pacote recorre a 50 MB; somente0desativa o cache. Um relato de “cache não desativa” normalmente usounull. - Quando
signature.level = null, o pacote recorre silenciosamente ao PDF Advanced Electronic Signatures (PAdES) B-B. Um relato de “B-B inesperado” normalmente deixou o nível indefinido.
Desempenho
Seção intitulada “Desempenho”Se as primeiras requisições em um worker de longa duração estiverem lentas, o registro de fontes está fazendo o parsing sob demanda. Preencha nextpdf.preload_fonts para que o aquecimento seja executado uma vez no boot do worker. Consulte /integrations/laravel/configuration/ e /integrations/laravel/boot-and-discovery/ para mais detalhes.
Notas de segurança
Seção intitulada “Notas de segurança”As rejeições de caminho e de nome de arquivo são controles de segurança, não bugs. Não as contorne pré-decodificando ou relaxando as verificações. Em vez disso, encaminhe a saída de arquivos por um caminho de armazenamento controlado. Consulte /integrations/laravel/security-and-operations/.
Conformidade
Seção intitulada “Conformidade”| Afirmação | Fonte | Cláusula | reference_id |
|---|---|---|---|
| Entrada ausente no container lança not-found em get() | PSR-11 Container | §1.1.2 |
Consulte também
Seção intitulada “Consulte também”- /integrations/laravel/install/ — passos de descoberta e publicação
- /integrations/laravel/configuration/ — cada chave e seu valor padrão
- /integrations/laravel/production-usage/ — injeção de dependência (DI) e padrões de fila
- /integrations/laravel/security-and-operations/ — por que as verificações de caminho existem