Использование в продакшене с CodeIgniter 4
В продакшене контроллеры получают конкретные сервисы NextPDF, явно обрабатывают документированную иерархию исключений и отправляют сигналы наблюдаемости. Долгие операции с Portable Document Format (PDF) выносите из запроса в очередь CodeIgniter 4 Queue.
Концептуальный обзор
Заголовок раздела «Концептуальный обзор»CodeIgniter 4 получает сервисы пакета через собственный локатор. В паттерне service locator объект получает контейнер и сам извлекает из него свои зависимости. PHP Standard Recommendation (PSR) не рекомендует этот паттерн (PSR-11 §1.3, модальность SHOULD NOT). Чтобы следовать этой рекомендации, получайте каждый сервис NextPDF один раз на границе контроллера, а дальше передавайте внутрь конкретный объект. Не передавайте класс Services или контейнер в код вашей предметной области.
В каждом примере на PHP declare(strict_types=1); вынесено на отдельную строку (PSR-12 §x1.x3.p34).
Поверхность API
Заголовок раздела «Поверхность API»| Задача продакшена | Проверенная поверхность |
|---|---|
| Разрешение сервисов | Services::pdf(false), Services::pdfDocument(false), Services::documentFactory() |
| Построение ответа | PdfResponse::download() / inline() → DownloadResponse |
| Перехват сбоев | NextPDF\Exception\NextPdfException (базовый тип экосистемы) |
| Асинхронная генерация | GeneratePdfJob, зарегистрированный в Config\Queue::$jobHandlers |
| Проверки пути и вызываемого объекта | GeneratePdfJob выбрасывает InvalidArgumentException |
Контроллер для продакшена — обработка ошибок и наблюдаемость
Заголовок раздела «Контроллер для продакшена — обработка ошибок и наблюдаемость»Основной движок выбрасывает исключения, которые все расширяют NextPDF\Exception\NextPdfException. Перехватывайте этот единственный тип, чтобы охватить сбои ядра и расширений. Блок catch ниже записывает контекст в журнал и возвращает явный ответ об ошибке; пустого catch здесь нет.
<?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) при каждом вызове возвращает новый экземпляр библиотеки и новый базовый документ. Параллельные запросы никогда не используют общее состояние документа. Функциональные тесты пакета проверяют это поведение.
Безопасные для воркеров жизненные циклы сервисов
Заголовок раздела «Безопасные для воркеров жизненные циклы сервисов»По своей конструкции реестры шрифтов и изображений являются синглтонами на время жизни процесса. Реестр шрифтов прогревается и блокируется один раз. Реестр изображений — это ограниченный кеш с вытеснением давно неиспользуемых элементов (LRU). В долгоживущем воркере (сервер CodeIgniter spark, runner в стиле RoadRunner или воркер очереди) это сделано намеренно: затратные реестры сохраняются, тогда как каждый документ создаётся заново. Не запрашивайте общий документ (Services::pdfDocument(true)) в коде запроса или задачи: он существует только для сброса в тестах и иначе приводил бы к совместному использованию содержимого между запросами.
Асинхронная генерация с CodeIgniter Queue
Заголовок раздела «Асинхронная генерация с CodeIgniter Queue»GeneratePdfJob выполняет генерацию PDF вне запроса через codeigniter4/queue. Для среды выполнения очереди нужны две настройки. Настройте обе правильно.
1. Зарегистрируйте обработчик задачи по имени
Заголовок раздела «1. Зарегистрируйте обработчик задачи по имени»Очередь разрешает задачу по ключу-имени, а не по строке класса. Обработчик очереди сверяет имя поставленной в очередь задачи с ключами Config\Queue::$jobHandlers. Неизвестное имя отклоняется с CodeIgniter\Queue\Exceptions\QueueException. Зарегистрируйте задачу в 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. Отправляйте задачу по зарегистрированному имени
Заголовок раздела «2. Отправляйте задачу по зарегистрированному имени»При постановке задачи в очередь указывайте зарегистрированное имя вторым аргументом. Первый аргумент — имя очереди. Третий — массив данных задачи.
<?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. Реализуйте билдер в App\PdfBuilders
Заголовок раздела «3. Реализуйте билдер в App\PdfBuilders»Задача допускает вызываемые билдеры только из пространства имён App\PdfBuilders и ограничивает пути вывода каталогом WRITEPATH/pdfs/. Реализуйте билдер как статический метод. Он получает новый Document и массив контекста, а затем возвращает документ.
<?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; }}Запуск воркера
Заголовок раздела «Запуск воркера»php spark queue:work pdf-queueПри каждом запуске задача начинает с нового документа из Services::pdfDocument(). Затем она применяет билдер и сохраняет файл по проверенному пути. Тесты пакета проверяют, что два последовательных запуска задачи не используют общее состояние документа.
Граничные случаи и подводные камни
Заголовок раздела «Граничные случаи и подводные камни»- Очередь отклоняет
GeneratePdfJob::classв качестве имени задачи при постановке в очередь, потому что это не зарегистрированный ключ'generate-pdf'. Всегда помещайте в очередь ключ изjobHandlers. - Строка билдера должна точно соответствовать
App\PdfBuilders\<Class>::<method>. Функции, другие пространства имён, а также полезные данные с префиксом или суффиксом вызываютInvalidArgumentExceptionещё до выполнения какого-либо кода. - Путь вывода должен разрешаться внутри
WRITEPATH/pdfs/и заканчиваться на.pdf(без учёта регистра). Пути с обходом каталогов и с префиксом соседнего каталога отклоняются. codeigniter4/queue— это зависимость пакета только для разработки. Подключайте её в приложении, которое запускает воркеры.
Производительность
Заголовок раздела «Производительность»Реестры создаются один раз для каждого процесса воркера. Затраты на построение документа растут вместе с содержимым, а не с адаптером. Для крупных пакетных задач используйте путь через очередь, чтобы воркеры запросов оставались отзывчивыми. Задавайте performance_budget в любом рецепте с измеримой целью.
Замечания по безопасности
Заголовок раздела «Замечания по безопасности»Задача очереди — поверхность с наивысшим риском. Когда брокер доступен, злоумышленник может влиять на полезные данные очереди. Список разрешённых вызываемых объектов и ограничение путей описаны в /integrations/codeigniter/security-and-operations/ вместе с проверенными случаями отклонения.
Соответствие
Заголовок раздела «Соответствие»- Контроллеры получают конкретные сервисы, а не контейнер, что согласуется с рекомендацией PSR-11 §1.3 по паттерну service locator.
Коммерческий контекст
Заголовок раздела «Коммерческий контекст»Ядро NextPDF распространяется под Apache-2.0. Чтобы создавать подписанный вывод и вывод PDF/A в задачах очереди, установите NextPDF Pro или Enterprise в окружении воркера. Пакет CodeIgniter предоставляет соответствующие методы сервиса. Они возвращают null, пока не установлен соответствующий пакет Premium. См. </get-license/?intent=codeigniter-async-signing>.
Смотрите также
Заголовок раздела «Смотрите также»- /integrations/codeigniter/quickstart/ — минимальный вариант этих контроллеров.
- /integrations/codeigniter/configuration/ — подписание, служба меток времени (TSA) и настройка путей.
- /integrations/codeigniter/security-and-operations/ — модель угроз очереди и усиление защиты.
- /integrations/codeigniter/troubleshooting/ — режимы сбоев очереди и обнаружения.
- /integrations/codeigniter/integration/ — справочник по подключению и дымовой тест.