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.