Pular para o conteúdo
getnextpdf.com

Um motor, todos os frameworks

Spec: PSR-11 Container, §1.1.2Spec: PSR-4 Autoloader, §3

A maioria dos parques PHP em crescimento acaba com mais de um framework. O NextPDF é um único motor de PDF que encontra cada um deles em seus próprios termos: bridges idiomáticas para Laravel, Symfony e CodeIgniter, mais um caminho standalone para código que não roda em nenhum deles. O modelo de documento é compartilhado. Só muda a forma como você o chama.

Uma biblioteca de PDF diferente por stack é um imposto silencioso. Cada uma tem suas próprias peculiaridades, seu próprio tratamento de fontes, sua própria ideia do que “válido” significa. Uma fatura que renderiza corretamente a partir do serviço Laravel pode renderizar de forma sutilmente diferente a partir do worker Symfony, porque uma biblioteca diferente a desenhou. Agora o seu alvo de arquivamento, o posicionamento da sua assinatura e suas tags de acessibilidade dependem de qual equipe entregou o documento. O relatório de bug diz “o PDF está errado”, e a resposta depende de qual dos três motores o produziu.

Padronizar em um único motor faz essa superfície colapsar. Há um único lugar onde um perfil PDF/A é decidido, um pipeline de fontes para certificar, um validador para confiar. O framework em que você por acaso está deixa de ser uma variável na questão de o documento estar correto.

  • O motor core é agnóstico de framework. nextpdf/core não sabe nada sobre HTTP, roteamento ou wiring de container. Ele é um motor de PDF 2.0 e nada além disso.
  • Cada bridge adapta, ela não reimplementa. Os pacotes Laravel, Symfony e CodeIgniter lhe dão um facade ou factory, um helper de resposta HTTP e um caminho de geração enfileirado ou assíncrono — sobre o mesmo motor.
  • Uma bridge segue o seu framework, não o seu documento. Ela muda como você chama o motor, nunca o que o motor pode produzir.
  • O standalone está sempre disponível. Uma ferramenta CLI, um daemon ou uma biblioteca não tem framework do qual fazer bridge; ela constrói um documento diretamente.
  • Um único modelo de documento viaja por todos os quatro. Os mesmos value objects, enums e contrato de saída aparecem em todo lugar, então um documento se move entre pontos de chamada inalterado.

A arquitetura é uma divisão deliberada. O motor é o ativo; a bridge é um adaptador fino que fala os idiomas de um framework. Uma bridge registra um pequeno namespace sobre o core compartilhado por meio do autoloading padrão (Spec: PSR-4 Autoloader, §3) e devolve um documento por meio do contrato de container (Spec: PSR-11 Container, §1.1.2). Esse contrato é o herói silencioso aqui: ele permite que duas resoluções do mesmo identificador retornem instâncias diferentes, que é exatamente como uma bridge lhe dá um documento novo e descartável por requisição, mantendo o registro de fontes parseadas e o cache de imagens como singletons de escopo de processo. Workers de vida longa — Octane, RoadRunner, Swoole, Messenger — obtêm o parsing de fontes amortizado sem vazamento de estado entre requisições, por construção.

Os quatro idiomas diferem apenas na superfície:

  1. Core enginenextpdf/core — the framework-agnostic PDF 2.0 engine; the single shared document model, value objects, and output contract.
  2. Laravel bridgenextpdf/laravel — auto-discovered provider, a Pdf facade, a PdfResponse helper, and a queued GeneratePdfJob.
  3. Symfony bridgenextpdf/symfony — an auto-registered bundle, an injectable PdfFactory, a PdfResponse, and an optional Messenger handler.
  4. CodeIgniter bridgenextpdf/codeigniter — a service and pdf() helper, a Pdf library over a disposable Document, and a PdfResponse.
  5. StandaloneNo framework to bridge from — construct a Document directly in a CLI tool, daemon, or library.
Um único motor core agnóstico de framework alcançado por meio de quatro superfícies idiomáticas: um facade Laravel, um factory Symfony injetado, um serviço CodeIgniter ou um documento standalone construído diretamente — cada um devolvendo o mesmo modelo Document descartável.

Leia o diagrama da esquerda para a direita e a lição é a simetria. Toda superfície resolve para o mesmo Document. O facade Laravel, o factory Symfony, o serviço CodeIgniter e o construtor standalone são quatro portas para uma única sala.

As mesmas três linhas de intenção, expressas em cada idioma. O corpo que constrói o documento — páginas, fontes, células, assinatura, conformidade — é idêntico nos quatro, porque é o mesmo motor.

<?php
declare(strict_types=1);
// Laravel — resolve a fresh document from the container.
use NextPDF\Contracts\PdfDocumentInterface;
$document = app(PdfDocumentInterface::class);
// Symfony — inject the factory, then ask it for a document.
use NextPDF\Symfony\Service\PdfFactory;
$document = $factory->create(); // PdfFactory injected into your service
// CodeIgniter — pull it from the Services layer.
use NextPDF\CodeIgniter\Config\Services;
$document = Services::pdfDocument();
// Standalone — no framework; construct it directly.
use NextPDF\Core\Document;
$document = Document::createStandalone();
// From here, the code is identical regardless of how $document arrived.
$document->addPage();
$document->cell(0, 10, 'One engine, every framework', newLine: true);
$bytes = $document->getPdfData();

As primeiras linhas são a única diferença. Tudo o que vem depois é portátil: mova um serviço de construção de documentos do Symfony para um worker standalone e o código de renderização não muda, porque o contrato do qual ele depende não mudou.

A suposição frequente é que a bridge do framework destrava capacidades — que a validação de assinatura de longo prazo ou o faturamento eletrônico estruturado chega porque você instalou nextpdf/laravel em vez de chamar o motor diretamente. Não chega. Uma bridge muda o ponto de chamada, nunca o alcance do motor. Capacidades core como a saída PDF/A e a assinatura baseline PAdES são de código aberto e alcançam toda superfície; capacidades avançadas são destravadas por uma edição e ficam então disponíveis por meio de qualquer bridge ou do caminho standalone igualmente. Escolher uma integração de framework não é escolher um conjunto de recursos.

O equívoco espelhado é que “um motor” deve significar um único caminho de renderização para todo documento. Não significa. O motor in-process renderiza PDF diretamente; quando um documento genuinamente precisa de um motor de layout de nível navegador, um pacote renderizador cuida disso. Renderização e invocação são eixos separados — o guia de decisão de integração é o lugar que os mapeia.

Uma bridge não expande o que o motor pode renderizar. Esse é o limite honesto, e é o ponto: a capacidade vive no core e no tier, não no adaptador por meio do qual você a alcança.

Framework bridges over one engine — edition availability
EditionAvailability
Core

Toda bridge (Laravel, Symfony, CodeIgniter) e o caminho standalone são Apache-2.0 e funcionam sobre o Core. Elas adaptam ou expõem o motor; elas não restringem recursos e não mudam o que ele pode produzir.

Pro

Capacidades avançadas como a validação de assinatura de longo prazo (PAdES B-LT e B-LTA) são destravadas por uma edição, e então alcançadas de forma idêntica por meio de qualquer bridge ou standalone — nunca trocando de framework. A saída PDF/A de arquivamento e a assinatura baseline PAdES (B-B e B-T) já estão no Core, disponíveis da mesma forma por meio de toda superfície.

Enterprise

O faturamento eletrônico estruturado (EN 16931) e a ferramenta de compliance mais profunda também são capacidades de edição, igualmente as mesmas seja qual for a superfície que chama o motor, enquanto a própria validação de conformidade vem no Core.

Duas fronteiras adicionais valem ser ditas com clareza. Primeiro, cada bridge acompanha um major atual de seu framework — Laravel, Symfony e CodeIgniter cada um fixa um intervalo suportado, então “todos os frameworks” significa a versão suportada de cada um, não toda release histórica; trate a documentação de cada pacote como autoritativa para sua API. Segundo, as bridges são adaptadores de framework, não backends de renderização. Se um documento precisa de um motor completo de layout de navegador, isso é uma escolha de renderizador independente de qual framework chamou o motor.

  • O guia de decisão de integração — o mapa de caso de uso para pacote, incluindo renderizadores e a superfície do serviço Connect, quando você precisa decidir em vez de padronizar.
  • Open core, sem lock-in — por que o motor é o ativo e as bridges são finas, então padronizar não aprisiona você.
  • O pipeline HTML — o que o motor in-process cobre, para que você saiba quando um renderizador de navegador é a questão separada.
  • As fundações do PHP 8.4 — o piso de runtime que toda bridge e o caminho standalone compartilham.
  • Motor corenextpdf/core, o motor de PDF 2.0 agnóstico de framework sobre o qual toda bridge e o caminho standalone se constroem.
  • Bridge de framework — um pacote de integração (Laravel, Symfony, CodeIgniter) que adapta o motor aos idiomas de um framework — facade, factory, resposta, job enfileirado — sem mudar suas capacidades.
  • Caminho standalone — usar o motor core diretamente, sem framework, construindo um Document você mesmo; a rota para ferramentas CLI, daemons e bibliotecas.
  • Documento descartável — o contrato Document de uso único: construir, emitir, descartar. Cada resolução de container retorna um novo, então nenhum estado vaza entre requisições em um worker de vida longa.
  • PAdES — PDF Advanced Electronic Signatures, a família de perfis ETSI para assinatura de PDF. A assinatura baseline (B-B e B-T) está no Core; a validação de longo prazo (B-LT e B-LTA) é uma capacidade das edições avançadas. Qualquer uma é alcançada por meio de qualquer superfície, abordada em profundidade nas páginas de assinatura.