NextPDF Symfony em produção
Visão geral
Seção intitulada “Visão geral”Use o bundle em runtimes PHP de longa duração. Ele cria documentos não compartilhados, bloqueia o registro de fontes após o aquecimento e redefine o cache de imagens entre requisições. Faça streaming de arquivos grandes em Portable Document Format (PDF) e delegue tarefas pesadas a workers do Messenger.
Ciclo de vida do serviço seguro para workers
Seção intitulada “Ciclo de vida do serviço seguro para workers”Runtimes de longa duração mantêm o contêiner ativo entre requisições; portanto, o estado específico de cada requisição deve permanecer isolado. FrankenPHP, RoadRunner e workers do Messenger seguem esse modelo. O services.php do bundle define o ciclo de vida abaixo, verificado em relação às definições de serviço:
- Document — não compartilhado. O
nextpdf.document(e os aliasesPdfDocumentInterface/Document) é resolvido como uma nova instância em cada resolução. Sob o PSR-11 (PHP Standard Recommendation 11), um contêiner pode legitimamente retornar um valor diferente em cadaget()para o mesmo id (PSR-11 §1.1.2). Resolva um documento por requisição. Nunca o mantenha entre requisições. - FontRegistry — compartilhado e bloqueado. O registro é um singleton durante toda a vida do processo. Após
warmup()(quandopreload_fontsnão está vazio), o compiler pass chamalock(). O bloqueio impede a mutação em tempo de execução e a contaminação do estado das fontes entre requisições. - ImageRegistry — compartilhado, redefinido por requisição. O cache de imagens limitado, do tipo least recently used (LRU), é compartilhado, mas recebe a marcação
kernel.resetcom o métodoreset, então o Symfony o limpa entre requisições em runtimes que respeitamkernel.reset. - Contratos EInvoice — não compartilhados. Quando há implementações Premium presentes, os serviços de embedder, validator, profile e schematron são registrados como não compartilhados. O contexto do parser permanece restrito a cada chamada e nunca vaza entre requisições.
Padrão de injeção recomendado
Seção intitulada “Padrão de injeção recomendado”Injete PdfFactory, um objeto compartilhado e sem estado que mantém a configuração, e chame create() por requisição:
public function __construct(private readonly PdfFactory $pdf) {}
public function action(): Response{ $doc = $this->pdf->create(); // fresh, disposable // ... build ... return PdfResponse::inline($doc, 'document.pdf');}Não injete Document nem nextpdf.document em um serviço compartilhado mantido entre requisições. Em vez disso, resolva-o dentro do método com escopo de requisição.
Streaming de documentos grandes
Seção intitulada “Streaming de documentos grandes”PdfResponse::streamDownload() e streamInline() retornam um StreamedResponse. O callback envia o corpo do PDF em blocos de 64 KB e faz flush do buffer após cada bloco, o que limita o buffer de resposta para documentos grandes. Os comportamentos abaixo são verificados em relação ao PdfResponse:
- As variantes em streaming omitem
Content-Lengthintencionalmente, porque o objeto de resposta não conhece o tamanho do corpo de antemão. Barras de progresso de download e alguns proxies funcionam melhor com um tamanho conhecido. Use os métodos sem streamingdownload()ouinline()quando o documento for pequeno o suficiente para ser mantido em memória e um tamanho de conteúdo for desejável. - As variantes em streaming emitem os mesmos cabeçalhos de segurança e o mesmo
Cache-Control: private, max-age=0, must-revalidateque as variantes com buffer.
Escolha o streaming para relatórios de vários megabytes e exportações em lote. Escolha as variantes com buffer para respostas pequenas e sensíveis à latência.
Geração assíncrona em escala
Seção intitulada “Geração assíncrona em escala”Delegue a geração ao Messenger quando as requisições precisarem retornar rapidamente ou quando a renderização exigir muito processamento.
- Implemente
PdfBuilderInterfacepara cada tipo de documento. - Registre os builders em um
container.service_locatore conecte esse locator aoGeneratePdfHandlerna propriedade$builderLocator. - Roteie
GeneratePdfMessagepara um transporte durável. - Execute os workers com tempos de vida limitados.
Tempo de vida limitado dos workers
Seção intitulada “Tempo de vida limitado dos workers”Recicle os workers para impedir que uma alocação vazada em uma dependência de terceiros cresça sem limite:
php bin/console messenger:consume async \ --limit=200 \ --memory-limit=256M \ --time-limit=3600As chaves de configuração messenger.timeout e messenger.retries do bundle registram o timeout por mensagem e o orçamento pretendido de novas tentativas. Imponha o mesmo comportamento por meio da estratégia de novas tentativas do Symfony e das flags dos workers.
Segurança do caminho de saída nos workers
Seção intitulada “Segurança do caminho de saída nos workers”GeneratePdfMessage valida o caminho de saída no momento da construção. GeneratePdfHandler o revalida em tempo de execução, antes de gravar em disco. Essa verificação em duas etapas é importante para o trabalho assíncrono. Uma mensagem pode ficar em uma fila entre o despacho e o consumo; portanto, o handler não confia cegamente no caminho enfileirado. Restrinja as permissões de sistema de arquivos dos workers ao diretório de saída pretendido como defesa em profundidade.
Observabilidade
Seção intitulada “Observabilidade”Os serviços FontRegistry e ImageRegistry aceitam um Psr\Log\LoggerInterface opcional (vinculado com nullOnInvalid()). Quando a aplicação fornece um logger, esses registros podem emitir diagnósticos por meio dele. O logger é um colaborador opcional e substituível no contrato de logger PSR-3 (PSR-3). Para visibilidade no nível da requisição, registre logs em torno de PdfFactory::create() e do handler do Messenger no código da aplicação. Use messenger:consume -vv durante a triagem de incidentes.
Checklist de implantação
Seção intitulada “Checklist de implantação”- Fixe uma versão major de
nextpdf/corenocomposer.jsonda aplicação (o bundle aceita^3.0 || ^5.2). - Certifique-se de que
ext-mbstringeext-zlibestejam habilitados na imagem PHP implantada (caso contrário, o bundle falha imediatamente na inicialização). - Defina previamente
preload_fontscom as fontes que os documentos usam, para que o registro aqueça e bloqueie na inicialização, em vez de na primeira requisição. - Aponte
cache_pathpara um local gravável e persistente se você depende de artefatos em cache entre implantações. Caso contrário, o padrão%kernel.cache_dir%é adequado. - Execute
php bin/console cache:warmupdurante a implantação para que o contêiner compilado (incluindo as sondas de extensões opcionais) seja construído antes do tráfego. - Use um transporte durável do Messenger (não
sync) para o trabalho assíncrono em produção e recicle os workers com--limit/--memory-limit/--time-limit.
Casos extremos e armadilhas
Seção intitulada “Casos extremos e armadilhas”- Respostas em streaming atrás de um proxy com buffer — Um proxy que armazena o corpo inteiro em buffer elimina o ganho de memória. Configure o proxy para fazer streaming de respostas PDF ou use respostas com buffer nesse caso.
kernel.resetnão respeitado — Sob um runtime que não chamakernel.reset, o cache de imagens é limitado porimage_cache_mb, mas não é limpo entre requisições; dimensione o limite adequadamente.- Manter um documento entre requisições — Um
Documentcapturado de uma requisição anterior carregará estado obsoleto. Sempre resolva por requisição viaPdfFactory.
Conformidade
Seção intitulada “Conformidade”Cada linha é uma afirmação normativa nesta página, vinculada a um reference_id completo de 64 caracteres hexadecimais do corpus restrito da standards development organization (SDO). A procedência do manifesto do corpus e do transporte de recuperação está em _sidecars/rag-citations.yaml.
| Especificação | Cláusula | reference_id | Afirmação |
|---|---|---|---|
| PSR-11 | psr_11_container#1.1.2.p3.b | Serviço não compartilhado: valor distinto por resolução | |
| PSR-3 | psr_3_logger#x3.p17 | Colaborador de logger opcional |
Veja também
Seção intitulada “Veja também”- /integrations/symfony/configuration/ — ciclo de vida do serviço e parâmetros.
- /integrations/symfony/security-and-operations/ — cabeçalhos de resposta, validação de caminho, tratamento de chaves.
- /integrations/symfony/troubleshooting/ — diagnósticos de inicialização e tempo de execução.
- /integrations/symfony/quickstart/ — a configuração assíncrona mínima.