Solución de problemas del paquete NextPDF para Laravel
De un vistazo
Sección titulada «De un vistazo»Esta página relaciona cada modo de fallo observable del paquete con su causa raíz, verificada en el código fuente. Cada entrada identifica el síntoma, la causa y la solución.
Instalación
Sección titulada «Instalación»composer require nextpdf/laravelphp artisan vendor:publish --tag=nextpdf-configPanorama conceptual
Sección titulada «Panorama conceptual»La mayoría de los problemas reportados se agrupan en cinco categorías: descubrimiento, resolución en el contenedor, firma, trabajos en cola y nombres de archivo HTTP. El paquete falla de forma explícita por diseño. Las funciones opcionales sin configurar devuelven null, y las entradas inseguras lanzan excepciones tipadas. Por eso, el síntoma suele apuntar directamente a la causa.
Superficie de la API: del síntoma a la causa
Sección titulada «Superficie de la API: del síntoma a la causa»Descubrimiento y arranque
Sección titulada «Descubrimiento y arranque»| Síntoma | Causa verificada | Solución |
|---|---|---|
| El proveedor no se registra después de la instalación | La aplicación quedó excluida mediante extra.laravel.dont-discover | Quitar el paquete de dont-discover, o registrar NextPdfServiceProvider manualmente en bootstrap/providers.php |
config('nextpdf') está vacío | La configuración no se fusionó porque no se resolvió ninguna vinculación anunciada (proveedor diferido) | Resolver cualquier entrada de provides(), o confirmar el descubrimiento con php artisan package:discover --ansi |
La publicación no creó config/nextpdf.php | Etiqueta de publicación incorrecta | Usar la etiqueta exacta: php artisan vendor:publish --tag=nextpdf-config |
| RuntimeException: «NextPDF requires the ext-mbstring/ext-zlib PHP extension» | Falta en tiempo de ejecución una extensión de PHP requerida | Instalar o habilitar mbstring y zlib en php.ini |
Resolución en el contenedor
Sección titulada «Resolución en el contenedor»| Síntoma | Causa verificada | Solución |
|---|---|---|
app(SignerInterface::class) devuelve null | Firma deshabilitada o certificado vacío en nextpdf.signature | Definir signature.enabled = true y un signature.certificate válido; instalar nextpdf/premium para obtener la implementación concreta del firmante |
app(TsaClient::class) devuelve null | nextpdf.tsa.url está vacío | Configurar tsa.url (y las credenciales/pins que se necesiten) |
| Clase no encontrada para un tipo de versión PDF/A | nextpdf.pdfa no es null, pero nextpdf/premium no está instalado | Instalar nextpdf/premium, o volver a poner pdfa en null |
| Clase no encontrada al resolver un contrato de factura electrónica | Las vinculaciones están registradas, pero las implementaciones concretas de Premium no están presentes | Instalar nextpdf/premium; los contratos de factura electrónica se resuelven de forma diferida y solo dan error en la primera resolución si falta Premium |
| El mismo documento se modificó en dos operaciones lógicas distintas | La vinculación del documento es una factory; se reutilizó una única instancia ya resuelta | Resolver un PdfDocumentInterface nuevo por cada documento |
Un contenedor sin la entrada lanza una excepción de no encontrado en get() (PSR-11 §1.1.2). Los contratos de factura electrónica están vinculados, así que el has() del contenedor devuelve true. El error aparece porque falta la implementación concreta de Premium en el momento de la construcción, no por el contenedor en sí.
Trabajos en cola
Sección titulada «Trabajos en cola»| Síntoma | Causa verificada | Solución |
|---|---|---|
InvalidArgumentException: Path traversal sequences are not allowed | La ruta de salida contiene un segmento .. de recorrido | Usar una ruta absoluta y sin recorrido (traversal) dentro del directorio de almacenamiento |
InvalidArgumentException: Stream wrappers are not allowed | La ruta coincide con un esquema como php:// | Usar una ruta de sistema de archivos sin esquema |
InvalidArgumentException: Output path contains null bytes | La ruta contiene un byte nulo \0 | Sanear la ruta antes del despacho |
InvalidArgumentException: Output path must end with .pdf extension | La ruta no termina en .pdf (sin distinguir mayúsculas y minúsculas) | Usar un sufijo .pdf (o .PDF) |
| El trabajo se ejecuta, pero el archivo está vacío o es incorrecto | El closure constructor no devolvió el documento configurado | Devolver el documento desde el closure constructor; el valor devuelto es lo que se guarda |
| El trabajo usa la cola o el tiempo de espera equivocados | nextpdf.queue.* no está configurado como se esperaba | Definir queue.queue, queue.connection, queue.timeout; tries y backoff requieren crear una subclase |
Las comprobaciones de ruta se ejecutan dentro de handle() en el worker, así que una ruta incorrecta falla durante la ejecución, no durante el despacho. Esto es intencional: la carga útil serializada en el transporte de la cola se valida donde se consume.
Respuestas HTTP y nombres de archivo
Sección titulada «Respuestas HTTP y nombres de archivo»| Síntoma | Causa verificada | Solución |
|---|---|---|
El nombre de archivo de descarga es document.pdf de forma inesperada | Se pasó un nombre de archivo vacío; la factory le asigna el valor predeterminado | Pasar un nombre de archivo no vacío |
| El nombre de archivo perdió su ruta o sus caracteres especiales | El saneador de nombres de archivo elimina los separadores de ruta, los caracteres de control y los bytes nulos | Pasar solo el nombre base del archivo; este endurecimiento es esperado |
| Un nombre de archivo no ASCII muestra mojibake en algunos clientes | RFC 5987 filename*= se emite para los nombres no ASCII; los clientes antiguos leen el respaldo ASCII | Esperado; proporcionar un nombre seguro en ASCII si un cliente heredado debe coincidir exactamente |
La respuesta transmitida (streamed) no tiene Content-Length | Las respuestas transmitidas (streamed) omiten Content-Length por diseño (salida fragmentada) | Esperado; usar los métodos no transmitidos inline()/download() si se requiere un encabezado de longitud |
Ejemplo de código: diagnóstico
Sección titulada «Ejemplo de código: diagnóstico»# 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 límite y trampas
Sección titulada «Casos límite y trampas»- El proveedor diferido implica que una instalación nueva puede parecer «rota» hasta la primera resolución relevante. La señal correcta de éxito es que
package:discoverliste el paquete. image_cache_mb = nullusa 50 MB como valor predeterminado; solo0deshabilita la caché. Un reporte de «la caché no se deshabilita» normalmente había usadonull.signature.level = nullusa silenciosamente PAdES B-B como valor predeterminado. Un reporte de «B-B inesperado» normalmente dejó el nivel sin definir.
Rendimiento
Sección titulada «Rendimiento»Si las primeras solicitudes en un worker de larga duración son lentas, el registro de fuentes se analiza bajo demanda. Completar nextpdf.preload_fonts permite que el calentamiento se ejecute una sola vez al arrancar el worker. Consultar /integrations/laravel/configuration/ y /integrations/laravel/boot-and-discovery/ para más detalles.
Notas de seguridad
Sección titulada «Notas de seguridad»Los rechazos de ruta y de nombre de archivo son controles de seguridad, no errores. No se deben eludir predecodificando ni relajando las comprobaciones. En su lugar, se debe enrutar la salida de archivos a través de una ruta de almacenamiento controlada. Consultar /integrations/laravel/security-and-operations/.
Conformidad
Sección titulada «Conformidad»| Afirmación | Fuente | Cláusula | reference_id |
|---|---|---|---|
| Una entrada ausente en el contenedor lanza una excepción de no encontrado en get() | Contenedor PSR-11 | §1.1.2 |
Ver también
Sección titulada «Ver también»- /integrations/laravel/install/ — pasos de descubrimiento y publicación
- /integrations/laravel/configuration/ — cada clave y su valor predeterminado
- /integrations/laravel/production-usage/ — patrones de DI y de cola
- /integrations/laravel/security-and-operations/ — por qué existen las comprobaciones de ruta