Aller au contenu

Exécuter compat-legacy en production

L’adaptateur peut être exécuté en toute sécurité dans les gestionnaires HTTP, les workers de file d’attente et les processus longue durée. Il est plus sûr que l’ancien TCPDF 6.2.13, car il élimine les deux risques de production les plus courants : la sortie directe vers le tampon et l’appel à die() en cas d’erreur. Cette page explique comment l’exploiter correctement.

Prérequis : effectue l’audit en mode strict dans /integrations/tcpdf-compat/migration/ et déploie avec le mode strict désactivé.

Gestion de la sortie dans les workers et les gestionnaires

Section intitulée « Gestion de la sortie dans les workers et les gestionnaires »

Dans l’ancien TCPDF, Output() écrit directement dans le tampon de sortie actif. Cela corrompt les réponses dans les frameworks HTTP et fait échouer les workers de file d’attente. À la place, l’adaptateur achemine la sortie via un pont de destination sûr.

Utilise la destination qui correspond à l’appelant :

ContexteDestinationPourquoi
Worker de file d’attente qui écrit vers le stockageOutput($path, 'F')Écrit le fichier ; renvoie une chaîne vide. Aucune interaction avec le tampon.
Génération puis attach/uploadOutput($name, 'S')Renvoie les octets du PDF ; tu contrôles leur destination.
Pièce jointe d’e-mailOutput($name, 'E')Renvoie un corps MIME en base64 avec Content-Type: application/pdf.
Réponse HTTP que tu contrôlesOutput($name, 'S')Récupère les octets, puis définis tes propres en-têtes et ton propre corps.
examples/production-worker.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\Exception\TcpdfNotImplementedException;
use NextPDF\Compat\Tcpdf\TCPDF;
/**
* Render an invoice in a queue worker. Returns the storage path.
*
* @throws \RuntimeException on a render failure (Error() throws, not die()).
*/
function renderInvoiceJob(array $invoice, string $storageDir): string
{
$pdf = new TCPDF('P', 'mm', 'A4');
$pdf->SetFont('helvetica', '', 12);
$pdf->AddPage();
$pdf->Cell(0, 10, 'Invoice ' . $invoice['number'], 0, 1);
$path = $storageDir . '/invoice-' . $invoice['number'] . '.pdf';
try {
$pdf->Output($path, 'F'); // writes file, no buffer pollution
} catch (TcpdfNotImplementedException $e) {
// Only reachable if strict mode is on — it must NOT be in production.
throw new \RuntimeException('Adapter strict-mode gap in production: ' . $e->getMessage(), 0, $e);
} catch (\RuntimeException $e) {
// Error() throws RuntimeException instead of die().
throw new \RuntimeException('PDF render failed: ' . $e->getMessage(), 0, $e);
}
return $path;
}

Dans un gestionnaire HTTP, préfère 'S' et définis toi-même les en-têtes :

examples/production-http.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\TCPDF;
$pdf = new TCPDF();
$pdf->AddPage();
$pdf->SetFont('helvetica', '', 12);
$pdf->Cell(0, 10, 'Report');
$bytes = $pdf->Output('report.pdf', 'S');
header('Content-Type: application/pdf');
header('Content-Length: ' . strlen($bytes));
header('Content-Disposition: inline; filename="report.pdf"');
echo $bytes;

Error() lève RuntimeException ; elle n’appelle jamais die(). C’est le changement opérationnel le plus important par rapport à l’ancien TCPDF.

  • Entoure chaque point d’entrée de rendu d’un try/catch.
  • Fais correspondre l’exception au contrat d’erreur de ton application (HTTP 5xx, tâche en échec, nouvelle tentative, file d’attente des lettres mortes).
  • Ne suppose pas que le processus se termine en cas d’échec de rendu : ce n’est pas le cas.

Si tu exécutes une tâche CI périodique en mode strict (recommandé), une TcpdfNotImplementedException y signale un vrai problème : un chemin de code repose sur un paramètre TCPDF non pris en charge. Traite-la comme une tâche de migration, pas comme un test instable.

  • Le document construit ses octets PDF à la demande lors du premier appel de sortie. Close() est facultative ; l’appeler met les octets en cache. Open() est une opération neutre sans danger.
  • endPage() ne fait rien : NextPDF gère le cycle de vie des pages. Supprime-le des boucles critiques ; il n’apporte aucune valeur.
  • Laisse PHP libérer l’adaptateur par le ramasse-miettes entre les tâches. _destroy() réinitialise les données mises en cache de l’adaptateur, mais tu n’as pas besoin de l’appeler explicitement dans les boucles de worker classiques.
  • Construis un nouvel adaptateur par document. Ne réutilise pas une même instance d’adaptateur pour des documents sans rapport dans un worker longue durée ; l’état du document est propre à chaque instance.
  • L’adaptateur est une couche de délégation légère ; le coût vient surtout du moteur, pas de l’adaptateur.
  • Définis les constantes héritées une seule fois au démarrage. LegacyDefaults::register() et LegacyBootstrap::enableAliases() sont toutes deux idempotentes et protégées, si bien que les appels répétés sont peu coûteux. Définir les constantes à chaque requête est du travail inutile.
  • Préfère Output(..., 'S') ou 'F' à 'I'/'D' dans les contextes hors navigateur. Les chemins inline/download produisent une sortie indépendante du framework, ce que tu ne veux généralement pas dans un worker.
  • Pour une génération à fort volume, profile le moteur, pas l’adaptateur. Le budget par page pour la surcharge propre à l’adaptateur est faible par rapport au rendu.
  • Chaque instance d’adaptateur est indépendante et détient son propre état de document. La concurrence au niveau du processus ou du worker est sûre tant que chaque unité de travail utilise sa propre instance d’adaptateur.
  • Les garde-fous d’idempotence de LegacyBootstrap et LegacyDefaults utilisent un état statique local au processus ; ils sont sûrs dans les modèles PHP per-request/per-worker classiques. Ils ne sont pas conçus pour partager un état modifiable entre plusieurs threads.

Liste de vérification avant la mise en production

Section intitulée « Liste de vérification avant la mise en production »
  • Audit en mode strict terminé ; la production tourne avec le mode strict désactivé.
  • Tous les points d’entrée de rendu entourés d’un try/catch pour RuntimeException (aucune dépendance à die()).
  • Les workers utilisent Output(..., 'F') ou 'S', jamais le chemin inline.
  • Constantes héritées définies une seule fois au démarrage, avant la première construction.
  • Une tâche CI périodique en mode strict est en place pour détecter les régressions.
  • Assertions de test au niveau des octets recalibrées (voir /integrations/tcpdf-compat/migration/).
  • /integrations/tcpdf-compat/security-and-operations/ — chiffrement, posture de signature, durcissement
  • /integrations/tcpdf-compat/troubleshooting/ — schémas de défaillance en production et correctifs
  • /integrations/tcpdf-compat/configuration/ — mode strict et hygiène des constantes
  • /integrations/tcpdf-compat/migration/ — l’audit qui doit précéder la mise en production