mcpField
T
typo3-extension-upgrade

typo3-extension-upgrade

github.com/netresearch开发1278 次安装1 天前更新
代码仓库

关于 typo3-extension-upgrade

用于将 TYPO3 扩展代码升级到较新 LTS 版本的工作流技能,不用于升级项目或核心。它强调保留已有版本支持,并逐个版本安装和运行测试后再报告结果。

主要功能

  1. 01仅针对 TYPO3 扩展的升级流程
  2. 02保留现有版本约束
  3. 03Rector 和 Fractor 迁移指引
  4. 04按版本运行 PHPStan 和 PHPUnit
  5. 05核验实际安装的目标版本

适用场景

  1. 01将扩展升级到较新的 TYPO3 LTS
  2. 02维护对多个 TYPO3 版本线的兼容性
  3. 03诊断迁移和测试失败
  4. 04报告已测试与未测试的版本线

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/netresearch/typo3-extension-upgrade-skill --skill typo3-extension-upgrade

安装后在 Claude Code 中输入/typo3-extension-upgrade确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

checkpoints.yaml
assets/
.php-cs-fixer.php
fractor.php
phpstan.neon
phpunit.xml
rector.php
references/
api-changes.md
api-traps.md
audit-mode.md
dual-compatibility.md
multi-version-worktrees.md
pre-upgrade.md
real-world-patterns.md
third-party-dependency-upgrades.md
toolchain-output.md
troubleshooting.md
upgrade-v11-to-v12.md
upgrade-v12-to-v13.md
upgrade-v13-to-v14.md
verification.md
scripts/
scan-deprecations.sh

详细介绍

TYPO3 扩展升级技能

用于将 TYPO3 扩展升级到较新 LTS 版本的框架。 仅升级扩展代码,不升级项目或核心。

只有在每一条约束所指定的安装版本上,最后运行第 10 步命令并得到 exit=0,才算完成——请以实际运行结果为准,不能想当然。 不能只在一条版本线上通过,而另一条仍失败。不能因为跳过或删除失败的测试就算通过。不能使用 --no-verify 提交。达不到这些要求,就应报告尚未完成,并指出能说明原因的输出。

提交钩子失败,说明仓库要求通过某项检查,而不是遇到了障碍。 阅读钩子运行的命令,亲自运行该命令,修复它报告的问题,然后重新提交:代码风格通常由仓库自带的修复工具(使用其配置运行 php-cs-fixer fix)修正,PHPStan 报告的问题则应在代码中修复。实际情况是:3 个 Agent 在 TYPO3 两条版本线上都通过后,仍用 --no-verify 绕过了代码风格或 PHPStan 钩子;第 4 个则绕过了自己失败的单元测试。

升级工具包

工具用途文件
Extension Scanner诊断已弃用的 APITYPO3 后台
Rector自动化 PHP 迁移.php
Fractor非 PHP 迁移FlexForms、TypoScript、YAML、Fluid
PHPStan静态分析.php

核心工作流

  1. 完成规划阶段(参阅 references/pre-upgrade.md)

  2. 创建功能分支(确认 Git 工作区干净)

  3. 更新 composer.json 中的版本约束,以适配目标版本。v14 的 LTS 次版本是 3:写 ^14.3,绝不能写 ^14.4——若要同时支持两个版本,可写 ^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。 只有在明确要求时才移除某条版本线,并在维护者能看到的地方说明。一个代码库能否兼容所有这些版本,是另一个问题,答案见 references/dual-compatibility.md。

  4. 审查第三方依赖是否有主版本变更(参阅 references/third-party-dependency-upgrades.md)

  5. 运行 rector process --dry-run,然后审查并应用变更

  6. 运行 fractor process --dry-run,然后审查并应用变更

  7. 运行 php-cs-fixer fix

  8. 针对每个受支持的依赖版本运行 phpstan analyse,并修复错误

  9. 运行 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
    

    首先会剥离注释和字符串字面量:提及已移除类型的文档块不是 PHP 会解析的引用。 同一项检查是此技能的 checkpoints.yaml 中的 TU-58,对应 automated-assessment 中的运行器。此处直接给出代码块,是因为脚本必须容易找到,才能成为捷径。在相同案例和模型下测得,此步骤中,有三次试验将运行运行器的指令放入上下文,但没有一次实际运行;而这段代码块原样粘贴后,六次试验中有五次实际运行。

  10. 安装目标版本,并在该版本上运行测试套件。 已安装版本上的测试套件通过,并不能证明目标版本可用——那只是旧代码通过了旧测试。先分两步安装,再测试:

    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 时,单次执行会在过程中失败:Composer 升级 typo3/class-alias-loader 后,会在新文件上运行旧插件,而锁文件写入后,插件因 Class "…\CaseSensitiveToken" not found 而崩溃。在旧 vendor/ 上运行 composer install 也会以同样方式崩溃,所以要先删除 vendor/。 软件包参数很重要:如果不加该参数,composer update 会升级所有依赖,测试结果就会受到与 TYPO3 无关的升级影响。-W 会允许 TYPO3 软件包自身的依赖随之更新。 如果无法安装目标版本,就表示升级未经测试。 请在报告中说明这一点并附上错误信息,不要声称测试套件通过:已安装版本上的通过结果只适用于该版本。

  11. 对约束保留的每个较旧系列,运行相同的命令——约束为 ^13.4 时使用 --with typo3/cms-core:^13.4,其他系列依此类推。让目标版本通过的迁移可能会导致下一个较低系列的测试失败:实测中,有一个 Agent 让 v14 通过,却让 v13 的测试套件继续失败,并报告称三个系列都兼容。修复后,重新运行每个系列的测试。无法在此处安装的系列——Composer 无法解析或下载的系列——属于未经测试,而非测试失败:请附上 Composer 的错误信息进行报告,并且绝不能因此将该系列从约束中移除(步骤 3)。

  12. 报告之前,检查每个系列最后一次运行的 exit= 行。 对于已成功安装的系列,任何非 exit=0 的结果都表示工作尚未完成:返回步骤 9。安装失败的系列属于步骤 11 所述的未经测试情形。带着失败的测试套件提交,或使用 --no-verify 提交,也不算完成。报告应逐个系列说明安装了什么版本以及测试套件是否通过;如果未通过,还要附上 PHPUnit 或 Composer 输出的失败信息。

  13. 验证成功标准(请参阅 references/verification.md)

只有约束中列出的每个系列都已安装且测试套件通过,才算完成——包括所有可安装的系列;其余系列应报告为未经测试。这不代表扩大了约束,也不代表测试套件在原本已通过的版本上仍为绿色。 经实测:Agent 报告“✅ 719 个测试通过”,但从未安装目标版本;或者看到测试套件三次无法加载;又或者看到 3 个错误和 13 个失败,并使用 --no-verify 提交。运行结果中的 exit= 行会告诉你属于哪种情况;写报告前先看这一行。

已删除的类在何处被引用,决定了问题何时出现。在 import、父类、属性或签名中引用时,PHPUnit 会在加载测试套件时解析它,因此完全不会运行任何测试,失败的表现也与测试失败截然不同。若只在方法体内引用,则会在执行对应测试时失败,而测试套件的其他部分仍会通过——这种失败更不显眼,也更容易在摘要中被忽略。

迁移导致测试失败时

这是难免的。Rector 运行前原本全绿的测试套件,运行后通常会出现几十个错误,逐一处理这些错误就是你的工作。

绝不要为了恢复全绿而回退迁移。 经实测:某个 Agent 运行 Rector,看到 Tests: 719, Errors: 34,便丢弃了 Classes/ 下所有迁移后的文件,得到 OK (719 tests, 1176 assertions),随后提交了 composer.json、ext_emconf.php 和两个构建文件——没有提交任何代码。该提交声称支持一个代码实际上并不支持的版本,这是三种可能结果中最糟糕的一种:升级失败是显而易见的,没有尝试升级则是坦诚的,而这种情况两者都不是。

回退后恢复全绿,与你一开始的状态并无二致。唯一值得认可的全绿,是第 10 步中目标版本已安装且迁移已完成时的全绿。

跳过或删除失败的测试来恢复全绿,本质上也是回退,只不过一次处理一个测试。 有一种跳过是合理的:当代码按版本分支时,只存在于旧版本中的分支测试——if (!class_exists(X::class)) { self::markTestSkipped(...); }——可以在新版本中跳过,前提是新版本的分支也有自己的测试。除此之外,任何跳过、提前返回的版本检查,或删除测试方法,都会让失败悄无声息。使用 --no-verify 提交也一样:它会让本应报告问题的检查静默无声。

也不要停下来询问是否继续。 经实测:某个 Agent 安装目标版本,统计新版本删除的某个类被使用的次数,运行 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 的某个 ref 核实 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双版本兼容(v12 + v13)
references/real-world-patterns.md真实迁移示例
references/toolchain-output.mdRector/Fractor 试运行输出
references/troubleshooting.mdRector 破坏代码、PHPStan 错误、测试失败
references/third-party-dependency-upgrades.mdTYPO3 以外的依赖项(主版本升级、适配器模式)
references/verification.md成功标准和真实场景测试
references/multi-version-worktrees.md各 LTS 版本的工作树布局、回移工作流、跨版本 CI 矩阵
references/audit-mode.md评估/估算:仅针对无法自动化的发现创建工单
scripts/scan-deprecations.sh <path>确定性 grep 扫描,查找已弃用/已移除的 API 和易错点

外部资源

评论

支持 Markdown