---
title: "XCLASSes (extending classes)"
manual: "TYPO3 Explained"
version: "main"
permalink: "https://docs.typo3.org/permalink/t3coreapi:xclasses@main"
source: "ApiOverview/Xclasses/Index.rst"
rendered: "2026-09-19T06:56:03+00:00"
---

# XCLASSes (extending classes) {#xclasses}

## Introduction {#xclasses-intro}

XCLASSing is a mechanism in TYPO3 to extend classes or overwrite methods from the Core or extensions
with one's own code. This enables a developer to easily change a given functionality,
if other options like [events](https://docs.typo3.org/permalink/t3coreapi:eventdispatcher@main) or [hooks](https://docs.typo3.org/permalink/t3coreapi:hooks@main),
or the dependency injection mechanisms do not work or do not exist.

> [!WARNING]
> Using XCLASSes is risky: Your XCLASS may break if the underlying
> code is changed. Preferably use events or hooks to extend class functionality.
> For other limitations see [XClass limitations](https://docs.typo3.org/permalink/t3coreapi:xclasses-limitations@main)

If you need a hook or event that does not exist, feel free to submit
a feature request and - even better - a patch. Consult the
[TYPO3 Contribution Guide](https://docs.typo3.org/m/typo3/guide-contributionworkflow/main/en-us/Index.html#Start)
about how to do this.

## How does it work? {#xclasses-mechanism}

In general every class instance in the Core and in extensions that sticks to
the recommended [coding guidelines](https://docs.typo3.org/permalink/t3coreapi:cgl@main) is created with the API call
`TYPO3CMSCoreUtilityGeneralUtility::makeInstance()`.
This method takes care of singletons and also searches for existing XCLASSes.
If there is an XCLASS registered for the specific class that should be instantiated,
an instance of that XCLASS is returned instead of an instance of the original class.

## Limitations {#xclasses-limitations}

-   Using XCLASSes is risky: neither the Core, nor extensions authors
    can guarantee that XCLASSes will not break if the underlying code changes
    (for example during upgrades). Be aware that your XCLASS can easily break
    and has to be maintained and fixed if the underlying code changes.
    If possible, you should use a hook instead of an XCLASS.
-   XCLASSes do **not** work for static classes, static methods, abstract classes or final classes.
-   There can be **only one** XCLASS per base class, but an XCLASS can be XCLASSed again.
    Be aware that such a construct is even more risky and definitely not advisable.
-   A small number of Core classes are required very early during bootstrap
    before configuration and other things are loaded. XCLASSing those classes will fail if they are singletons
    or might have unexpected side-effects.

## Declaration {#xclasses-declaration}

The `$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects']` global array acts as a registry
of overloaded (XCLASSed) classes.

The syntax is as follows and is commonly located in an extension's
[`ext_localconf.php`](../../ExtensionArchitecture/FileStructure/ExtLocalconf.md#file-extension-ext-localconf-php) file:

**EXT:my_extension/ext_localconf.php**

```php
<?php

declare(strict_types=1);

use MyVendor\MyExtension\Xclass\NewRecordController as NewRecordControllerXclass;
use TYPO3\CMS\Backend\Controller\NewRecordController;

defined('TYPO3') or die();

$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][NewRecordController::class] = [
  'className' => NewRecordControllerXclass::class,
];

```

In this example, we declare that the `TYPO3CMSBackendControllerNewRecordController` class
will be overridden by the `T3docsExamplesXclassNewRecordController`
class, the latter being part of the [`t3docs/examples`](https://packagist.org/packages/t3docs/examples) extension.

When XCLASSing a class that does not use namespaces, use that class name
in the declaration.

## XCLASSing an Extbase controller action {#xclasses-extbase-actions}

Extbase controllers are resolved as services from the
[dependency injection container](https://docs.typo3.org/permalink/t3coreapi:dependencyinjection@main) and are never
instantiated directly. Two additional requirements therefore apply when
XCLASSing them.

**The XCLASS has to be registered as a service itself.**

The conventional `resource: '../Classes/*'` entry in the
[`Configuration/Services.yaml`](../../ExtensionArchitecture/FileStructure/Configuration/ServicesYaml.md#file-extension-configuration-services-yaml) of the extension providing the XCLASS is
sufficient. Without it the container does not know the replacement class, and
some action attributes will not work - both those from the original action and
any declared on the XCLASS itself.

**Attributes are not inherited in PHP.**

An overriding action method declares its own set of attributes; those of the
parent method are not merged in. Every attribute of the original action that
should remain in effect has to be repeated - `#[Authorize]` and
`#[RateLimit]` on the method, `#[Validate]` and
`#[IgnoreValidation]` on the corresponding parameters.

## Coding practices {#xclasses-coding}

The recommended way of writing an XCLASS is to **extend** the original class and
overwrite only the methods where a change is needed. This lowers the chances of the
XCLASS breaking after a code update.

> [!TIP]
> You are even safer if you can do your changes before or after the parent method
> and call the latter with `parent::`.

The example below extends the new record wizard screen. It first calls the original
method and then adds its own content:

**EXT:my_extension/Classes/Xclass/NewRecordController.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Xclass;

use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Controller\NewRecordController as CoreNewRecordController;

class NewRecordController extends CoreNewRecordController
{
  protected function renderNewRecordControls(
    ServerRequestInterface $request,
  ): void {
    parent::renderNewRecordControls($request);
    $languageDomain = 'my_extension:messages';
    $label = $GLOBALS['LANG']->translate('help', $languageDomain);
    $text = $GLOBALS['LANG']->label('make_choice', $languageDomain);
    $str = '<div><h2 class="uppercase" >' . htmlspecialchars($label)
        . '</h2>' . $text . '</div>';
    $this->code .= $str;
  }
}

```

The result can be seen here:

![](../../Images/ManualScreenshots/Examples/Xclasses/XclassNewElementWizard.png)

The object-oriented rules of PHP, such as rules about visibility, apply here.
As you are extending the original class you can overload or call methods
marked as public and protected but not private or static ones. Read more about
[visibility and inheritance at php.net](https://www.php.net/manual/en/language.oop5.visibility.php)

> [!NOTE]
> **See also**
>
> [How to use constructor dependency injection in a XCLASSed TYPO3 class](https://www.derhansen.de/2021/06/how-to-use-constructor-injection-with-typo3-xclass.html)
