Uso in produzione con CodeIgniter 4
In breve
Sezione intitolata “In breve”I controller di produzione dipendono da servizi NextPDF concreti. Gestiscono esplicitamente la gerarchia di eccezioni documentata ed emettono segnali di osservabilità. La generazione PDF di lunga durata viene spostata fuori dalla richiesta tramite la coda di CodeIgniter 4.
Panoramica concettuale
Sezione intitolata “Panoramica concettuale”CodeIgniter 4 risolve i servizi del pacchetto tramite il proprio locator. Il pattern service-locator passa un container a un oggetto affinché l’oggetto recuperi le proprie dipendenze. Questo pattern è sconsigliato (PSR-11 §1.3, verbo modale SHOULD NOT). Per seguire questa indicazione, risolvere ciascun servizio NextPDF una sola volta al confine del controller e passare l’oggetto concreto al codice interno. Non passare la classe Services — né un container — al codice di dominio.
Ogni esempio PHP dichiara declare(strict_types=1); su una riga dedicata (PSR-12 §x1.x3.p34).
Superficie dell’API
Sezione intitolata “Superficie dell’API”| Aspetto di produzione | Superficie verificata |
|---|---|
| Risolvere i servizi | Services::pdf(false), Services::pdfDocument(false), Services::documentFactory() |
| Costruire la risposta | PdfResponse::download() / inline() → DownloadResponse |
| Intercettare gli errori | NextPDF\Exception\NextPdfException (tipo base dell’ecosistema) |
| Generazione asincrona | GeneratePdfJob registrato in Config\Queue::$jobHandlers |
| Controlli su path / callable | GeneratePdfJob lancia InvalidArgumentException |
Controller di produzione — gestione degli errori e osservabilità
Sezione intitolata “Controller di produzione — gestione degli errori e osservabilità”Le eccezioni lanciate dal motore core estendono tutte NextPDF\Exception\NextPdfException. Intercettando quel singolo tipo si coprono gli errori del core e delle estensioni. Qui il blocco catch registra l’errore con contesto e restituisce una risposta di errore definita, mai un catch vuoto.
<?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) restituisce una nuova libreria e un nuovo documento sottostante a ogni chiamata. Le richieste concorrenti quindi non condividono mai lo stato del documento. I test funzionali del pacchetto verificano questo comportamento.
Cicli di vita dei servizi sicuri per i worker
Sezione intitolata “Cicli di vita dei servizi sicuri per i worker”I registri dei font e delle immagini sono, per progettazione, singleton con ciclo di vita pari a quello del processo. Il registro dei font viene preriscaldato e bloccato una sola volta. Il registro delle immagini è una cache limitata least-recently-used (LRU). In un worker di lunga durata (server spark di CodeIgniter, runner in stile RoadRunner o worker di coda) questo è il comportamento previsto: i registri costosi persistono, mentre ogni documento è nuovo. Non richiedere un documento condiviso (Services::pdfDocument(true)) nel codice della richiesta o del job; esiste solo per il reset dei test e condividerebbe il contenuto tra le richieste.
Generazione asincrona con CodeIgniter Queue
Sezione intitolata “Generazione asincrona con CodeIgniter Queue”GeneratePdfJob esegue la generazione del PDF fuori dalla richiesta tramite codeigniter4/queue. Il runtime della coda impone due requisiti, che devono essere entrambi configurati correttamente.
1. Registrare il job handler per nome
Sezione intitolata “1. Registrare il job handler per nome”La coda risolve un job tramite una chiave nominale, non tramite una stringa di classe. L’handler della coda valida il nome del job inviato confrontandolo con le chiavi di Config\Queue::$jobHandlers. Rifiuta un nome sconosciuto con CodeIgniter\Queue\Exceptions\QueueException. Registrare il job in 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, ];}2. Inviare tramite il nome registrato
Sezione intitolata “2. Inviare tramite il nome registrato”Inviare il job usando il nome registrato come secondo argomento. Il primo argomento è il nome della coda. Il terzo argomento è l’array dei dati del job.
<?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. Implementare il builder in App\PdfBuilders
Sezione intitolata “3. Implementare il builder in App\PdfBuilders”Il job limita i callable del builder al namespace App\PdfBuilders e confina i path di output in WRITEPATH/pdfs/. Il builder è un metodo statico. Riceve un nuovo Document e l’array di contesto, poi restituisce il 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; }}Eseguire il worker
Sezione intitolata “Eseguire il worker”php spark queue:work pdf-queueOgni esecuzione del job parte da un nuovo documento tramite Services::pdfDocument(). Applica il builder, quindi lo salva nel path convalidato. I test del pacchetto verificano che due esecuzioni consecutive del job non condividano lo stato del documento.
Casi limite e insidie
Sezione intitolata “Casi limite e insidie”- La coda rifiuta
GeneratePdfJob::classcome nome del job al momento dell’invio, perché non è la chiave registrata'generate-pdf'. Inviare sempre la chiave registrata injobHandlers. - La stringa del builder deve corrispondere esattamente a
App\PdfBuilders\<Class>::<method>. Funzioni, altri namespace o payload con prefisso o suffisso sollevanoInvalidArgumentExceptionprima che venga eseguito qualsiasi codice. - Il path di output deve risolversi all’interno di
WRITEPATH/pdfs/e terminare con.pdf(senza distinzione tra maiuscole e minuscole). I path di traversal e quelli con prefisso di pari livello vengono rifiutati. codeigniter4/queueè una dipendenza del pacchetto solo in fase di sviluppo. Richiederla nell’applicazione che esegue i worker.
Prestazioni
Sezione intitolata “Prestazioni”I registri vengono creati una sola volta per ogni processo worker. Il costo di costruzione del documento dipende dal contenuto, non dall’adapter. Per i job batch di grandi dimensioni, preferire il percorso della coda affinché i worker delle richieste restino reattivi. Impostare un performance_budget in qualsiasi ricetta che abbia un obiettivo misurabile.
Note sulla sicurezza
Sezione intitolata “Note sulla sicurezza”Il job di coda è la superficie a rischio più elevato. I payload della coda possono essere manipolati da un attaccante quando il broker è raggiungibile. L’allowlist dei callable e il confinamento dei path sono trattati in /integrations/codeigniter/security-and-operations/ con i casi di rifiuto verificati.
Conformità
Sezione intitolata “Conformità”- I controller ricevono servizi concreti, non un container, coerentemente con l’indicazione sul service-locator di PSR-11 §1.3.
Contesto commerciale
Sezione intitolata “Contesto commerciale”Il core di NextPDF è Apache-2.0. L’output firmato e PDF/A nei job di coda richiede NextPDF Pro o Enterprise installato nell’ambiente del worker. Il pacchetto CodeIgniter espone i metodi di servizio corrispondenti. Questi metodi restituiscono null finché non è installato il pacchetto Premium corrispondente. Vedere </get-license/?intent=codeigniter-async-signing>.
Vedere anche
Sezione intitolata “Vedere anche”- /integrations/codeigniter/quickstart/ — la versione essenziale di questi controller.
- /integrations/codeigniter/configuration/ — firma, TSA e configurazione dei path.
- /integrations/codeigniter/security-and-operations/ — modello di minaccia della coda e hardening.
- /integrations/codeigniter/troubleshooting/ — modalità di errore della coda e della discovery.
- /integrations/codeigniter/integration/ — riferimento per il wiring e gli smoke test.