Ir al contenido

Solución de problemas del paquete NextPDF para Laravel

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.

Ventana de terminal
composer require nextpdf/laravel
php artisan vendor:publish --tag=nextpdf-config

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»
SíntomaCausa verificadaSolución
El proveedor no se registra después de la instalaciónLa aplicación quedó excluida mediante extra.laravel.dont-discoverQuitar el paquete de dont-discover, o registrar NextPdfServiceProvider manualmente en bootstrap/providers.php
config('nextpdf') está vacíoLa 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.phpEtiqueta de publicación incorrectaUsar 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 requeridaInstalar o habilitar mbstring y zlib en php.ini
SíntomaCausa verificadaSolución
app(SignerInterface::class) devuelve nullFirma deshabilitada o certificado vacío en nextpdf.signatureDefinir signature.enabled = true y un signature.certificate válido; instalar nextpdf/premium para obtener la implementación concreta del firmante
app(TsaClient::class) devuelve nullnextpdf.tsa.url está vacíoConfigurar tsa.url (y las credenciales/pins que se necesiten)
Clase no encontrada para un tipo de versión PDF/Anextpdf.pdfa no es null, pero nextpdf/premium no está instaladoInstalar nextpdf/premium, o volver a poner pdfa en null
Clase no encontrada al resolver un contrato de factura electrónicaLas vinculaciones están registradas, pero las implementaciones concretas de Premium no están presentesInstalar 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 distintasLa vinculación del documento es una factory; se reutilizó una única instancia ya resueltaResolver 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í.

SíntomaCausa verificadaSolución
InvalidArgumentException: Path traversal sequences are not allowedLa ruta de salida contiene un segmento .. de recorridoUsar una ruta absoluta y sin recorrido (traversal) dentro del directorio de almacenamiento
InvalidArgumentException: Stream wrappers are not allowedLa ruta coincide con un esquema como php://Usar una ruta de sistema de archivos sin esquema
InvalidArgumentException: Output path contains null bytesLa ruta contiene un byte nulo \0Sanear la ruta antes del despacho
InvalidArgumentException: Output path must end with .pdf extensionLa 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 incorrectoEl closure constructor no devolvió el documento configuradoDevolver el documento desde el closure constructor; el valor devuelto es lo que se guarda
El trabajo usa la cola o el tiempo de espera equivocadosnextpdf.queue.* no está configurado como se esperabaDefinir 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.

SíntomaCausa verificadaSolución
El nombre de archivo de descarga es document.pdf de forma inesperadaSe pasó un nombre de archivo vacío; la factory le asigna el valor predeterminadoPasar un nombre de archivo no vacío
El nombre de archivo perdió su ruta o sus caracteres especialesEl saneador de nombres de archivo elimina los separadores de ruta, los caracteres de control y los bytes nulosPasar solo el nombre base del archivo; este endurecimiento es esperado
Un nombre de archivo no ASCII muestra mojibake en algunos clientesRFC 5987 filename*= se emite para los nombres no ASCII; los clientes antiguos leen el respaldo ASCIIEsperado; proporcionar un nombre seguro en ASCII si un cliente heredado debe coincidir exactamente
La respuesta transmitida (streamed) no tiene Content-LengthLas 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
Ventana de terminal
# 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.
}
  • 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:discover liste el paquete.
  • image_cache_mb = null usa 50 MB como valor predeterminado; solo 0 deshabilita la caché. Un reporte de «la caché no se deshabilita» normalmente había usado null.
  • signature.level = null usa silenciosamente PAdES B-B como valor predeterminado. Un reporte de «B-B inesperado» normalmente dejó el nivel sin definir.

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.

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/.

AfirmaciónFuenteCláusulareference_id
Una entrada ausente en el contenedor lanza una excepción de no encontrado en get()Contenedor PSR-11§1.1.2
  • /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