Upgrading from older Extbase versions 

This page lists breaking changes and migration steps for upgrading an Extbase extension to a newer TYPO3 version. Each entry details what has changed, which version introduced the change, and what to do.

Annotations replaced by PHP attributes (TYPO3 v12 / required from v14) 

Changed in version 14.0

DocBlock annotation support was fully removed. Extbase ignores annotations silently — no error is thrown.

Native PHP attributes (introduced in PHP 8.0) replace the Doctrine-based DocBlock annotation syntax. Extbase has supported PHP attributes since TYPO3 v12. In TYPO3 v14 the annotation syntax was removed entirely.

Old annotation (removed in v14) New PHP attribute
@Extbase\ORM\Lazy #[Lazy]
@Extbase\ORM\Cascade("remove") #[Cascade('remove')]
@Extbase\ORM\Transient #[Transient]
@Extbase\Validate(...) #[Validate(...)]
@Extbase\IgnoreValidation #[IgnoreValidation]
EXT:my_extension/Classes/Domain/Model/Entity.php
 use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
+use TYPO3\CMS\Extbase\Attribute\ORM\Lazy;
 use TYPO3\CMS\Extbase\Persistence\ObjectStorage;

 class Entity extends AbstractEntity
 {
-    /**
-     * @var ObjectStorage<ChildEntity>
-     * @TYPO3\CMS\Extbase\Annotation\ORM\Lazy
-     */
+    #[Lazy]
     protected ObjectStorage $property;
 }
Copied!

Attribute namespace moved from Annotation to Attribute (TYPO3 v14) 

Changed in version 14.0

The namespace \TYPO3\CMS\Extbase\Annotation is deprecated. Class aliases remain available in v14 but will be removed in v15. Update your use statements to \TYPO3\CMS\Extbase\Attribute when dropping TYPO3 v13 support.

All Extbase attributes have been moved from \TYPO3\CMS\Extbase\Annotation to \TYPO3\CMS\Extbase\Attribute.

Attribute array syntax deprecated (TYPO3 v14, removed in v15) 

Changed in version 14.0

Passing a configuration array as the first argument to Extbase attributes is deprecated (Deprecation #97559). The array-based syntax still works in v14 but will be removed in v15.

Old array syntax (deprecated in v14) New named-argument syntax
#[Cascade(['value' => 'remove'])] #[Cascade('remove')]
#[Validate(['validator' => 'NotEmpty'])] #[Validate('NotEmpty')]

#[FileUpload] array syntax replaced by named arguments (TYPO3 v14) 

Changed in version 14.0

The array-based configuration syntax for #[FileUpload] is deprecated and will be removed in TYPO3 v15. Replace the array with named arguments.

In TYPO3 v13, #[FileUpload] accepted a single configuration array. In v14 the attribute requires named arguments instead:

EXT:my_extension/Classes/Domain/Model/Conference.php
-#[FileUpload([
-    'validation' => [
-        'required' => true,
-        'maxFiles' => 1,
-        'fileSize' => ['minimum' => '10K', 'maximum' => '2M'],
-        'mimeType' => ['allowedMimeTypes' => ['image/jpeg', 'image/png']],
-        'fileExtension' => ['allowedFileExtensions' => ['jpg', 'jpeg', 'png']],
-    ],
-    'uploadFolder' => '1:/user_upload/conference_logos/',
-])]
+#[FileUpload(
+    validation: [
+        'required' => true,
+        'maxFiles' => 1,
+        'fileSize' => ['minimum' => '10K', 'maximum' => '2M'],
+        'mimeType' => ['allowedMimeTypes' => ['image/jpeg', 'image/png']],
+        'fileExtension' => ['allowedFileExtensions' => ['jpg', 'jpeg', 'png']],
+    ],
+    uploadFolder: '1:/user_upload/conference_logos/',
+)]
 protected ?FileReference $logo = null;
Copied!

#[Validate] and #[IgnoreValidation] moved to parameter level (TYPO3 v14, removed in v15) 

Changed in version 14.0

Placing #[Validate] and #[IgnoreValidation] on the action method with a named $param / $argumentName property is deprecated and will be removed in TYPO3 v15.

Before TYPO3 v14, PHP attributes could only be attached to methods, so both attributes required a named property to identify which parameter they targeted. TYPO3 v14 introduced support for placing attributes directly on the parameter, meaning you can now position the attribute on the parameter itself:

EXT:my_extension/Classes/Controller/ConferenceController.php
-#[Validate(param: 'conference', validator: 'NotEmpty')]
-#[IgnoreValidation(argumentName: 'conference')]
 public function editAction(
+    #[Validate(validator: 'NotEmpty')]
+    #[IgnoreValidation]
     Conference $conference,
 ): ResponseInterface {
 }
Copied!

Magic findBy(), findOneBy(), countBy*() methods removed (TYPO3 v14) 

Changed in version 14.0

Magic property-name methods were deprecated in TYPO3 v12.3 and removed in v14. Replace them with the explicit array-based signatures.

Old (removed in v14) New
findByTitle($value) findBy(['title' => $value])
findOneByTitle($value) findOneBy(['title' => $value])
countByTitle($value) count(['title' => $value])

findByUid() and findByIdentifier() are not affected and remain available.

StandaloneView removed (TYPO3 v14) 

Changed in version 14.0

\TYPO3\CMS\Fluid\View\StandaloneView was deprecated in TYPO3 v13 and removed in v14. The replacement is ViewFactoryInterface together with ViewFactoryData .

StandaloneView was the historical way to render a Fluid template outside of a controller, for example, inside a service class, a scheduler task, or an email renderer. It is no longer available.

The new way injects ViewFactoryInterface and creates a view by passing a ViewFactoryData value object:

EXT:my_extension/Classes/Service/MailService.php
use TYPO3\CMS\Core\View\ViewFactoryData;
use TYPO3\CMS\Core\View\ViewFactoryInterface;

readonly class MailService
{
    public function __construct(
        protected ViewFactoryInterface $viewFactory,
    ) {}

    public function renderTemplate(ServerRequestInterface $request): string
    {
        $view = $this->viewFactory->create(new ViewFactoryData(
            templateRootPaths: ['EXT:my_extension/Resources/Private/Templates/'],
            partialRootPaths: ['EXT:my_extension/Resources/Private/Partials/'],
            layoutRootPaths: ['EXT:my_extension/Resources/Private/Layouts/'],
            request: $request,
        ));
        $view->assign('data', $this->loadData());
        return $view->render('Mail/Notification');
    }
}
Copied!

Pass the current ServerRequestInterface wherever possible. The view factory uses it for request-aware rendering (language, base URI, etc.).

list_type plugin removed; fifth parameter of configurePlugin() restricted (TYPO3 v14) 

Changed in version 14.0

The list_type / "General Plugin" content element was removed. All plugins must be registered as dedicated CType content elements. The fifth parameter $pluginType of ExtensionUtility::configurePlugin() now only accepts 'CType' (or being omitted); any other value throws an \InvalidArgumentException (Important #105538).

Older extensions could pass ExtensionUtility::PLUGIN_TYPE_PLUGIN_LIST ('list_type') as the fifth argument, or register their plugin as a list_type TCA entry. Both approaches no longer work in TYPO3 v14.

What to do:

  • Remove the fifth argument from configurePlugin() or pass ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT.
  • Provide an upgrade wizard for your extension that extends \TYPO3\CMS\Install\Updates\AbstractListTypeToCTypeUpdate to migrate existing content records from list_type to the new CType. TYPO3 does not ship a wizard for extension-specific plugins. Each extension must provide its own (see Deprecation #105076 for a reference implementation).

Translation domain syntax as shorter alternative to LLL:EXT: (TYPO3 v14) 

New in version 14.0

A shorter domain-based syntax for label references was introduced as an alternative to the legacy LLL:EXT: file path syntax (Feature #93334).

Legacy syntax remains fully supported and is not deprecated. Both forms resolve to the same translation entries and can be used interchangeably:

Legacy syntax Domain syntax (v14+)
LLL:EXT:my_ext/Resources/Private/Language/locallang.xlf:key my_ext.messages:key
LLL:EXT:my_ext/Resources/Private/Language/locallang_db.xlf:key my_ext.db:key
LLL:EXT:my_ext/Resources/Private/Language/locallang_val.xlf:key my_ext.val:key
LLL:EXT:my_ext/Resources/Private/Language/Form/locallang.xlf:key my_ext.form.messages:key

The domain syntax follows the pattern extension_key.resource:label_key. The resource name is derived from the file name:

  • locallang.xlf maps to the fixed resource name messages.
  • locallang_suffix.xlf strips the locallang_ prefix, leaving suffix — so locallang_db.xlf becomes db.
  • Subdirectories below Resources/Private/Language/ are prepended as dot-separated parts — Form/locallang.xlf becomes form.messages.

Check Extbase feature toggle defaults after upgrading (TYPO3 v14) 

Changed in version 14.0

The extbase.consistentDateTimeHandling feature toggle now defaults to true for new installations (Important #106467).

Feature toggles behave differently on upgrade than fresh installs: an existing instance keeps whatever value it already had, while a new installation will get the new default. A toggle whose default changed between versions will therefore retain the old behaviour after an upgrade unless you set it explicitly. After upgrading to TYPO3 v14 check the values below — the defaults may not match what your code now expects.

extbase.consistentDateTimeHandling

Introduced in TYPO3 v13.4 and set to false by default; new TYPO3 v14 installations default to true. (TYPO3 v12 did not have this toggle.)

An instance upgraded from v12 or v13 will keep the false setting and will continue to use the old \DateTime mapping. Certain functionality is missing with the false setting: correct timezone-offset handling for native datetime fields, named timezones instead of fixed offsets (no daylight-saving shifts), correct interpretation of integer-based time fields, and support for 00:00:00 as a valid time in nullable fields. Many extensions contain timezone workarounds that exist only to rectify the old behaviour; enabling the toggle lets you remove them. Enable it once you have reviewed your \DateTime handling:

config/system/settings.php
$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['extbase.consistentDateTimeHandling'] = true;
Copied!
extbase.enableHistoryTracking
Added in TYPO3 v14.2 and set to false by default, so you won't get an upgrade-preservation surprise, but it is worth knowing that it exists. The global feature toggle enables history tracking for all Extbase domain model tables; individual tables can then opt out via TCA with 'ctrl' => ['extbase' => ['enableHistoryTracking' => false]]. Note the GDPR implication: full data snapshots are written to sys_history, so disable it for tables holding sensitive data and prune regularly. See Feature #107289.