Implemente estratégias personalizadas de recuperação de erros e repetição
Visão geral
Seção intitulada “Visão geral”Um serviço de documentos em produção faz mais do que capturar e registrar uma exceção. Ele decide o que fazer em seguida: continuar com uma saída degradada, alternar para um segundo caminho de renderização, repetir com uma entrada aceita pelo engine ou entregar as páginas construídas antes da falha. Esta receita mostra quatro estratégias de recuperação construídas sobre a hierarquia de exceções do NextPDF e os métodos de inspeção do estado do documento:
- Degradação controlada em uma falha de fonte — capture
NextPDF\Exception\FontNotFoundException, faça fallback para uma face garantida e continue construindo o documento. - Um renderizador alternativo — quando o caminho in-process
Document::writeHtml()rejeitar a entrada, repita por meio deDocument::writeHtmlChrome(), a ponte com o Chrome donextpdf/artisan. - Repetir com HTML alternativo — quando ocorrer
NextPDF\Exception\HtmlParsingExceptionouNextPDF\Exception\CssResolutionBudgetExceededException, repita com uma variante simplificada e comprovadamente válida de HTML. - Recuperação de documento parcial — leia
Document::getNumPages()após uma falha e salve o que já foi construído, em vez de descartá-lo.
Você já sabe como capturar no nível certo. A página complementar Trate erros com a hierarquia de exceções do NextPDF aborda a hierarquia em si. Esta página mostra o que você faz depois da captura.
Esta receita tem como alvo a edição core de software de código aberto (OSS). Toda interface de programação de aplicações (API) citada aqui está em nextpdf/core. A única dependência opcional é nextpdf/artisan para a alternativa com Chrome.
Instalação
Seção intitulada “Instalação”composer require nextpdf/core:^3A estratégia de renderizador alternativo usa adicionalmente a ponte com o Chrome:
composer require nextpdf/artisanQuando nextpdf/artisan está ausente, Document::writeHtmlChrome() lança NextPDF\Exception\PageLayoutException em vez de renderizar. A estratégia alternativa a seguir trata a ausência da ponte como mais um caso recuperável.
Visão conceitual
Seção intitulada “Visão conceitual”A recuperação depende de dois fatos sobre o NextPDF, ambos verificados em relação ao código-fonte.
A hierarquia de exceções informa o que é recuperável. Toda exceção de domínio estende a base abstrata NextPDF\Exception\NextPdfException, que estende RuntimeException e implementa NextPDF\Contracts\ContextAwareExceptionInterface. Capture um subtipo específico para escolher um caminho de recuperação adequado àquela falha:
FontNotFoundExceptioncarregagetFontName(),getSearchPaths()ewasFallbackAttempted()— suficiente para repetir com uma face diferente.HtmlParsingExceptioncarregagetRule(),getPosition()egetHtmlSnippet()— suficiente para decidir se vale a pena tentar novamente com uma versão simplificada.CssResolutionBudgetExceededExceptioncarregagetVisits()egetBudget()— um sinal de que uma folha de estilos reduzida pode liberar um seletor patológico.- Um limite importante:
NextPDF\Support\DegradedExceptionestendeRuntimeExceptiondiretamente, nãoNextPdfException. Portanto,catch (NextPdfException $e)não captura uma rejeição da política de degradação. Quando aNextPDF\Contracts\DegradationPolicyativa éStrictouBalanced, captureDegradedExceptionexplicitamente para recuperar-se dela.
O documento pode ser inspecionado enquanto você o constrói. Um Document expõe seu estado de construção por meio de acessores somente leitura. getNumPages() retorna a contagem total de páginas, incluindo a página ativa ainda não descarregada, e getPage() retorna o índice de base zero da página atual. Após uma falha no meio da construção, leia getNumPages() para descobrir se existem páginas completas e, em seguida, chame save() ou getPdfData() para emiti-las. O engine também registra eventos de degradação não fatais: getWarnings() retorna uma list<NextPDF\Support\Warning>, hasWarnings() informa se alguma foi coletada e hasDegradedParity() informa se a fidelidade da saída foi afetada. Esses métodos permitem que uma rotina de recuperação diferencie “concluído sem problemas” de “concluído com fidelidade reduzida” sem precisar analisar uma exceção.
A política de degradação controla quais eventos você trata como exceções e quais trata como avisos. NextPDF\Core\Config usa por padrão DegradationPolicy::Balanced, que avisa e continua em degradações limitadas, mas lança quando há impacto bloqueante. DegradationPolicy::Permissive nunca lança e coleta tudo no canal de avisos. DegradationPolicy::Strict lança diante de qualquer risco de conformidade, perda semântica ou impacto bloqueante. Escolha primeiro a política e, em seguida, escreva a recuperação para os formatos de falha que ela produz.
Superfície da API
Seção intitulada “Superfície da API”O código de recuperação abaixo usa estes membros verificados:
NextPDF\Core\Document::createStandalone(?Config $config = null): self,addPage(),setFont(string $family, string $style = '', float $size = 12.0): static,cell(...),writeHtml(string $html): static,writeHtmlChrome(string $html, ?float $width = null, ?float $height = null): static,save(string $path): void,getPdfData(): string,getNumPages(): int,getPage(): int,getWarnings(): list<Warning>,hasWarnings(): bool,hasDegradedParity(): bool,addFontDirectory(string $directory): static.NextPDF\Core\Config::withDegradationPolicy(DegradationPolicy $policy): selfe o padrão dedegradationPolicyigual aDegradationPolicy::Balanced.NextPDF\Contracts\DegradationPolicy—Strict,Balanced,Permissive.NextPDF\Exception\NextPdfException(base abstrata),NextPDF\Exception\FontNotFoundException,NextPDF\Exception\HtmlParsingException,NextPDF\Exception\CssResolutionBudgetExceededException,NextPDF\Exception\WriterException,NextPDF\Exception\PageLayoutException.NextPDF\Support\DegradedException(carregandocapabilityepolicy),NextPDF\Support\Capability(id,status,reason,isDegraded()),NextPDF\Support\Warning,NextPDF\Support\WarningSeverity.
Exemplo de código — Início rápido
Seção intitulada “Exemplo de código — Início rápido”A menor recuperação útil captura uma falha de fonte ausente, faz fallback para uma face garantida e continua. Este trecho deixa de fora o tratamento mais amplo presente no exemplo de produção. Para um manipulador completo com logs e o limite de DegradedException, leia o exemplo de produção abaixo.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;use NextPDF\Exception\FontNotFoundException;
$doc = Document::createStandalone();$doc->addPage();
try { // A face that may not be installed on every host. $doc->setFont('CorporateSans', '', 12);} catch (FontNotFoundException $e) { // Recover: fall back to a face the engine always resolves. $doc->setFont('helvetica', '', 12);}
$doc->cell(0, 10, 'Rendered with a recovered font.', newLine: true);$doc->save(getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/out.pdf');Exemplo de código — Produção
Seção intitulada “Exemplo de código — Produção”O exemplo completo conecta as quatro estratégias em um único pipeline de renderização: uma alternativa de fonte, uma alternativa de renderizador do caminho in-process para o Chrome, uma repetição com HTML alternativo e a recuperação de documento parcial conduzida por getNumPages(). Ele respeita o canal de saída do harness e nunca captura uma Exception genérica nem deixa um bloco catch vazio.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Contracts\ContextAwareExceptionInterface;use NextPDF\Contracts\DegradationPolicy;use NextPDF\Core\Config;use NextPDF\Core\Document;use NextPDF\Exception\CssResolutionBudgetExceededException;use NextPDF\Exception\FontNotFoundException;use NextPDF\Exception\HtmlParsingException;use NextPDF\Exception\NextPdfException;use NextPDF\Exception\PageLayoutException;use NextPDF\Exception\WriterException;use NextPDF\Support\DegradedException;
/** * A minimal structured sink. In production this is your PSR-3 logger; the * exception class and its structured context become log fields. * * @param array<string, mixed> $context */function logRecovery(string $message, array $context): void{ fwrite(STDERR, $message . ' ' . json_encode($context, JSON_THROW_ON_ERROR) . "\n");}
/** * Resolve a usable font, degrading from the requested face to a guaranteed * fallback. Returns the face actually applied so the caller can record it. * * @param non-empty-string $requested * @param non-empty-string $fallback * * @return non-empty-string */function applyFontWithFallback(Document $doc, string $requested, string $fallback): string{ try { $doc->setFont($requested, '', 12);
return $requested; } catch (FontNotFoundException $e) { // STRATEGY 1 — graceful degradation on a font failure. logRecovery('Font unavailable; degrading to a guaranteed face', [ 'exception' => $e::class, 'font_name' => $e->getFontName(), 'searched' => $e->getSearchPaths(), 'fallback' => $fallback, ]); $doc->setFont($fallback, '', 12);
return $fallback; }}
/** * Render HTML through the in-process pipeline, then through the Chrome bridge, * then through a simplified HTML variant. Each layer recovers a more specific * failure than the last. */function renderHtmlWithRecovery(Document $doc, string $primaryHtml, string $simplifiedHtml): void{ try { // Primary path: the in-process HTML/CSS pipeline. $doc->writeHtml($primaryHtml);
return; } catch (CssResolutionBudgetExceededException $e) { // STRATEGY 3 — retry with alternative HTML for a pathological selector. logRecovery('CSS resolution budget exceeded; retrying with simplified HTML', [ 'exception' => $e::class, 'visits' => $e->getVisits(), 'budget' => $e->getBudget(), ]); $doc->writeHtml($simplifiedHtml);
return; } catch (HtmlParsingException $e) { // STRATEGY 2 — fall back to the Chrome renderer for input the // in-process parser rejects. The Chrome bridge uses a browser CSS // engine, so it may accept what the in-process parser would not. logRecovery('In-process HTML parse failed; trying the Chrome fallback renderer', [ 'exception' => $e::class, 'rule' => $e->getRule(), 'position' => $e->getPosition(), ]);
try { $doc->writeHtmlChrome($primaryHtml);
return; } catch (PageLayoutException $chromeError) { // The Chrome bridge is absent (nextpdf/artisan not installed) or // rejected the input. Last resort: the simplified HTML variant // through the in-process pipeline. logRecovery('Chrome fallback unavailable; retrying with simplified HTML', [ 'exception' => $chromeError::class, ]); $doc->writeHtml($simplifiedHtml);
return; } }}
// --- Configure the degradation policy up front ---------------------------// Balanced (the default) warns on bounded degradation and throws only on a// blocking impact. A regulated workflow would choose DegradationPolicy::Strict.$config = (new Config())->withDegradationPolicy(DegradationPolicy::Balanced);$doc = Document::createStandalone($config);
$primaryHtml = '<h1>Quarterly report</h1><p>Body paragraph with rich styling.</p>';$simplifiedHtml = '<h1>Quarterly report</h1><p>Body paragraph (simplified).</p>';
$outputPath = getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/out.pdf';
try { $doc->addPage(); $applied = applyFontWithFallback($doc, 'CorporateSans', 'helvetica'); $doc->cell(0, 12, 'Custom error recovery patterns', newLine: true);
renderHtmlWithRecovery($doc, $primaryHtml, $simplifiedHtml);
$doc->save($outputPath);
logRecovery('Document built', [ 'font_applied' => $applied, 'pages' => $doc->getNumPages(), 'has_warnings' => $doc->hasWarnings(), 'degraded_parity' => $doc->hasDegradedParity(), ]);} catch (DegradedException $e) { // BOUNDARY: DegradedException extends RuntimeException directly, NOT // NextPdfException, so the catch-all below would not have caught it. // Under Strict/Balanced policy a blocking degradation lands here. logRecovery('Capability degraded under the active policy; emitting a built partial', [ 'exception' => $e::class, 'capability' => $e->capability->id, 'status' => $e->capability->status->value, 'reason' => $e->capability->reason ?? 'unknown', 'policy' => $e->policy->value, ]); // STRATEGY 4 — partial-document recovery: save whatever pages exist. if ($doc->getNumPages() > 0) { $doc->save($outputPath); }} catch (WriterException $e) { // Serialization or I/O failure: the in-memory document is valid but could // not be written. Surface the stage so infrastructure can act on it. logRecovery('PDF write failed; document was valid in memory', [ 'exception' => $e::class, 'writer_state' => $e->getWriterState(), 'output_path' => $e->getOutputPath(), ]);} catch (NextPdfException $e) { // Catch-all for every other NextPDF\Exception\*. STRATEGY 4 again: if any // complete pages were built before the failure, emit them rather than // discarding the work. $context = ['exception' => $e::class, 'pages' => $doc->getNumPages()]; if ($e instanceof ContextAwareExceptionInterface) { $context += $e->getContext(); } logRecovery('Unrecovered NextPDF failure; attempting a partial save', $context); if ($doc->getNumPages() > 0) { $doc->save($outputPath); }}
fwrite(STDERR, "Recovery pipeline complete.\n");STDOUT permanece livre para o harness. Os diagnósticos de recuperação vão para STDERR, e o arquivo Portable Document Format (PDF) só é gravado em NEXTPDF_COOKBOOK_OUTPUT.
Casos extremos e armadilhas
Seção intitulada “Casos extremos e armadilhas”- Ordene os blocos catch do específico para o geral. O PHP usa o primeiro
catchcompatível. Colocarcatch (NextPdfException $e)antes decatch (WriterException $e)transforma o bloco específico em código morto, porqueWriterExceptionestendeNextPdfException. DegradedExceptionfica fora da hierarquia. Ela estendeRuntimeException, nãoNextPdfException. Um pipeline que captura apenasNextPdfExceptiondeixa uma rejeição de política estrita se propagar sem captura. CaptureDegradedException(ou umaRuntimeExceptionmais ampla) quando uma política de degradação diferente da padrão estiver ativa.- Uma fonte alternativa também pode falhar. Se a face alternativa não estiver registrada, o segundo
setFont()lança novamente. Use um alias Base14 comohelvetica, que o engine resolve sem uma consulta ao sistema de arquivos, ou registre uma face empacotada por meio deaddFontDirectory()na inicialização para que a alternativa seja garantida. getNumPages()conta a página ativa ainda não descarregada. Ela retorna a contagem de páginas descarregadas mais um quando uma página está aberta no momento. Um “salvamento parcial” inclui a página que estava sendo construída quando a falha ocorreu, o que normalmente é o que você quer. Se você precisar apenas de páginas totalmente concluídas, ramifique também com base emgetPage().- A alternativa do Chrome altera a fidelidade, não apenas a disponibilidade. O pipeline in-process e a ponte com o Chrome usam engines de layout diferentes, então um documento que recorre ao Chrome pode ter aparência diferente. Trate a alternativa como uma recuperação, não como um substituto transparente, e registre qual caminho produziu a saída.
- Uma repetição deve usar uma entrada comprovadamente válida. A repetição com HTML simplificado só ajuda quando a variante simplificada é genuinamente mais simples: menos seletores aninhados, sem cadeias de
:has()que esgotem o orçamento de resolução. Repetir com a mesma entrada que já falhou leva à mesma exceção em loop. - Inspecione os avisos após uma execução sem problemas. Uma renderização que retorna sem lançar ainda pode ter sido degradada. Verifique
hasDegradedParity()e leiagetWarnings()antes de tratar a saída como fiel pixel a pixel; sobDegradationPolicy::Permissivetoda degradação é um aviso, nunca uma exceção.
Desempenho
Seção intitulada “Desempenho”- A recuperação acrescenta custo apenas no caminho de falha. O NextPDF lança em estados excepcionais, então uma renderização sem problemas não paga nada pelo
try/catchao redor. - Uma alternativa de renderizador executa a renderização novamente. A tentativa in-process é descartada e a tentativa do Chrome começa do zero; portanto, uma renderização alternativa custa, no pior caso, ambos os tempos de renderização mais a ida e volta entre processos até o Chrome. Considere isso ao definir os tempos limite de requisição.
- Uma repetição com HTML alternativo analisa um segundo documento. Mantenha a variante simplificada pequena para que a repetição seja barata em relação à tentativa primária.
- Um salvamento parcial serializa as páginas já construídas. Seu custo escala com a contagem de páginas restantes, não com o trabalho que falhou.
Notas de segurança
Seção intitulada “Notas de segurança”- Não exiba mensagens de exceção brutas nem caminhos do sistema de arquivos aos usuários finais. A mensagem de uma
FontNotFoundExceptioninclui os diretórios pesquisados, e umaWriterExceptioninclui o caminho de saída; ambas expõem a estrutura do servidor. Registre o contexto estruturado no servidor e retorne uma mensagem genérica ao chamador. - Trate o HTML repetido como entrada não confiável em toda tentativa. A alternativa e a repetição com HTML simplificado fluem pelo mesmo limite de entrada; o pipeline in-process e a ponte com o Chrome aplicam cada um sua própria política de segurança de HTML, e uma repetição não relaxa essa validação. Não presuma que uma variante “simplificada” seja mais segura só porque você a criou.
- Um salvamento parcial ainda grava um arquivo. Aplique a uma saída parcial as mesmas regras de validação de caminho, permissões e local de armazenamento que você aplica a uma saída completa.
Document::save()rejeita stream wrappers e bytes nulos e resolve o diretório pai para bloquear a travessia de caminho (path traversal), mas o destino que você passa é responsabilidade sua.
Conformidade
Seção intitulada “Conformidade”Esta receita não faz nenhuma afirmação normativa sobre padrões. Ela compõe as APIs públicas de exceção e de inspeção de documentos do NextPDF em um fluxo de controle de recuperação; não afirma um comportamento definido pela ISO 32000-2 nem por qualquer outro padrão, portanto não traz um bloco citations:.
Esta página é verificada com o perfil de reprodutibilidade semantic. O documento recuperado carrega um /ID no trailer e uma data de modificação que são regerados a cada salvamento; portanto, a identidade byte a byte não é alcançável. A comparação da árvore de sintaxe abstrata (AST) estrutural mais somente metadados é estável entre execuções.
Veja também
Seção intitulada “Veja também”- Trate erros com a hierarquia de exceções do NextPDF — granularidade da captura e contexto estruturado, a base desta página.
- Módulo Exception — a referência completa de exceções.
- Módulo Support —
DegradedException,Capability,Warninge os tipos de degradação. - Módulo Config — configuração da política de degradação.
- Renderize PDFs com segurança em um worker de longa duração — recuperação em um worker que reutiliza registries compartilhados.