typo3-extension-upgrade について
TYPO3 プロジェクトやコアではなく、拡張コードを新しい LTS バージョンにアップグレードするワークフロースキルです。対応中のバージョン系列を維持し、系列ごとにインストールとテストを確認してから結果を報告します。
主な機能
- 01TYPO3 拡張専用のアップグレード手順
- 02バージョン制約の維持
- 03Rector と Fractor による移行ガイダンス
- 04バージョンごとの PHPStan と PHPUnit 検査
- 05対象バージョンのインストール確認
活用シーン
- 01拡張を新しい TYPO3 LTS にアップグレード
- 02複数の TYPO3 系列との互換性維持
- 03移行やテストの失敗の診断
- 04テスト済み・未テスト系列の報告
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ netresearch/ typo3-extension-upgrade-skill --skill typo3-extension-upgradeインストール後、Claude Code で/typo3-extension-upgradeを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
checkpoints.yamlassets/.php-cs-fixer.phpfractor.phpphpstan.neonphpunit.xmlrector.phpreferences/api-changes.mdapi-traps.mdaudit-mode.mddual-compatibility.mdmulti-version-worktrees.mdpre-upgrade.mdreal-world-patterns.mdthird-party-dependency-upgrades.mdtoolchain-output.mdtroubleshooting.mdupgrade-v11-to-v12.mdupgrade-v12-to-v13.mdupgrade-v13-to-v14.mdverification.mdscripts/scan-deprecations.sh詳細
TYPO3 拡張機能のアップグレードスキル
TYPO3 拡張機能を新しい LTS バージョンにアップグレードするためのフレームワークです。 拡張機能のコードのみが対象で、プロジェクトやコアのアップグレードは対象外です。
完了とは、各行でインストールされると制約に記載されたすべてのバージョンに対して、最後に実行したステップ 10 のコマンドが exit=0 になることです。実行結果で確認し、推測で済ませてはいけません。 あるバージョンでは成功していても、別のバージョンが失敗したままなら完了ではありません。失敗するテストをスキップしたり削除したりして成功扱いにするのも違います。--no-verify を使ったコミットも同様です。これらを満たさない場合は未完了と報告し、その理由が示されている行を記載してください。
失敗するコミットフックは、リポジトリが求めるチェックであり、障害物ではありません。 実行されたコマンドを確認し、自分で実行して、報告された問題を修正してから、もう一度コミットしてください。コードスタイルは通常、リポジトリ独自のフィクサー(設定に従った php-cs-fixer fix)で修正します。PHPStan の指摘はコードを修正します。
実測では、TYPO3 の両バージョンでチェックが成功していた 3 つのエージェントがスタイルまたは PHPStan のフックを --no-verify で回避し、4 つ目は失敗したユニットテストを回避していました。
アップグレードツールキット
| ツール | 目的 | ファイル |
|---|---|---|
| Extension Scanner | 非推奨 API の診断 | TYPO3 Backend |
| Rector | PHP の自動移行 | .php |
| Fractor | PHP 以外の移行 | FlexForms、TypoScript、YAML、Fluid |
| PHPStan | 静的解析 | .php |
コアワークフロー
-
計画フェーズを完了します(
references/pre-upgrade.mdを参照してください) -
フィーチャーブランチを作成します(git がクリーンであることを確認してください)
-
対象バージョンに合わせて
composer.jsonの制約を更新します。v14 の LTS マイナーバージョンは 3 です。^14.4ではなく、必ず^14.3と記述してください — 両方をサポートするには^13.4 || ^14.3とします。v11.5、v12.4、v13.4 が続いているため14.4が次のバージョンに見えますが、該当するリリースは存在しません。そのためcomposer updateは依存関係の解決に失敗し、ゼロ以外のコードで終了して、何もインストールしません。各バージョンの組み合わせの制約については、references/upgrade-v13-to-v14.mdを参照してください。バージョンを追加することは、既存のバージョンを置き換えることではありません。
^14.3だけを記述すると、拡張機能が以前サポートしていたすべてのバージョンが対象外になり、インストールするすべてのユーザーにとって破壊的変更になります。新しいバージョンで依存関係を解決する手っ取り早い方法でもあるため、意図せず起こりがちです。「現在の LTS で動作させて」という依頼は、新しいバージョンを追加することであり、古いバージョンを失うことではありません。 既存の制約を読み、そこに含まれるすべてのバージョンを維持したうえで対象バージョンを追加してください。^12.4 || ^13.4は^12.4 || ^13.4 || ^14.3となり、v13 のみをすでにサポートしていた拡張機能だけが^13.4 || ^14.3となります。バージョンを削除するのは、依頼された場合に限り、メンテナーが確認できる場所にその旨を記載してください。1 つのコードベースですべてに対応できるかどうかは別の問題であり、references/dual-compatibility.mdで説明しています。 -
サードパーティの依存関係について、メジャーバージョンの変更を監査します(
references/third-party-dependency-upgrades.mdを参照してください) -
rector process --dry-runを実行して確認し、変更を適用します -
fractor process --dry-runを実行して確認し、変更を適用します -
php-cs-fixer fixを実行します -
サポート対象の依存関係バージョンごとに
phpstan analyseを実行し、エラーを修正します -
phpunitを実行してテストを修正します。Tests/はアップグレードの一部であり、その結果ではありません。 テストが参照するクラスが v14 で削除されている場合、それはテストの失敗ではなく致命的なエラーです。インポート、親クラス、プロパティ、シグネチャのいずれかで使われていると、PHPUnit がテストスイートを読み込む際に停止するため、テストはまったく実行されません。実行する前に、両方のツリーで削除された型を検索してください。grep -rnE 'TypoScriptFrontendController|StandaloneView|TemplateView|HashService|LocalPreviewHelper|LocalCropScaleMaskHelper|FreezableBackendInterface' Classes/ Tests/該当箇所はすべて修正が必要です。use行を削除するだけでは解決しません。インポートがないと、Foo::classは現在の名前空間から解決されるため、実在しないクラスを指定することになり、古いバージョンにも対応する必要がある箇所までエラーが表面化しません。インポートではなく、使用箇所を変更してください。削除されたクラスのcreateMockも、名前を置き換えるだけでは修正できません。references/upgrade-v13-to-v14.mdを参照してください。 その後、テストスイートを実行する前と編集後に、拡張機能のルートディレクトリからこれを実行します。 上記の編集内容を確認するチェックで、貼り付けたまま実行できるブロックです。探すパスも、バインドする変数もありません。出力された行は、削除された型を短縮名で使用していて、その型のuse行がないファイルを示します。手順 10 に進む前に、それぞれ修正してください。出力がなく終了コードが 0 なら問題ありません。検出があると終了コード 1 で終了するため、この後に&& phpunitを続けても、問題を含んだ状態でテストスイートが実行されることはありません。types='TypoScriptFrontendController|StandaloneView|TemplateView|HashService|LocalPreviewHelper|LocalCropScaleMaskHelper|FreezableBackendInterface' strip='foreach (token_get_all(file_get_contents($argv[1])) as $t) { if (!is_array($t)) { echo $t; } elseif (!in_array($t[0], [T_COMMENT, T_DOC_COMMENT, T_CONSTANT_ENCAPSED_STRING, T_ENCAPSED_AND_WHITESPACE], true)) { echo $t[1]; } }' bad=0 for f in $(grep -rlE --include='*.php' "\b($types)\b" Classes Tests 2>/dev/null); do code=$(php -r "$strip" "$f") for t in $(grep -oE "\b($types)\b" <<<"$code" | sort -u); do grep -qE "^use .*\\\\$t;" <<<"$code" || grep -qE "\\\\$t\b" <<<"$code" \ || { echo "$f: $t used without import"; bad=1; } done done test "$bad" = 0コメントと文字列リテラルは最初に除去されます。削除された型を記した docblock は、PHP が解決する参照ではありません。 同じチェックは、このスキルの
checkpoints.yamlではTU-58で、automated-assessmentのランナー用です。スクリプトが近道として使われるには、すぐ実行できる場所にある必要があるため、ここにブロックを記載しています。同じケースとモデルでこの手順を測定したところ、ランナーを実行する指示は 3 回中 3 回コンテキストに含まれていましたが、実行されたのは 0 回でした。このブロックを丸ごと貼り付けた場合は、6 回中 5 回実行されました。 -
対象バージョンをインストールし、そのバージョンでテストスイートを実行します。 すでにインストール済みのバージョンでテストスイートが成功しても、対象バージョンについては何も証明できません。それは古いコードが古いテストに合格しているだけです。まず 2 段階でインストールし、その後テストします。
composer update "typo3/*" --with typo3/cms-core:^14.3 -W --no-install \ && rm -rf vendor && composer install \ && composer show typo3/cms-core | grep '^versions' \ && vendor/bin/phpunit -c Build/phpunit/UnitTests.xml; echo "exit=$?"コマンドは連結されているため、インストールに失敗すると、古いツリーに対して PHPUnit が実行される前に停止します。
versions行には実際にインストールされたバージョンが表示され、exit=には最後に実行された手順の終了ステータスが表示されます。 v13 から v14 への更新では、1 回の実行では失敗します。Composer がtypo3/class-alias-loaderをアップグレードした後、新しいファイルに対して古いプラグインを実行するため、ロックファイルを書き込んだ後にClass "…\CaseSensitiveToken" not foundで停止します。古いvendor/の上からcomposer installを実行しても同じように停止するため、先にvendor/を削除します。 パッケージ引数が重要です。これを指定せずにcomposer updateを実行すると、すべての依存関係が更新され、テスト結果が TYPO3 と無関係な更新に左右されます。-Wを指定すると、TYPO3 パッケージ自身の依存関係も追随できます。 対象バージョンをインストールできない場合、アップグレードはテストされていません。 エラーとともにその旨を報告し、テストスイートが合格したとは報告しないでください。すでにインストールされていたバージョンでテストが成功しても、それはそのバージョンでの結果にすぎません。 -
制約で維持されるすべての旧系列について、同じコマンドを実行します。
^13.4の場合は--with typo3/cms-core:^13.4とし、以下同様です。対象バージョンでテストが成功するようにした移行によって、その下の系列で失敗することがあります。実際に、エージェントが v14 ではテストを成功させたものの、v13 のテストスイートは失敗したままにして、3 系列すべてで互換性があると報告した例がありました。修正したら、すべての系列で再実行してください。この環境ではまったくインストールできない系列、つまり Composer が依存関係を解決できない、またはダウンロードできない系列は、失敗ではなく未テストです。Composer のエラーとともに報告し、その理由で制約から削除しないでください(手順 3)。 -
報告する前に、各系列で最後に実行したテストの
exit=行を確認します。 インストールできた系列でexit=0以外の場合、作業は完了していません。手順 9 に戻ってください。インストールに失敗した系列は、手順 11 の未テストのケースです。テストスイートが失敗したままコミットすることや、--no-verifyを付けてコミットすることも完了とは言えません。報告には系列ごとに、インストールされたバージョンとテストスイートの合否を記載します。失敗した場合は、PHPUnit または Composer が出力したエラーも記載してください。 -
成功基準を確認します(
references/verification.mdを参照)。
完了とは、制約に記載されたすべての系列で、その系列をインストールしたうえでテストスイートに合格することです。 インストール可能なすべての系列が対象で、残りは未テストとして報告します。制約の範囲を広げたことでも、すでにテストが成功していたバージョンで再び成功したことでもありません。
実際に確認された事例:対象バージョンを一度もインストールしていない、テストスイートの読み込みに3回失敗した、または3件のエラーと13件の失敗を確認したうえで --no-verify を使ってコミットしたエージェントが、「✅ 719 tests pass」と報告しました。実行時に出力される exit= の行を見れば、どの状況なのかがわかります。報告を書く前に確認してください。
削除されたクラスがどこで参照されているかによって、問題が発生するタイミングが決まります。import、親クラス、プロパティ、シグネチャで参照されている場合は、PHPUnit がテストスイートを読み込むときに解決されるため、実行自体が始まらず、テストの失敗とはまったく異なる形でエラーが発生します。メソッド本体の中だけで参照されている場合は、そのテストが実行されたときに失敗しますが、テストスイートの残りは引き続き成功します。こちらのほうが厄介さは少なく、サマリーの行だけでは見落としやすい失敗です。
マイグレーションでテストが失敗したとき
失敗します。Rector を実行する前は成功していたテストスイートでも、何十件ものエラーが発生して戻ってくるのはよくあることで、それを一つずつ解決するのが作業です。
成功状態に戻すために、マイグレーションを絶対に元に戻さないでください。 実際に確認された事例:あるエージェントは Rector を実行して Tests: 719, Errors: 34 となった後、Classes/ にあるマイグレーション済みファイルをすべて破棄しました。その結果、OK (719 tests, 1176 assertions) となり、コードは一切含めずに composer.json、ext_emconf.php、ビルドファイル2つをコミットしました。このコミットは、コードが実際には対応していないバージョンへの対応を謳っています。考えられる3つの結果の中でも最悪です。アップグレードに失敗すればそれは明らかであり、試みていないなら正直ですが、これはそのどちらでもありません。
元に戻した後の成功状態は、作業を始めたときの状態と同じです。意味があるのは、対象バージョンをインストールし、マイグレーションを適用した状態でステップ10を実行して成功した場合だけです。
テストをスキップしたり削除したりして成功状態にするのも、テストを1つずつ元に戻しているのと同じです。 正当なスキップが認められるケースは1つあります。バージョンによってコードの分岐があり、古いバージョンでのみ存在する分岐をテストする場合です。新しいバージョンでは、次のテストをスキップしてもかまいません。
if (!class_exists(X::class)) { self::markTestSkipped(...); }
ただし、新しいバージョンの分岐にも独自のテストがある場合に限ります。それ以外のスキップ、早期リターンするバージョンチェック、テストメソッドの削除は、失敗を隠してしまいます。コミット時の --no-verify も同じです。失敗を報告するチェックを無効にしてしまいます。
また、続行してよいか確認するために作業を中断しないでください。 実際に確認された事例:あるエージェントは対象バージョンをインストールし、新しいバージョンで削除されたクラスの使用箇所を数えた後、git reset --hard を実行し、リファクタリングすべきかどうかを尋ねました。拡張機能を新しいバージョンで動作させるという依頼は、それを実行する許可です。新しいバージョンで削除されたものを置き換えることこそがアップグレードです。何か所で使われているかにかかわらず、それは作業であり、報告すべき発見事項ではありません。確認するのは、サポート対象のバージョン系列を削除するかどうかなど、依頼で未確定の事項だけです(ステップ3)。
自動適用しない場合
複数バージョンとの互換性、テスト不足、変更内容の不明確さ、または複雑な API(DBAL、Extbase)が関わる場合は、Rector/Fractor を盲目的に適用しないでください。その代わりにルールを手動で適用し、変更のたびにテストしてください。
サードパーティ依存関係のアップグレード
composer.json で依存関係の許容範囲を新しいメジャーバージョンまで広げる場合は、API の使用箇所を列挙し、新しい API と照合し、モックを検証し、シグネチャに差異がある場合はアダプターパターンを使用して、メジャーバージョンごとに PHPStan を実行してください。詳しくは references/third-party-dependency-upgrades.md を参照してください。
クイックコマンド
rector process --dry-run && rector process # PHP migrations
fractor process --dry-run && fractor process # Non-PHP migrations
php-cs-fixer fix && phpstan analyse && phpunit # Quality checks
アセットテンプレート
assets/ 内の設定テンプレート:rector.php、fractor.php、phpstan.neon、phpunit.xml、.php-cs-fixer.php
参考資料
| 参考資料 | 次の場合に使用します… |
|---|---|
references/pre-upgrade.md | 計画チェックリスト、バージョン監査、リスク評価 |
references/api-changes.md | TYPO3 のバージョンごとに非推奨/削除された API を確認する場合 |
references/api-traps.md | バージョンをまたぐ落とし穴:TCA の制限、起動順序、DI のバイパス、返されない v12 以降の redirect()、TYPO3/typo3 の特定の参照に基づいて API の事実を確かめる場合 |
references/upgrade-v11-to-v12.md | TYPO3 v11 から v12 へアップグレードする場合 |
references/upgrade-v12-to-v13.md | TYPO3 v12 から v13 へアップグレードする場合 |
references/upgrade-v13-to-v14.md | TYPO3 v13 から v14 へアップグレードする場合 |
references/dual-compatibility.md | 2 つのバージョンとの互換性(v12 + v13) |
references/real-world-patterns.md | 実際の移行例 |
references/toolchain-output.md | Rector/Fractor のドライラン出力 |
references/troubleshooting.md | Rector によるコードの破損、PHPStan エラー、テストの失敗 |
references/third-party-dependency-upgrades.md | TYPO3 以外の依存関係(メジャーバージョンの更新、アダプターパターン) |
references/verification.md | 成功基準と実環境でのテスト |
references/multi-version-worktrees.md | LTS ごとのワークツリー構成、バックポートのワークフロー、バージョン横断の CI マトリックス |
references/audit-mode.md | 評価/見積もり:自動化できない調査結果のみをチケット化する場合 |
scripts/scan-deprecations.sh <path> | 非推奨/削除された API と落とし穴を対象とする、決定論的な grep スキャン |