Lewati ke konten

Penggunaan produksi pada CodeIgniter 4

Controller produksi menerima layanan NextPDF konkret. Controller tersebut menangani hierarki eksepsi yang terdokumentasi secara eksplisit dan memancarkan sinyal observabilitas. Pindahkan pekerjaan Portable Document Format (PDF) yang berjalan lama ke luar alur permintaan melalui CodeIgniter 4 Queue.

CodeIgniter 4 menyelesaikan (resolve) layanan paket ini melalui locator-nya. Dalam pola service-locator, sebuah objek menerima container dan menggunakannya untuk mengambil dependensinya sendiri. Panduan PHP Standard Recommendation (PSR) tidak menganjurkan pola tersebut (PSR-11 §1.3, modal SHOULD NOT). Untuk mengikuti panduan itu, selesaikan setiap layanan NextPDF satu kali pada batas controller, lalu teruskan objek konkretnya ke lapisan di dalamnya. Jangan teruskan kelas Services atau container ke kode domain Anda.

Setiap contoh PHP menempatkan declare(strict_types=1); pada barisnya sendiri (PSR-12 §x1.x3.p34).

Aspek produksiPermukaan terverifikasi
Menyelesaikan layananServices::pdf(false), Services::pdfDocument(false), Services::documentFactory()
Membangun responsPdfResponse::download() / inline()DownloadResponse
Menangkap kegagalanNextPDF\Exception\NextPdfException (tipe dasar ekosistem)
Pembuatan asinkronGeneratePdfJob terdaftar di Config\Queue::$jobHandlers
Penjaga path / callableGeneratePdfJob melempar InvalidArgumentException

Controller produksi — penanganan error dan observabilitas

Bagian berjudul “Controller produksi — penanganan error dan observabilitas”

Semua eksepsi yang dilempar engine inti merupakan turunan dari NextPDF\Exception\NextPdfException. Tangkap satu tipe ini saja untuk mencakup kegagalan inti maupun ekstensi. Blok catch ini mencatat konteks dan mengembalikan respons error yang terdefinisi, bukan catch kosong.

<?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) mengembalikan library baru beserta dokumen dasar baru setiap kali dipanggil. Permintaan yang berjalan bersamaan tidak pernah berbagi state dokumen. Pengujian fungsional paket ini menegaskan perilaku tersebut.

Registry font dan gambar dirancang sebagai singleton yang hidup selama proses berjalan. Registry font melakukan pemanasan dan penguncian satu kali. Registry gambar adalah cache least recently used (LRU) yang dibatasi. Pada worker yang berjalan lama (server spark CodeIgniter, runner bergaya RoadRunner, atau worker antrean), hal ini disengaja: registry yang biaya pembuatannya mahal tetap bertahan, sementara setiap dokumen selalu baru. Jangan meminta dokumen bersama (Services::pdfDocument(true)) di dalam kode permintaan atau job; dokumen tersebut hanya ada untuk reset pengujian dan akan berbagi konten lintas permintaan.

GeneratePdfJob menjalankan pembuatan PDF di luar alur permintaan melalui codeigniter4/queue. Runtime antrean memerlukan dua pengaturan. Konfigurasikan keduanya dengan benar.

Antrean menyelesaikan sebuah job berdasarkan kunci nama, bukan berdasarkan string kelas. Handler antrean memvalidasi nama job yang dikirimkan terhadap kunci-kunci pada Config\Queue::$jobHandlers. Handler menolak nama yang tidak dikenal dengan CodeIgniter\Queue\Exceptions\QueueException. Daftarkan job tersebut di 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,
];
}

Kirim job dengan nama yang terdaftar sebagai argumen kedua. Argumen pertama adalah nama antrean. Argumen ketiga adalah array data 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. Implementasikan builder di bawah App\PdfBuilders

Bagian berjudul “3. Implementasikan builder di bawah App\PdfBuilders”

Job hanya mengizinkan callable builder di dalam namespace App\PdfBuilders dan membatasi path keluaran ke WRITEPATH/pdfs/. Implementasikan builder sebagai method statis. Builder menerima Document baru dan array konteks, lalu mengembalikan dokumen tersebut.

<?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

Setiap eksekusi job dimulai dengan dokumen baru dari Services::pdfDocument(). Job menerapkan builder, lalu menyimpannya ke path yang telah divalidasi. Pengujian paket ini memverifikasi bahwa dua eksekusi job berurutan tidak berbagi state dokumen.

  • Antrean menolak GeneratePdfJob::class sebagai nama job saat dikirimkan karena itu bukan kunci yang terdaftar 'generate-pdf'. Selalu gunakan kunci jobHandlers saat mengirim job.
  • String builder harus sama persis dengan App\PdfBuilders\<Class>::<method>. Fungsi, namespace lain, atau payload dengan awalan maupun akhiran tambahan akan memunculkan InvalidArgumentException sebelum kode apa pun berjalan.
  • Path keluaran harus mengarah ke dalam WRITEPATH/pdfs/ dan berakhiran .pdf (tidak peka huruf besar/kecil). Path traversal dan path dengan awalan sibling akan ditolak.
  • codeigniter4/queue adalah dependensi paket yang hanya untuk pengembangan. Jadikan dependensi itu wajib pada aplikasi yang menjalankan worker.

Registry dibuat satu kali per proses worker. Biaya pembuatan dokumen berskala sesuai konten, bukan adapter. Untuk job batch berukuran besar, gunakan jalur antrean agar worker yang melayani permintaan tetap responsif. Tetapkan performance_budget pada setiap resep yang memiliki target terukur.

Job antrean adalah permukaan dengan risiko tertinggi. Ketika broker dapat dijangkau, payload antrean dapat dipengaruhi oleh penyerang. Allowlist callable dan pembatasan path dibahas di /integrations/codeigniter/security-and-operations/ bersama kasus penolakan yang terverifikasi.

  • Controller menerima layanan konkret, bukan container, sesuai dengan panduan service-locator PSR-11 §1.3.

Inti NextPDF berlisensi Apache-2.0. Untuk menghasilkan keluaran bertanda tangan dan PDF/A di dalam job antrean, pasang NextPDF Pro atau Enterprise di lingkungan worker. Paket CodeIgniter mengekspos method layanan yang sesuai. Method tersebut mengembalikan null hingga paket Premium yang sesuai dipasang. Lihat </get-license/?intent=codeigniter-async-signing>.

  • /integrations/codeigniter/quickstart/ — versi minimal dari controller di halaman ini.
  • /integrations/codeigniter/configuration/ — penandatanganan, Time Stamping Authority (TSA), dan konfigurasi path.
  • /integrations/codeigniter/security-and-operations/ — model ancaman antrean dan pengerasan keamanan.
  • /integrations/codeigniter/troubleshooting/ — mode kegagalan antrean dan discovery.
  • /integrations/codeigniter/integration/ — referensi penyambungan (wiring) dan smoke test.