Ir al contenido

Uso en producción con CodeIgniter 4

Los controladores en producción dependen de servicios concretos de NextPDF. Gestionan de forma explícita la jerarquía de excepciones documentada y emiten señales de observabilidad. El trabajo pesado de generación de PDF se desplaza fuera de la solicitud mediante la cola de CodeIgniter 4.

CodeIgniter 4 resuelve los servicios del paquete a través de su localizador. El patrón de localizador de servicios consiste en entregar un contenedor a un objeto para que el propio objeto pueda obtener sus dependencias. Ese patrón está desaconsejado (PSR-11 §1.3, modal SHOULD NOT). Para seguir esa recomendación, se debe resolver cada servicio de NextPDF una sola vez en el límite del controlador y pasar hacia dentro el objeto concreto. No se debe pasar la clase Services —ni un contenedor— al código de dominio.

Todos los ejemplos de PHP declaran declare(strict_types=1); en su propia línea (PSR-12 §x1.x3.p34).

Aspecto de producciónSuperficie verificada
Resolver serviciosServices::pdf(false), Services::pdfDocument(false), Services::documentFactory()
Construir la respuestaPdfResponse::download() / inline()DownloadResponse
Capturar los fallosNextPDF\Exception\NextPdfException (tipo base del ecosistema)
Generación asíncronaGeneratePdfJob registrado en Config\Queue::$jobHandlers
Validaciones de ruta y de invocableGeneratePdfJob lanza InvalidArgumentException

Controlador de producción: gestión de errores y observabilidad

Sección titulada «Controlador de producción: gestión de errores y observabilidad»

Todas las excepciones que lanza el motor central extienden NextPDF\Exception\NextPdfException. Capturar ese único tipo cubre los fallos del núcleo y de las extensiones. El bloque catch mostrado registra el contexto y devuelve una respuesta de error definida, nunca deja un catch vacío.

<?php
declare(strict_types=1);
namespace App\Controllers;
use CodeIgniter\HTTP\DownloadResponse;
use CodeIgniter\HTTP\ResponseInterface;
use NextPDF\CodeIgniter\Config\Services;
use NextPDF\Exception\NextPdfException;
use Psr\Log\LoggerInterface;
final class InvoiceController extends BaseController
{
public function download(int $id): DownloadResponse|ResponseInterface
{
/** @var LoggerInterface $logger */
$logger = \service('logger');
$start = \hrtime(true);
try {
$pdf = Services::pdf(false);
$pdf->document()->addPage();
$pdf->document()->cell(0, 10, "Invoice #{$id}");
$response = $pdf->download("invoice-{$id}.pdf");
$logger->info('pdf.invoice.generated', [
'invoice_id' => $id,
'elapsed_ms' => (\hrtime(true) - $start) / 1_000_000,
]);
return $response;
} catch (NextPdfException $e) {
$logger->error('pdf.invoice.failed', [
'invoice_id' => $id,
'exception' => $e::class,
'message' => $e->getMessage(),
]);
return $this->response
->setStatusCode(ResponseInterface::HTTP_INTERNAL_SERVER_ERROR)
->setJSON(['error' => 'pdf_generation_failed', 'invoice_id' => $id]);
}
}
}

Services::pdf(false) devuelve una instancia de biblioteca nueva y un documento subyacente nuevo en cada llamada. Por lo tanto, las solicitudes concurrentes nunca comparten el estado del documento. Las pruebas funcionales del paquete verifican este comportamiento.

Tiempos de vida seguros para servicios en procesos de trabajo

Sección titulada «Tiempos de vida seguros para servicios en procesos de trabajo»

Los registros de fuentes e imágenes son, por diseño, singletons que viven durante todo el proceso. El registro de fuentes se precalienta y se bloquea una sola vez. El registro de imágenes es una caché acotada que descarta las entradas usadas menos recientemente (LRU). En un proceso de trabajo de larga duración (el servidor spark de CodeIgniter, un runner de estilo RoadRunner o un trabajador de cola), este es el comportamiento previsto: los registros de alto costo persisten, mientras que cada documento es nuevo. No se debe solicitar un documento compartido (Services::pdfDocument(true)) en el código de una solicitud o de un trabajo; existe solo para reinicios en pruebas y compartiría contenido entre solicitudes.

Generación asíncrona con la cola de CodeIgniter

Sección titulada «Generación asíncrona con la cola de CodeIgniter»

GeneratePdfJob ejecuta la generación de PDF fuera de la solicitud mediante codeigniter4/queue. El entorno de ejecución de la cola impone dos condiciones, y ambas deben configurarse correctamente.

1. Registrar el manejador del trabajo por nombre

Sección titulada «1. Registrar el manejador del trabajo por nombre»

La cola resuelve un trabajo por una clave de nombre, no por una cadena de clase. El manejador de la cola valida el nombre del trabajo encolado contra las claves de Config\Queue::$jobHandlers. Rechaza los nombres desconocidos con CodeIgniter\Queue\Exceptions\QueueException. Registrar el trabajo en app/Config/Queue.php:

<?php
declare(strict_types=1);
namespace Config;
use CodeIgniter\Queue\Config\Queue as BaseQueue;
use NextPDF\CodeIgniter\Jobs\GeneratePdfJob;
final class Queue extends BaseQueue
{
/** @var array<string, class-string> */
public array $jobHandlers = [
'generate-pdf' => GeneratePdfJob::class,
];
}

Encolar el trabajo usando el nombre registrado como segundo argumento. El primer argumento es el nombre de la cola. El tercer argumento es el arreglo de datos del trabajo.

<?php
declare(strict_types=1);
namespace App\Controllers;
use CodeIgniter\HTTP\ResponseInterface;
final class InvoiceController extends BaseController
{
public function queueInvoice(int $id): ResponseInterface
{
\service('queue')->push('pdf-queue', 'generate-pdf', [
'builder' => 'App\\PdfBuilders\\InvoiceBuilder::build',
'outputPath' => WRITEPATH . 'pdfs/invoice-' . $id . '.pdf',
'context' => ['invoice_id' => $id],
]);
return $this->response
->setStatusCode(ResponseInterface::HTTP_ACCEPTED)
->setJSON(['status' => 'queued', 'invoice_id' => $id]);
}
}

3. Implementar el constructor en App\PdfBuilders

Sección titulada «3. Implementar el constructor en App\PdfBuilders»

El trabajo restringe los invocables del constructor al espacio de nombres App\PdfBuilders y confina las rutas de salida a WRITEPATH/pdfs/. El constructor es un método estático. Recibe un Document nuevo y el arreglo de contexto, y devuelve el documento.

<?php
declare(strict_types=1);
namespace App\PdfBuilders;
use NextPDF\Core\Document;
final class InvoiceBuilder
{
/** @param array<string, mixed> $context */
public static function build(Document $document, array $context): Document
{
$invoiceId = (int) ($context['invoice_id'] ?? 0);
$document->addPage();
$document->cell(0, 10, "Invoice #{$invoiceId}");
return $document;
}
}
Ventana de terminal
php spark queue:work pdf-queue

Cada ejecución del trabajo parte de un documento nuevo mediante Services::pdfDocument(). Aplica el constructor y luego guarda el resultado en la ruta validada. Las pruebas del paquete verifican que dos ejecuciones consecutivas del trabajo no comparten el estado del documento.

  • Al encolar, la cola rechaza GeneratePdfJob::class como nombre del trabajo porque no es la clave registrada 'generate-pdf'. Siempre se debe encolar la clave de jobHandlers.
  • La cadena del constructor debe coincidir exactamente con App\PdfBuilders\<Class>::<method>. Las funciones, otros espacios de nombres o las cargas útiles con prefijo o sufijo generan InvalidArgumentException antes de que se ejecute cualquier código.
  • La ruta de salida debe resolverse dentro de WRITEPATH/pdfs/ y terminar en .pdf (sin distinguir mayúsculas de minúsculas). Se rechazan las rutas con recorrido de directorios y con prefijo de directorio hermano.
  • codeigniter4/queue es una dependencia del paquete solo para desarrollo. Debe requerirse en la aplicación que ejecuta los procesos de trabajo.

Los registros se crean una vez en cada proceso de trabajo. El costo de construir el documento escala con el contenido, no con el adaptador. Para trabajos por lotes grandes, es preferible usar la vía de la cola para que los procesos de trabajo de las solicitudes sigan respondiendo. Debe definirse un performance_budget en cualquier recipe que tenga un objetivo medible.

El trabajo en cola es la superficie de mayor riesgo. Las cargas útiles de la cola pueden estar influidas por un atacante cuando el broker es accesible. La lista de permitidos de invocables y el confinamiento de rutas se tratan en /integrations/codeigniter/security-and-operations/ con los casos de rechazo verificados.

  • Los controladores reciben servicios concretos, no un contenedor, en consonancia con la recomendación sobre el localizador de servicios de PSR-11 §1.3.

El núcleo de NextPDF es Apache-2.0. La salida firmada y PDF/A en los trabajos en cola requiere NextPDF Pro o Enterprise instalado en el entorno del proceso de trabajo. El paquete de CodeIgniter expone los métodos de servicio correspondientes. Devuelven null hasta que se instale el paquete Premium correspondiente. Consulta </get-license/?intent=codeigniter-async-signing>.

  • /integrations/codeigniter/quickstart/ — la versión mínima de estos controladores.
  • /integrations/codeigniter/configuration/ — firma, TSA y configuración de rutas.
  • /integrations/codeigniter/security-and-operations/ — modelo de amenazas de la cola y endurecimiento.
  • /integrations/codeigniter/troubleshooting/ — modos de fallo de la cola y del descubrimiento.
  • /integrations/codeigniter/integration/ — referencia de conexión y prueba de humo.