Uso em produção com CodeIgniter 4
Visão geral
Seção intitulada “Visão geral”Os controllers de produção recebem serviços concretos do NextPDF. Eles tratam explicitamente a hierarquia documentada de exceções e emitem sinais de observabilidade. Mova o processamento demorado de Portable Document Format (PDF) para fora do ciclo da requisição usando a Queue do CodeIgniter 4.
Visão conceitual
Seção intitulada “Visão conceitual”O CodeIgniter 4 resolve os serviços do pacote por meio do próprio locator. No padrão service-locator, um objeto recebe um container e o usa para obter suas dependências. A orientação da PHP Standard Recommendation (PSR) desaconselha esse padrão (PSR-11 §1.3, modal SHOULD NOT). Para seguir essa orientação, resolva cada serviço do NextPDF uma única vez na fronteira do controller e, em seguida, passe o objeto concreto para as camadas internas. Não passe a classe Services nem um container para o código de domínio.
Cada exemplo de PHP coloca declare(strict_types=1); em uma linha própria (PSR-12 §x1.x3.p34).
Superfície da API
Seção intitulada “Superfície da API”| Aspecto de produção | Superfície verificada |
|---|---|
| Resolver serviços | Services::pdf(false), Services::pdfDocument(false), Services::documentFactory() |
| Construir resposta | PdfResponse::download() / inline() → DownloadResponse |
| Capturar falhas | NextPDF\Exception\NextPdfException (tipo base do ecossistema) |
| Geração assíncrona | GeneratePdfJob registrado em Config\Queue::$jobHandlers |
| Validações de path / callable | GeneratePdfJob lança InvalidArgumentException |
Controller de produção — tratamento de erros e observabilidade
Seção intitulada “Controller de produção — tratamento de erros e observabilidade”As exceções lançadas pelo motor do core estendem NextPDF\Exception\NextPdfException. Capture esse único tipo para cobrir falhas do core e das extensões. Esse bloco catch registra o contexto em log e retorna uma resposta de erro bem definida, nunca um catch vazio.
<?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) retorna uma nova biblioteca e um novo documento subjacente a cada chamada. Requisições concorrentes nunca compartilham o estado do documento. Os testes funcionais do pacote garantem esse comportamento.
Tempos de vida de serviço seguros para workers
Seção intitulada “Tempos de vida de serviço seguros para workers”Os registries de fontes e imagens são, por design, singletons com tempo de vida do processo. O registry de fontes é aquecido e travado uma única vez. O registry de imagens é um cache limitado do tipo least recently used (LRU). Em um worker de longa duração (servidor spark do CodeIgniter, runner no estilo RoadRunner ou worker de fila), isso é intencional: registries caros persistem, enquanto cada documento é novo. Não solicite um documento compartilhado (Services::pdfDocument(true)) no código de requisição ou de job; ele existe apenas para reset de teste e compartilharia conteúdo entre requisições.
Geração assíncrona com a CodeIgniter Queue
Seção intitulada “Geração assíncrona com a CodeIgniter Queue”GeneratePdfJob executa a geração de PDF fora da requisição por meio de codeigniter4/queue. O runtime da fila exige duas configurações. Ajuste ambas corretamente.
1. Registre o handler do job por nome
Seção intitulada “1. Registre o handler do job por nome”A fila resolve um job por uma chave de nome, não por uma string de classe. O handler da fila valida o nome do job enviado em relação às chaves de Config\Queue::$jobHandlers. Ele rejeita nomes desconhecidos com CodeIgniter\Queue\Exceptions\QueueException. Registre o job em 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. Despache pelo nome registrado
Seção intitulada “2. Despache pelo nome registrado”Envie o job usando o nome registrado como segundo argumento. O primeiro argumento é o nome da fila. O terceiro argumento é o array de dados do 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. Implemente o builder em App\PdfBuilders
Seção intitulada “3. Implemente o builder em App\PdfBuilders”O job permite callables de builder apenas no namespace App\PdfBuilders e restringe os caminhos de saída a WRITEPATH/pdfs/. Implemente o builder como um método estático. Ele recebe um novo Document e o array de contexto e, em seguida, retorna o 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; }}Execute o worker
Seção intitulada “Execute o worker”php spark queue:work pdf-queueCada execução de job começa com um novo documento de Services::pdfDocument(). Ele aplica o builder e, em seguida, salva no caminho validado. Os testes do pacote verificam que duas execuções consecutivas de job não compartilham o estado do documento.
Casos extremos e armadilhas
Seção intitulada “Casos extremos e armadilhas”- A fila rejeita
GeneratePdfJob::classcomo nome do job no momento do push porque ele não é a chave registrada'generate-pdf'. Sempre envie a chave dejobHandlers. - A string do builder deve corresponder exatamente a
App\PdfBuilders\<Class>::<method>. Funções, outros namespaces ou payloads com prefixo ou sufixo geramInvalidArgumentExceptionantes da execução de qualquer código. - O caminho de saída deve ser resolvido dentro de
WRITEPATH/pdfs/e terminar em.pdf(sem distinção entre maiúsculas e minúsculas). Caminhos com traversal e com prefixo de diretório irmão são rejeitados. codeigniter4/queueé uma dependência apenas de desenvolvimento do pacote. Inclua-a como dependência na aplicação que executa workers.
Desempenho
Seção intitulada “Desempenho”Os registries são criados uma única vez por processo de worker. O custo de construção do documento escala com o conteúdo, não com o adaptador. Para jobs em lote grandes, use o caminho da fila para manter os workers de requisição responsivos. Defina um performance_budget em qualquer receita que tenha uma meta mensurável.
Notas de segurança
Seção intitulada “Notas de segurança”O job de fila é a superfície de maior risco. Quando o broker está acessível, os payloads da fila podem ser influenciados por um atacante. A allowlist de callables e o confinamento de caminhos são tratados em /integrations/codeigniter/security-and-operations/ com os casos de rejeição verificados.
Conformidade
Seção intitulada “Conformidade”- Os controllers recebem serviços concretos, não um container, em conformidade com a orientação sobre service-locator da PSR-11 §1.3.
Contexto comercial
Seção intitulada “Contexto comercial”O core do NextPDF é Apache-2.0. Para produzir saída assinada e PDF/A em jobs de fila, instale o NextPDF Pro ou Enterprise no ambiente do worker. O pacote do CodeIgniter expõe os métodos de serviço correspondentes. Eles retornam null até que o respectivo pacote Premium seja instalado. Consulte </get-license/?intent=codeigniter-async-signing>.
Veja também
Seção intitulada “Veja também”- /integrations/codeigniter/quickstart/ — versão mínima desses controllers.
- /integrations/codeigniter/configuration/ — assinatura, Time Stamping Authority (TSA) e configuração de caminhos.
- /integrations/codeigniter/security-and-operations/ — modelo de ameaças da fila e hardening.
- /integrations/codeigniter/troubleshooting/ — modos de falha de fila e de discovery.
- /integrations/codeigniter/integration/ — referência de wiring e smoke test.