Bỏ qua để đến nội dung

Sử dụng trong môi trường production với CodeIgniter 4

Trong môi trường production, controller nhận trực tiếp các service NextPDF cụ thể. Controller xử lý rõ ràng cây phân cấp exception đã được tài liệu hóa và phát tín hiệu quan sát. Hãy chuyển các tác vụ Portable Document Format (PDF) chạy lâu ra khỏi luồng request thông qua CodeIgniter 4 Queue.

CodeIgniter 4 phân giải các service của package thông qua locator của framework. Trong mẫu service locator, một đối tượng nhận một container và dùng container đó để lấy các dependency của chính nó. Hướng dẫn PHP Standard Recommendation (PSR) khuyến cáo không nên dùng mẫu này (PSR-11 §1.3, từ tình thái SHOULD NOT). Để tuân theo hướng dẫn đó, hãy phân giải mỗi service NextPDF một lần tại ranh giới controller, rồi truyền đối tượng cụ thể vào bên trong. Đừng truyền lớp Services hoặc một container vào mã domain của bạn.

Mỗi mẫu PHP đặt declare(strict_types=1); trên dòng riêng của nó (PSR-12 §x1.x3.p34).

Vấn đề trong productionBề mặt đã kiểm chứng
Phân giải serviceServices::pdf(false), Services::pdfDocument(false), Services::documentFactory()
Tạo responsePdfResponse::download() / inline()DownloadResponse
Bắt lỗi khi thất bạiNextPDF\Exception\NextPdfException (kiểu cơ sở của hệ sinh thái)
Tạo tài liệu bất đồng bộGeneratePdfJob được đăng ký trong Config\Queue::$jobHandlers
Cơ chế bảo vệ đường dẫn / callableGeneratePdfJob ném ra InvalidArgumentException

Controller production — xử lý lỗi và khả năng quan sát

Phần tiêu đề “Controller production — xử lý lỗi và khả năng quan sát”

Các exception do core engine ném ra đều kế thừa từ NextPDF\Exception\NextPdfException. Hãy bắt đúng kiểu duy nhất này để bao quát lỗi của core và extension. Khối catch này ghi log ngữ cảnh và trả về một error response đã được định nghĩa, tuyệt đối không để catch rỗng.

<?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) trả về một library mới và một tài liệu nền mới cho mỗi lần gọi. Các request đồng thời không bao giờ dùng chung trạng thái tài liệu. Các functional test của package xác nhận hành vi này.

Theo thiết kế, registry phông chữ và hình ảnh là singleton tồn tại trong suốt vòng đời tiến trình. Registry phông chữ được làm nóng và khóa một lần. Registry hình ảnh là một cache least recently used (LRU) có giới hạn. Trong một worker chạy lâu (CodeIgniter spark server, runner kiểu RoadRunner, hoặc một queue worker), đây là chủ ý: các registry tốn kém vẫn được giữ lại, còn mọi tài liệu đều là mới. Đừng yêu cầu một tài liệu dùng chung (Services::pdfDocument(true)) trong mã của request hay job; nó chỉ tồn tại để reset trong test và sẽ dùng chung nội dung giữa các request.

Tạo tài liệu bất đồng bộ với CodeIgniter Queue

Phần tiêu đề “Tạo tài liệu bất đồng bộ với CodeIgniter Queue”

GeneratePdfJob đưa việc tạo PDF ra khỏi luồng request thông qua codeigniter4/queue. Runtime của hàng đợi cần hai thiết lập. Hãy cấu hình đúng cả hai.

Hàng đợi phân giải một job theo khóa tên, chứ không theo chuỗi tên lớp. Queue handler đối chiếu tên job được đẩy vào với các khóa trong Config\Queue::$jobHandlers. Nó từ chối tên không xác định bằng CodeIgniter\Queue\Exceptions\QueueException. Hãy đăng ký job trong 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,
];
}

Hãy đẩy job với tên đã đăng ký làm đối số thứ hai. Đối số đầu tiên là tên hàng đợi. Đối số thứ ba là mảng dữ liệu cho 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. Triển khai builder bên trong App\PdfBuilders

Phần tiêu đề “3. Triển khai builder bên trong App\PdfBuilders”

Job chỉ cho phép các callable builder trong namespace App\PdfBuilders và giới hạn đường dẫn đầu ra trong WRITEPATH/pdfs/. Hãy triển khai builder dưới dạng một phương thức static. Phương thức này nhận một Document mới cùng mảng context, rồi trả về tài liệu.

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

Mỗi lần chạy job bắt đầu với một tài liệu mới từ Services::pdfDocument(). Job áp dụng builder, rồi lưu vào đường dẫn đã được xác thực. Các test của package xác minh rằng hai lần chạy job liên tiếp không dùng chung trạng thái tài liệu.

  • Hàng đợi từ chối GeneratePdfJob::class khi dùng làm tên job lúc đẩy vào, vì đó không phải là khóa đã đăng ký 'generate-pdf'. Hãy luôn đẩy khóa jobHandlers.
  • Chuỗi builder phải khớp chính xác với App\PdfBuilders\<Class>::<method>. Các hàm, namespace khác, hoặc payload có thêm tiền tố hay hậu tố sẽ làm phát sinh InvalidArgumentException trước khi bất kỳ mã nào được chạy.
  • Đường dẫn đầu ra phải phân giải vào bên trong WRITEPATH/pdfs/ và kết thúc bằng .pdf (không phân biệt chữ hoa chữ thường). Các đường dẫn dạng traversal và sibling-prefix đều bị từ chối.
  • codeigniter4/queue là dependency chỉ dành cho phát triển của package. Hãy require dependency này trong ứng dụng chạy các worker.

Các registry được tạo một lần cho mỗi tiến trình worker. Chi phí dựng tài liệu tỷ lệ theo nội dung, chứ không theo adapter. Với các batch job lớn, hãy dùng đường đi qua hàng đợi để những worker xử lý request vẫn phản hồi tốt. Hãy đặt một performance_budget trong bất kỳ recipe nào có mục tiêu đo lường được.

Queue job là bề mặt có rủi ro cao nhất. Khi broker có thể truy cập được, payload của hàng đợi có thể chịu ảnh hưởng từ kẻ tấn công. Danh sách cho phép callable và việc giới hạn đường dẫn được trình bày trong /integrations/codeigniter/security-and-operations/, cùng các trường hợp từ chối đã được kiểm chứng.

  • Controller nhận các service cụ thể, chứ không phải một container, nhất quán với hướng dẫn về service locator của PSR-11 §1.3.

NextPDF core dùng giấy phép Apache-2.0. Để tạo đầu ra đã ký và PDF/A trong các queue job, hãy cài đặt NextPDF Pro hoặc Enterprise trong môi trường worker. Package CodeIgniter cung cấp các phương thức service tương ứng. Các phương thức này trả về null cho đến khi package Premium tương ứng được cài đặt. Xem </get-license/?intent=codeigniter-async-signing>.

  • /integrations/codeigniter/quickstart/ — phiên bản tối giản của các controller này.
  • /integrations/codeigniter/configuration/ — cấu hình ký, Time Stamping Authority (TSA) và đường dẫn.
  • /integrations/codeigniter/security-and-operations/ — mô hình mối đe dọa của hàng đợi và cách tăng cường bảo mật.
  • /integrations/codeigniter/troubleshooting/ — các chế độ lỗi của hàng đợi và discovery.
  • /integrations/codeigniter/integration/ — tài liệu tham khảo về wiring và smoke test.