コンテンツにスキップ

TCPDF 6.x から NextPDF への移行

移行は明確な順序に沿って進めます。まずは可能な限り小さな変更で NextPDF エンジンへ移行します。すでに動作する箇所を確認し、動作しない箇所を監査します。呼び出し箇所ごとに修正し、その後アダプターを削除します。互換レイヤーはステップ 2 から 4 を支援するためのものであり、最終的な到達点ではありません。

このページでは移行戦略について説明します。個々のメソッドの正確な挙動については、/integrations/tcpdf-compat/method-coverage/ と、リポジトリ内の信頼できるマトリクス 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

どの段階でも、アプリケーションは出荷可能な状態を維持します。一括切り替えは一切必要ありません。

nextpdf/compat-legacy をインストールします(/integrations/tcpdf-compat/install/ を参照)。この時点では tecnickcom/tcpdf はまだ削除しないでください。両方を残しておくと比較を実行できます。

レガシーの呼び出し箇所でクラスを解決する方法を選択します。

  • 推奨: ファイルごとに use/requireuse NextPDF\Compat\Tcpdf\TCPDF; に変更します。明示的で、grep しやすくなります。
  • まだ呼び出し箇所を変更できない場合: 起動時に LegacyBootstrap::enableAliases() で、オプトインのグローバルエイリアスを一度だけ有効化します(/integrations/tcpdf-compat/boot-and-discovery/ を参照)。これにより \TCPDF と 4 つのヘルパークラスはアダプターに解決されます。

この 2 つの方法は、実質的には互いに排他的です。実際の TCPDF ライブラリがまだオートロード可能な状態でグローバルエイリアスを有効化すると、 \TCPDF クラスがすでに存在する場合、エイリアスはスキップされます。その結果、 気づかないうちにレガシーの TCPDF を使い続ける可能性があります。ステージ 1 では、各呼び出し箇所がどのクラスを使用するかを正確に把握できるよう、 ファイルごとのインポートを優先してください。参照: /integrations/tcpdf-compat/troubleshooting/.

他のコード変更を一切加えずに、アダプターに対して全テストスイートを実行します。委譲されるメソッドの大半(調査した約 120 のうち 94)は互換性のある挙動を示します。予測可能な失敗は 2 種類あります。

  1. バイトレベルのアサーション。 エンジンは独立した実装であるため、PDF のバイト列を完全一致で比較するテストは失敗します。これは想定された挙動であり、不具合ではありません。これらはステージ 4 まで先送りします。
  2. 戻り値による分岐。 一部のメソッドは計算結果ではなく、互換性のためのプレースホルダーを返します。特に MultiCell()1 を、Write()0 を返します。これらの戻り値に基づいて分岐しているコードは調整が必要です。

すべての失敗を一覧化します。それぞれを バイトベースライン戻り値、または 真の挙動差異 に分類します。

これは移行を安全に進めるための段階です。厳格モードを有効化し、スイート(または代表的な本番経路)を実行します。

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");
}

TcpdfNotImplementedException は 1 つの作業項目です。メッセージには、メソッド名、無視された正確なパラメーターの一覧、移行のヒントが含まれます。例外をスローするメソッドの一覧は tests/Unit/Compat/Tcpdf/TcpdfStrictModeTest.php に列挙されており、テストで検証されています。それぞれの根拠は docs/TCPDF_COVERAGE.md に記載されています。

厳格モードは本番環境ではなく、専用の CI ジョブとして実行します。目的は差異を明らかにすることであり、本番環境で例外をスローさせることではありません。

各差異について、最も低コストで正しい修正を選択します。

差異のパターン修正方法
無視されるパラメーターが重要でない場合(e.g. 依存していなかった TCPDF の $align など)パラメーターの削除。呼び出しは完全互換。
無視されるパラメーターが重要だった場合(e.g. クリック可能な Image() リンクなど)モダン API での書き換え。画像を描画してから、その矩形の上に Document::link() を追加。
メソッドが未実装の場合(setSignature()endPage()endPage() / Open(): 呼び出しの削除。署名: /integrations/tcpdf-compat/security-and-operations/ を参照。商用エディションが必要。
該当しないメソッドの場合(setPDFVersion()setUserRights()削除。出力は常に PDF 2.0。user-rights は PDF 2.0 では非推奨。
戻り値による分岐値の自前計算、またはロジックのモダン API への移行。

TCPDF の表面 API では表現できないものには、エスケープハッチを使用します。

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');

バイト完全一致のアサーションを、実際に重要な点を検証するアサーションに置き換えます。

  • 出力が %PDF で始まり、解析可能であること(スモークレベル)。
  • レンダリングされたテキスト内容が存在すること(テキストを抽出して検証)。
  • 構造的なプロパティ(ページ数、ページサイズ、アウトラインの有無)が一致すること。

これは一度きりのコストであり、将来のエンジン更新でも壊れにくいテストが得られます。

本番環境では厳格モードをオフにしたまま、厳格モード監査に合格し、ベースラインを再設定したアサーションでスイートがグリーンになったら、tecnickcom/tcpdf を削除します。

Terminal window
composer remove tecnickcom/tcpdf

スイートを再実行します。依然として実際の TCPDF クラスに解決されるものがあれば、ステージ 1 のエイリアスに関する注意が当てはまっています。残りの呼び出し箇所を修正し、アダプターを明示的にインポートしてください。

アダプターは移行を支援するためのものであり、恒久的なレイヤーではありません。TCPDF を取り除き、エンジンの動作を確認できたら、アダプターを段階的に廃止します。

  1. 各モジュールで、new TCPDF(...) をモダンな NextPDF\Core\Document の構築に置き換えます。
  2. TCPDF のメソッド呼び出しを、対応するモダンな呼び出しに置き換えます(ステージ 4 ですでに追加した getDocument() の呼び出しが雛形になります)。
  3. モジュールがアダプターを参照しなくなったら、互換用のインポートを削除します。
  4. どのモジュールもアダプターを参照しなくなったら、nextpdf/compat-legacycomposer.json から削除します。

これで互換レイヤーのない、モダンな PDF 2.0 API への移行が完了します。

  • nextpdf/compat-legacy をインストール済み。エンジンのリンクを検証済み。
  • 呼び出し箇所がアダプターを明示的にインポート(または、実際の TCPDF をオートロードパスから取り除いた上でエイリアスを有効化)。
  • アダプターに対して全スイートを実行。失敗を分類済み。
  • 厳格モードの CI ジョブを追加。すべての差異を一覧化済み。
  • 各差異を修正(パラメーター削除 / モダン API / 呼び出し削除)。
  • バイトレベルのアサーションを content/structure. にベースライン再設定済み。
  • tecnickcom/tcpdf を削除済み。スイートがグリーン。
  • アダプターをモジュール単位で廃止。依存関係を削除済み。
  • /integrations/tcpdf-compat/method-coverage/ — メソッドごとの挙動と置き換えガイダンス
  • docs/TCPDF_COVERAGE.md — 信頼でき、テストで検証されたマトリクス
  • /integrations/tcpdf-compat/configuration/ — 設定をグローバル定数から移行する方法
  • /integrations/tcpdf-compat/security-and-operations/ — 移行中の暗号化と署名
  • /integrations/tcpdf-compat/troubleshooting/ — alias/real-TCPDF の競合やその他の落とし穴