typo3-extension-upgrade

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TYPO3 Extension Upgrade Skill

TYPO3 扩展升级操作指南

Systematic framework for upgrading TYPO3 extensions to newer LTS versions.
TYPO3 API First: Always use TYPO3's built-in APIs, core features, and established conventions before creating custom implementations. Do not reinvent what TYPO3 already provides. Always verify that the APIs and methods you use exist and are not deprecated in your target TYPO3 version (v13 or v14) by checking the official TYPO3 documentation.
Scope: Extension code upgrades only. NOT for TYPO3 project/core upgrades.
一套将TYPO3扩展系统升级至更新LTS版本的框架方案。
TYPO3 API优先原则: 在创建自定义实现之前,请始终使用TYPO3的内置API、核心功能和既定规范。不要重复开发TYPO3已提供的功能。请务必通过官方TYPO3文档验证你使用的API和方法在目标TYPO3版本(v13或v14)中是否存在且未被弃用。
适用范围: 仅适用于扩展代码升级。不适用于TYPO3项目/核心版本升级。

Upgrade Toolkit

升级工具包

ToolPurposeFiles
Extension ScannerDiagnose deprecated APIsTYPO3 Backend
RectorAutomated PHP migrations
.php
FractorNon-PHP migrationsFlexForms, TypoScript, YAML, Fluid
PHPStanStatic analysis
.php
工具用途处理文件类型
Extension Scanner诊断已弃用的APITYPO3后端
Rector自动化PHP代码迁移
.php
Fractor非PHP代码迁移FlexForms、TypoScript、YAML、Fluid
PHPStan静态代码分析
.php

Planning Phase (Required)

规划阶段(必填)

Before ANY code changes for major upgrades:
  1. List all files with hardcoded versions (composer.json, CI, Docker, Rector)
  2. Document scope - how many places need changes?
  3. Present plan to user for approval
  4. Track progress with todo list
在进行重大升级的任何代码修改之前:
  1. 列出所有包含硬编码版本的文件(composer.json、CI配置、Docker配置、Rector配置)
  2. 记录升级范围 —— 需要修改的位置有多少?
  3. 向用户提交升级计划并获得批准
  4. 使用待办事项列表跟踪进度

Pre-Upgrade Checklist

升级前检查清单

  • Extension key and current TYPO3 version documented
  • Target TYPO3 version(s) identified (v13, v14, or both)
  • Current PHP version and target PHP version noted
  • All deprecation warnings from logs collected
  • Extension Scanner report reviewed
  • Dependencies checked for target version compatibility
  • 已记录扩展标识和当前TYPO3版本
  • 已确定目标TYPO3版本(v13、v14或两者兼顾)
  • 已记录当前PHP版本和目标PHP版本
  • 已收集日志中的所有弃用警告
  • 已审阅Extension Scanner报告
  • 已检查依赖项与目标版本的兼容性

Upgrade Workflow

升级工作流

1. Prepare Environment

1. 准备环境

bash
undefined
bash
undefined

Create backup/snapshot

Create backup/snapshot

ddev snapshot --name=before-upgrade
ddev snapshot --name=before-upgrade

Verify git is clean

Verify git is clean

git status
git status

Create feature branch

Create feature branch

git checkout -b feature/typo3-14-upgrade
undefined
git checkout -b feature/typo3-14-upgrade
undefined

2. Update Version Constraints

2. 更新版本约束

json
// composer.json
{
    "require": {
        "php": "^8.2",
        "typo3/cms-core": "^13.0 || ^14.0"
    }
}
php
// ext_emconf.php
$EM_CONF[$_EXTKEY] = [
    'constraints' => [
        'depends' => [
            'typo3' => '13.0.0-14.99.99',
            'php' => '8.2.0-8.4.99',
        ],
    ],
];
json
// composer.json
{
    "require": {
        "php": "^8.2",
        "typo3/cms-core": "^13.0 || ^14.0"
    }
}
php
// ext_emconf.php
$EM_CONF[$_EXTKEY] = [
    'constraints' => [
        'depends' => [
            'typo3' => '13.0.0-14.99.99',
            'php' => '8.2.0-8.4.99',
        ],
    ],
];

3. Run Rector

3. 运行Rector

Rector handles PHP code migrations automatically.
Rector可自动处理PHP代码迁移。

Configuration

配置

php
<?php
// rector.php
declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Ssch\TYPO3Rector\Set\Typo3LevelSetList;
use Ssch\TYPO3Rector\Set\Typo3SetList;

return RectorConfig::configure()
    ->withPaths([
        __DIR__ . '/Classes',
        __DIR__ . '/Configuration',
        __DIR__ . '/Tests',
    ])
    ->withSkip([
        __DIR__ . '/Resources',
    ])
    ->withSets([
        // PHP version upgrades
        LevelSetList::UP_TO_PHP_82,
        
        // TYPO3 upgrades
        Typo3LevelSetList::UP_TO_TYPO3_13,
        Typo3SetList::TYPO3_13,
    ])
    ->withImportNames();
php
<?php
// rector.php
declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Ssch\TYPO3Rector\Set\Typo3LevelSetList;
use Ssch\TYPO3Rector\Set\Typo3SetList;

return RectorConfig::configure()
    ->withPaths([
        __DIR__ . '/Classes',
        __DIR__ . '/Configuration',
        __DIR__ . '/Tests',
    ])
    ->withSkip([
        __DIR__ . '/Resources',
    ])
    ->withSets([
        // PHP version upgrades
        LevelSetList::UP_TO_PHP_82,
        
        // TYPO3 upgrades
        Typo3LevelSetList::UP_TO_TYPO3_13,
        Typo3SetList::TYPO3_13,
    ])
    ->withImportNames();

Run Rector

执行Rector

bash
undefined
bash
undefined

Dry run first

Dry run first

vendor/bin/rector process --dry-run
vendor/bin/rector process --dry-run

Review changes, then apply

Review changes, then apply

vendor/bin/rector process
vendor/bin/rector process

Review git diff

Review git diff

git diff
undefined
git diff
undefined

4. Run Fractor

4. 运行Fractor

Fractor handles non-PHP file migrations (FlexForms, TypoScript, YAML).
Fractor处理非PHP文件的迁移(FlexForms、TypoScript、YAML)。

Configuration

配置

php
<?php
// fractor.php
declare(strict_types=1);

use a]9r\Fractor\Configuration\FractorConfiguration;
use a9f\Typo3Fractor\Set\Typo3LevelSetList;

return FractorConfiguration::configure()
    ->withPaths([
        __DIR__ . '/Configuration',
        __DIR__ . '/Resources',
    ])
    ->withSets([
        Typo3LevelSetList::UP_TO_TYPO3_13,
    ]);
php
<?php
// fractor.php
declare(strict_types=1);

use a9f\Fractor\Configuration\FractorConfiguration;
use a9f\Typo3Fractor\Set\Typo3LevelSetList;

return FractorConfiguration::configure()
    ->withPaths([
        __DIR__ . '/Configuration',
        __DIR__ . '/Resources',
    ])
    ->withSets([
        Typo3LevelSetList::UP_TO_TYPO3_13,
    ]);

Run Fractor

执行Fractor

bash
undefined
bash
undefined

Dry run first

Dry run first

vendor/bin/fractor process --dry-run
vendor/bin/fractor process --dry-run

Review changes, then apply

Review changes, then apply

vendor/bin/fractor process
undefined
vendor/bin/fractor process
undefined

5. Fix Code Style

5. 修复代码风格

bash
undefined
bash
undefined

Run PHP-CS-Fixer

Run PHP-CS-Fixer

vendor/bin/php-cs-fixer fix
vendor/bin/php-cs-fixer fix

Verify no issues remain

Verify no issues remain

vendor/bin/php-cs-fixer fix --dry-run
undefined
vendor/bin/php-cs-fixer fix --dry-run
undefined

6. Run PHPStan

6. 运行PHPStan

bash
undefined
bash
undefined

Analyze codebase

Analyze codebase

vendor/bin/phpstan analyse
vendor/bin/phpstan analyse

Fix any reported issues

Fix any reported issues

Then re-run until clean

Then re-run until clean

undefined
undefined

7. Run Tests

7. 运行测试

bash
undefined
bash
undefined

Unit tests

Unit tests

vendor/bin/phpunit -c Tests/UnitTests.xml
vendor/bin/phpunit -c Tests/UnitTests.xml

Functional tests

Functional tests

vendor/bin/phpunit -c Tests/FunctionalTests.xml
vendor/bin/phpunit -c Tests/FunctionalTests.xml

Fix failing tests

Fix failing tests

undefined
undefined

8. Manual Testing

8. 手动测试

bash
undefined
bash
undefined

Test in target TYPO3 version

Test in target TYPO3 version

ddev composer require "typo3/cms-core:^14.0" --no-update ddev composer update ddev typo3 cache:flush
ddev composer require "typo3/cms-core:^14.0" --no-update ddev composer update ddev typo3 cache:flush

Test all extension functionality:

Test all extension functionality:

- Backend modules

- Backend modules

- Frontend plugins

- Frontend plugins

- CLI commands

- CLI commands

- Scheduler tasks

- Scheduler tasks

undefined
undefined

Common Migration Patterns

常见迁移模式

ViewFactory (Replaces StandaloneView)

ViewFactory(替代StandaloneView)

php
// ❌ OLD (deprecated in v13, removed in v14)
use TYPO3\CMS\Fluid\View\StandaloneView;

$view = GeneralUtility::makeInstance(StandaloneView::class);
$view->setTemplatePathAndFilename('...');

// ✅ NEW (v13/v14 compatible)
use TYPO3\CMS\Core\View\ViewFactoryInterface;
use TYPO3\CMS\Core\View\ViewFactoryData;

public function __construct(
    private readonly ViewFactoryInterface $viewFactory,
) {}

public function render(ServerRequestInterface $request): string
{
    $viewFactoryData = new ViewFactoryData(
        templateRootPaths: ['EXT:my_ext/Resources/Private/Templates'],
        request: $request,
    );
    $view = $this->viewFactory->create($viewFactoryData);
    $view->assign('data', $data);
    return $view->render('MyTemplate');
}
php
// ❌ OLD (deprecated in v13, removed in v14)
use TYPO3\CMS\Fluid\View\StandaloneView;

$view = GeneralUtility::makeInstance(StandaloneView::class);
$view->setTemplatePathAndFilename('...');

// ✅ NEW (v13/v14 compatible)
use TYPO3\CMS\Core\View\ViewFactoryInterface;
use TYPO3\CMS\Core\View\ViewFactoryData;

public function __construct(
    private readonly ViewFactoryInterface $viewFactory,
) {}

public function render(ServerRequestInterface $request): string
{
    $viewFactoryData = new ViewFactoryData(
        templateRootPaths: ['EXT:my_ext/Resources/Private/Templates'],
        request: $request,
    );
    $view = $this->viewFactory->create($viewFactoryData);
    $view->assign('data', $data);
    return $view->render('MyTemplate');
}

Controller ResponseInterface

Controller ResponseInterface

php
// ❌ OLD (v12 and earlier)
public function listAction(): void
{
    $this->view->assign('items', $items);
}

// ✅ NEW (v13+ required)
public function listAction(): ResponseInterface
{
    $this->view->assign('items', $items);
    return $this->htmlResponse();
}
php
// ❌ OLD (v12 and earlier)
public function listAction(): void
{
    $this->view->assign('items', $items);
}

// ✅ NEW (v13+ required)
public function listAction(): ResponseInterface
{
    $this->view->assign('items', $items);
    return $this->htmlResponse();
}

PSR-14 Events (Replace Hooks)

PSR-14 事件(替代钩子)

php
// ❌ OLD (hooks deprecated)
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['...']['hook'][] = MyHook::class;

// ✅ NEW (PSR-14 events)
// Configuration/Services.yaml
services:
  Vendor\MyExt\EventListener\MyListener:
    tags:
      - name: event.listener
        identifier: 'myext/my-listener'

// Or use PHP attribute
#[AsEventListener(identifier: 'myext/my-listener')]
final class MyListener
{
    public function __invoke(SomeEvent $event): void
    {
        // Handle event
    }
}
php
// ❌ OLD (hooks deprecated)
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['...']['hook'][] = MyHook::class;

// ✅ NEW (PSR-14 events)
// Configuration/Services.yaml
services:
  Vendor\MyExt\EventListener\MyListener:
    tags:
      - name: event.listener
        identifier: 'myext/my-listener'

// Or use PHP attribute
#[AsEventListener(identifier: 'myext/my-listener')]
final class MyListener
{
    public function __invoke(SomeEvent $event): void
    {
        // Handle event
    }
}

Static TCA (No Runtime Modifications)

静态TCA(禁止运行时修改)

php
// ❌ OLD (runtime TCA modification - forbidden in v14)
// ext_tables.php
$GLOBALS['TCA']['tt_content']['columns']['myfield'] = [...];

// ✅ NEW (static TCA files only)
// Configuration/TCA/Overrides/tt_content.php
$GLOBALS['TCA']['tt_content']['columns']['myfield'] = [...];
php
// ❌ OLD (runtime TCA modification - forbidden in v14)
// ext_tables.php
$GLOBALS['TCA']['tt_content']['columns']['myfield'] = [...];

// ✅ NEW (static TCA files only)
// Configuration/TCA/Overrides/tt_content.php
$GLOBALS['TCA']['tt_content']['columns']['myfield'] = [...];

Backend Module Registration

后端模块注册

php
// ❌ OLD (ext_tables.php registration)
ExtensionUtility::registerModule(...);

// ✅ NEW (Configuration/Backend/Modules.php)
return [
    'web_mymodule' => [
        'parent' => 'web',
        'access' => 'user,group',
        'iconIdentifier' => 'myext-module',
        'labels' => 'LLL:EXT:my_ext/Resources/Private/Language/locallang_mod.xlf',
        'extensionName' => 'MyExt',
        'controllerActions' => [
            MyController::class => ['index', 'list'],
        ],
    ],
];
php
// ❌ OLD (ext_tables.php registration)
ExtensionUtility::registerModule(...);

// ✅ NEW (Configuration/Backend/Modules.php)
return [
    'web_mymodule' => [
        'parent' => 'web',
        'access' => 'user,group',
        'iconIdentifier' => 'myext-module',
        'labels' => 'LLL:EXT:my_ext/Resources/Private/Language/locallang_mod.xlf',
        'extensionName' => 'MyExt',
        'controllerActions' => [
            MyController::class => ['index', 'list'],
        ],
    ],
];

API Changes Reference

API变更参考

TYPO3 v13 Breaking Changes

TYPO3 v13 破坏性变更

Removed/ChangedReplacement
StandaloneView
ViewFactoryInterface
ObjectManager
Constructor injection
TSFE->fe_user->user
Request attribute
Various hooksPSR-14 events
已移除/变更内容替代方案
StandaloneView
ViewFactoryInterface
ObjectManager
构造函数注入
TSFE->fe_user->user
请求属性
各类钩子PSR-14 事件

TYPO3 v14 Breaking Changes

TYPO3 v14 破坏性变更

Removed/ChangedReplacement
Runtime TCA changesStatic TCA only
Legacy backend modulesModules.php
$GLOBALS['TYPO3_DB']
QueryBuilder
已移除/变更内容替代方案
运行时TCA修改仅允许静态TCA文件
传统后端模块Modules.php 注册
$GLOBALS['TYPO3_DB']
QueryBuilder

Troubleshooting

故障排除

Rector Fails

Rector执行失败

bash
undefined
bash
undefined

Clear Rector cache

Clear Rector cache

rm -rf .rector_cache/
rm -rf .rector_cache/

Run with verbose output

Run with verbose output

vendor/bin/rector process --dry-run -vvv
vendor/bin/rector process --dry-run -vvv

Skip problematic rules

Skip problematic rules

Add to rector.php:

Add to rector.php:

->withSkip([ \Ssch\TYPO3Rector\SomeRule::class, ])
undefined
->withSkip([ \Ssch\TYPO3Rector\SomeRule::class, ])
undefined

PHPStan Errors

PHPStan报错

bash
undefined
bash
undefined

Generate baseline for existing issues

Generate baseline for existing issues

vendor/bin/phpstan analyse --generate-baseline
vendor/bin/phpstan analyse --generate-baseline

Add to phpstan.neon:

Add to phpstan.neon:

includes: - phpstan-baseline.neon
undefined
includes: - phpstan-baseline.neon
undefined

Extension Not Found

扩展未找到

bash
undefined
bash
undefined

Regenerate autoload

Regenerate autoload

ddev composer dump-autoload
ddev composer dump-autoload

Clear all caches

Clear all caches

ddev typo3 cache:flush rm -rf var/cache/*
ddev typo3 cache:flush rm -rf var/cache/*

Re-setup extension

Re-setup extension

ddev typo3 extension:setup
undefined
ddev typo3 extension:setup
undefined

Database Issues

数据库问题

bash
undefined
bash
undefined

Check for schema differences

Check for schema differences

ddev typo3 database:updateschema --verbose
ddev typo3 database:updateschema --verbose

Apply schema changes

Apply schema changes

ddev typo3 database:updateschema ".add,.change"
undefined
ddev typo3 database:updateschema ".add,.change"
undefined

Success Criteria

成功标准

Before considering upgrade complete:
  • rector --dry-run
    shows no changes
  • fractor --dry-run
    shows no changes
  • phpstan analyse
    passes
  • php-cs-fixer --dry-run
    passes
  • All unit tests pass
  • All functional tests pass
  • Manual testing in target version(s) complete
  • No deprecation warnings in logs
  • Extension works in TYPO3 v13
  • Extension works in TYPO3 v14
在确认升级完成前需满足:
  • rector --dry-run
    无变更提示
  • fractor --dry-run
    无变更提示
  • phpstan analyse
    执行通过
  • php-cs-fixer --dry-run
    执行通过
  • 所有单元测试通过
  • 所有功能测试通过
  • 目标版本中的手动测试已完成
  • 日志中无弃用警告
  • 扩展在TYPO3 v13中正常运行
  • 扩展在TYPO3 v14中正常运行

Resources

参考资源

Credits & Attribution

致谢与归属

Thanks to Netresearch DTT GmbH for their contributions to the TYPO3 community.
感谢Netresearch DTT GmbH为TYPO3社区做出的贡献。