Aller au contenu

Migrer de TCPDF 6.x vers NextPDF

La migration suit un ordre clair. Passe d’abord au moteur NextPDF avec le plus petit changement possible. Mets en évidence ce qui fonctionne déjà. Audite ce qui ne fonctionne pas. Corrige chaque site d’appel, un par un. Puis retire l’adaptateur. La couche de compatibilité prend en charge les étapes deux à quatre ; ce n’est pas la destination.

Cette page couvre la stratégie. Pour le comportement exact d’une méthode donnée, consulte /integrations/tcpdf-compat/method-coverage/ ainsi que la matrice de référence in-repo docs/TCPDF_COVERAGE.md.

TCPDF 6.x codebase

Swap dependency: install compat-legacy

Run existing suite unchanged

Strict-mode audit: enumerate behavioral gaps

Fix call sites: drop ignored params or move to modern API

Re-baseline byte-level test assertions

Remove the TCPDF dependency

Incrementally retire the adapter onto Document

Diagram

Chaque étape maintient l’application livrable. Tu n’as jamais besoin d’une bascule globale d’un seul coup.

Installe nextpdf/compat-legacy (voir /integrations/tcpdf-compat/install/). Ne retire pas encore tecnickcom/tcpdf — conserver les deux te permet de les comparer.

Choisis comment les sites d’appel hérités résolvent la classe :

  • Recommandé : remplace use/require par use NextPDF\Compat\Tcpdf\TCPDF; dans chaque fichier. C’est explicite et facile à repérer.
  • Quand tu ne peux pas encore toucher aux sites d’appel : active les alias globaux opt-in une seule fois au démarrage avec LegacyBootstrap::enableAliases() (voir /integrations/tcpdf-compat/boot-and-discovery/). Cela fait pointer \TCPDF et les quatre classes utilitaires vers l’adaptateur.

En pratique, les deux stratégies s’excluent mutuellement. Si la véritable bibliothèque TCPDF est encore autoloadable et que tu actives les alias globaux, l’alias est ignoré lorsqu’une classe \TCPDF existe déjà. Tu risques alors de continuer silencieusement à utiliser le TCPDF hérité. Pendant l’étape 1, privilégie des imports par fichier afin de savoir exactement quelle classe utilise chaque site d’appel. Consulte /integrations/tcpdf-compat/troubleshooting/.

Étape 2 — Lancer la suite existante sans la modifier

Section intitulée « Étape 2 — Lancer la suite existante sans la modifier »

Lance ta suite de tests complète contre l’adaptateur sans aucun autre changement de code. La plupart des méthodes déléguées (94 sur les ~120 recensées) ont un comportement compatible. Attends-toi à deux catégories d’échecs prévisibles :

  1. Assertions au niveau de l’octet. Les tests qui comparent exactement les octets PDF échoueront car le moteur est une implémentation indépendante. C’est attendu, pas un défaut. Reporte-les à l’étape 4.
  2. Branches sur la valeur de retour. Quelques méthodes renvoient des valeurs de substitution de compatibilité plutôt que des valeurs calculées — notamment MultiCell() renvoie 1, et Write() renvoie 0. Le code qui prend une branche en fonction de ces valeurs de retour doit être ajusté.

Répertorie chaque échec. Classe chacun comme référence-octet, valeur-de-retour ou véritable écart de comportement.

C’est l’étape qui rend la migration sûre. Lance la suite (ou un chemin de production représentatif) avec le mode strict activé :

examples/migration-audit.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\Exception\TcpdfNotImplementedException;
use NextPDF\Compat\Tcpdf\TCPDF;
function renderInvoice(TCPDF $pdf): void
{
// ... your existing rendering code, unchanged ...
}
$pdf = new TCPDF('P', 'mm', 'A4');
$pdf->setStrictMode(true);
try {
renderInvoice($pdf);
$pdf->Output(__DIR__ . '/audit.pdf', 'F');
} catch (TcpdfNotImplementedException $e) {
// Each message names the method, the ignored parameters, and a hint.
fwrite(STDERR, 'MIGRATION GAP: ' . $e->getMessage() . "\n");
}

Chaque TcpdfNotImplementedException est une tâche à traiter. Le message contient la méthode, la liste exacte des paramètres ignorés et un indice de migration. L’ensemble des méthodes qui lèvent une exception est répertorié et vérifié par des tests dans tests/Unit/Compat/Tcpdf/TcpdfStrictModeTest.php. La justification de chacune se trouve dans docs/TCPDF_COVERAGE.md.

Lance le mode strict comme une tâche CI dédiée, et non en production. Le but est de faire ressortir les écarts, pas de déclencher des exceptions en production.

Pour chaque écart, choisis la correction adaptée la moins coûteuse :

Type d’écartCorrection
Paramètre ignoré sans importance (par ex. un $align TCPDF dont tu ne t’es jamais servi)Supprime le paramètre. L’appel devient exactement compatible.
Paramètre ignoré qui comptait (par ex. un Image() cliquable)Réexprime-le via l’API moderne. Dessine l’image, puis ajoute Document::link() par-dessus le rectangle.
Méthode non implémentée (setSignature(), endPage())endPage() / Open() : supprime l’appel. Signature : voir /integrations/tcpdf-compat/security-and-operations/ — nécessite une édition commerciale.
Méthode non applicable (setPDFVersion(), setUserRights())Supprime. La sortie est toujours en PDF 2.0 ; les droits utilisateur sont dépréciés en PDF 2.0.
Branche sur la valeur de retourCalcule la valeur toi-même, ou déplace cette logique vers l’API moderne.

Utilise l’échappatoire pour tout ce que la surface TCPDF ne peut pas exprimer :

examples/migration-escape-hatch.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\TCPDF;
$pdf = new TCPDF();
$pdf->AddPage();
// Legacy path stays as-is for the parts that work:
$pdf->SetFont('helvetica', '', 12);
$pdf->Cell(0, 10, 'Header line', 0, 1);
// Modern path for what the TCPDF surface cannot express here:
$document = $pdf->getDocument();
$document->image('logo.png', 10, 30, 40, 0);
$document->link(10, 30, 40, 20, 'https://example.com');

Remplace les assertions sur les octets exacts par des assertions sur ce qui compte réellement :

  • La sortie commence par %PDF et se parse (niveau smoke).
  • Le contenu textuel rendu est présent (extrais le texte et fais des assertions dessus).
  • Les propriétés structurelles (nombre de pages, taille de page, présence d’un signet) correspondent.

C’est un coût ponctuel qui produit des tests capables de survivre aux futures montées de version du moteur.

Une fois que l’audit en mode strict passe avec le mode strict désactivé en production et que la suite est verte sur les assertions réétalonnées, retire tecnickcom/tcpdf :

Fenêtre de terminal
composer remove tecnickcom/tcpdf

Relance la suite. Si quelque chose pointe encore vers la véritable classe TCPDF, la mise en garde sur les alias de l’étape 1 s’applique — corrige les sites d’appel restants pour importer explicitement l’adaptateur.

L’adaptateur est une aide à la migration, pas une couche permanente. Une fois TCPDF retiré et le moteur éprouvé, mets l’adaptateur à la retraite de façon incrémentale :

  1. Dans chaque module, remplace new TCPDF(...) par la construction moderne NextPDF\Core\Document.
  2. Remplace les appels de méthode TCPDF par leurs équivalents modernes (les appels getDocument() que tu as déjà ajoutés à l’étape 4 servent de modèle).
  3. Quand un module ne référence plus l’adaptateur, supprime ses imports de compatibilité.
  4. Quand aucun module ne référence l’adaptateur, retire nextpdf/compat-legacy de composer.json.

Tu utilises alors l’API PDF 2.0 moderne sans aucune couche de compatibilité.

  • nextpdf/compat-legacy installé ; lien avec le moteur vérifié.
  • Les sites d’appel importent explicitement l’adaptateur (ou les alias sont activés avec le véritable TCPDF retiré du chemin d’autoload).
  • Suite complète lancée contre l’adaptateur ; échecs classés.
  • Tâche CI en mode strict ajoutée ; chaque écart catalogué.
  • Chaque écart corrigé (suppression du paramètre / API moderne / suppression de l’appel).
  • Assertions au niveau de l’octet réétalonnées vers le contenu/structure.
  • tecnickcom/tcpdf retiré ; suite verte.
  • Adaptateur mis à la retraite module par module ; dépendance retirée.
  • /integrations/tcpdf-compat/method-coverage/ — comportement par méthode et conseils de remplacement
  • docs/TCPDF_COVERAGE.md — matrice de référence, vérifiée par des tests
  • /integrations/tcpdf-compat/configuration/ — déplacer la configuration hors des constantes globales
  • /integrations/tcpdf-compat/security-and-operations/ — chiffrement et signature pendant la migration
  • /integrations/tcpdf-compat/troubleshooting/ — le conflit alias/real-TCPDF et autres pièges