Ga naar inhoud

Gebruik in productie met CodeIgniter 4

Productiecontrollers ontvangen concrete NextPDF-services. Ze verwerken de gedocumenteerde exception-hiërarchie expliciet en sturen observability-signalen uit. Verplaats langlopende Portable Document Format-verwerking (PDF) uit het verzoek via de CodeIgniter 4 Queue.

CodeIgniter 4 resolvet de services van het pakket via de eigen locator. In een service-locatorpatroon ontvangt een object een container en gebruikt het die om zijn eigen dependencies op te halen. De richtlijn van PHP Standard Recommendation (PSR) ontraadt dat patroon (PSR-11 §1.3, modaal SHOULD NOT). Om die richtlijn te volgen, los je elke NextPDF-service eenmalig op aan de controllergrens en geef je het concrete object daarna naar binnen door. Geef de Services-klasse of een container niet door aan je domeincode.

Elk PHP-voorbeeld plaatst declare(strict_types=1); op een eigen regel (PSR-12 §x1.x3.p34).

ProductieaspectGeverifieerd oppervlak
Services resolvenServices::pdf(false), Services::pdfDocument(false), Services::documentFactory()
Response opbouwenPdfResponse::download() / inline()DownloadResponse
Fouten opvangenNextPDF\Exception\NextPdfException (basistype van het ecosysteem)
Asynchrone generatieGeneratePdfJob geregistreerd in Config\Queue::$jobHandlers
Path- / callable-guardsGeneratePdfJob gooit InvalidArgumentException

Productiecontroller — foutafhandeling en observability

Sectie met titel “Productiecontroller — foutafhandeling en observability”

De kern-engine gooit exceptions die allemaal NextPDF\Exception\NextPdfException uitbreiden. Vang dit ene type op om fouten van zowel de kern als extensies af te dekken. Dit catch-blok logt context en geeft een gedefinieerde foutrespons terug, zonder ooit een lege catch achter te laten.

<?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) geeft bij elke aanroep een nieuwe library en een nieuw onderliggend document terug. Gelijktijdige verzoeken delen nooit documentstatus. De functionele tests van het pakket bevestigen dit gedrag.

De lettertype- en afbeeldingsregisters zijn doelbewust singletons met een procesgebonden levensduur. Het lettertyperegister wordt eenmalig opgewarmd en vergrendeld. Het afbeeldingsregister is een begrensde least recently used-cache (LRU). In een langlevende worker (CodeIgniter spark-server, een runner in RoadRunner-stijl of een queue-worker) is dat bewust zo: dure registers blijven behouden, terwijl elk document nieuw is. Vraag in verzoek- of taakcode geen gedeeld document aan (Services::pdfDocument(true)); het bestaat alleen voor het resetten van tests en zou inhoud delen tussen verzoeken.

GeneratePdfJob voert PDF-generatie buiten het verzoek uit via codeigniter4/queue. De queue-runtime vereist twee instellingen. Configureer beide correct.

De queue resolvet een taak via een naamsleutel, niet via een klassestring. De queue-handler valideert de gepushte taaknaam aan de hand van de sleutels van Config\Queue::$jobHandlers. Die wijst een onbekende naam af met CodeIgniter\Queue\Exceptions\QueueException. Registreer de taak 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,
];
}

Push de taak met de geregistreerde naam als tweede argument. Het eerste argument is de queue-naam. Het derde argument is de array met taakgegevens.

<?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. Implementeer de builder onder App\PdfBuilders

Sectie met titel “3. Implementeer de builder onder App\PdfBuilders”

De taak staat builder-callables alleen toe in de namespace App\PdfBuilders en beperkt uitvoerpaden tot WRITEPATH/pdfs/. Implementeer de builder als een statische methode. De methode ontvangt een nieuw Document en de context-array en geeft vervolgens het document terug.

<?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;
}
}
Terminal window
php spark queue:work pdf-queue

Elke taakuitvoering begint met een nieuw document van Services::pdfDocument(). Daarna wordt de builder toegepast en wordt het document opgeslagen op het gevalideerde pad. De tests van het pakket verifiëren dat twee opeenvolgende taakuitvoeringen geen documentstatus delen.

  • De queue wijst GeneratePdfJob::class als taaknaam af bij het pushen, omdat dit niet de geregistreerde sleutel 'generate-pdf' is. Push altijd de jobHandlers-sleutel.
  • De builder-string moet exact overeenkomen met App\PdfBuilders\<Class>::<method>. Functies, andere namespaces of payloads met een prefix of suffix veroorzaken InvalidArgumentException voordat code wordt uitgevoerd.
  • Het uitvoerpad moet binnen WRITEPATH/pdfs/ resolven en eindigen op .pdf (niet hoofdlettergevoelig). Paden met traversal of sibling-prefix worden afgewezen.
  • codeigniter4/queue is een development-only dependency van het pakket. Vereis het in de applicatie die workers uitvoert.

De registers worden eenmaal per workerproces aangemaakt. De kosten voor het opbouwen van een document schalen met de inhoud mee, niet met de adapter. Gebruik voor grote batchtaken het queue-pad, zodat request-workers responsief blijven. Stel een performance_budget in voor elk recipe met een meetbaar doel.

De queue-taak is het oppervlak met het hoogste risico. Wanneer de broker bereikbaar is, kunnen aanvallers queue-payloads beïnvloeden. De callable-allowlist en padbeperking worden behandeld in /integrations/codeigniter/security-and-operations/ met de geverifieerde afwijzingsgevallen.

  • Controllers ontvangen concrete services, geen container, in overeenstemming met de service-locatorrichtlijn van PSR-11 §1.3.

NextPDF core valt onder Apache-2.0. Om ondertekende en PDF/A-uitvoer in queue-taken te produceren, installeer je NextPDF Pro of Enterprise in de worker-omgeving. Het CodeIgniter-pakket stelt de bijbehorende servicemethoden beschikbaar. Ze geven null terug totdat het bijbehorende Premium-pakket is geïnstalleerd. Zie </get-license/?intent=codeigniter-async-signing>.

  • /integrations/codeigniter/quickstart/ — de minimale versie van deze controllers.
  • /integrations/codeigniter/configuration/ — ondertekening, Time Stamping Authority (TSA) en padconfiguratie.
  • /integrations/codeigniter/security-and-operations/ — dreigingsmodel en hardening van de queue.
  • /integrations/codeigniter/troubleshooting/ — faalmodi van de queue en discovery.
  • /integrations/codeigniter/integration/ — wiring-referentie en smoke test.