---
title: "USER and USER_INT"
manual: "TypoScript Explained"
version: "main"
permalink: "https://docs.typo3.org/permalink/t3tsref:cobj-user-int@main"
source: "ContentObjects/UserAndUserInt/Index.rst"
rendered: "2026-09-19T06:55:14+00:00"
---

# USER and USER_INT {#cobj-user-int}

> [!IMPORTANT]
> <!-- TODO: no Markdown rendering for "versionchanged" -->
>
> PHP functions called via TypoScript must now use the PHP
> attribute #[AsAllowedCallable]
> (\TYPO3\CMS\Core\Attribute\AsAllowedCallable).

This calls either a PHP function or a method in a class. This is very
useful if you want to incorporate your own data processing or content.

Basically USER and USER_INT are user defined cObjects, because they
call a function or method, which you control!

If you call a method in a class (which is of course instantiated as an
object), the internal variable `$cObj` of that class is set with a
*reference* to the parent cObject. This offers you an API of functions,
which might be more or less relevant for you. See
`ContentObjectRenderer.php` in the TYPO3 source code; access to `typolink`
or `stdWrap` are only two of the gimmicks you get.

If you create this object as `USER_INT`, it will be rendered non-cached,
outside the main page-rendering.

-   [Properties](https://docs.typo3.org/permalink/t3tsref:properties@main)
-   [Examples](https://docs.typo3.org/permalink/t3tsref:examples@main)

## Properties {#cobj-user-properties}

### userFunc {#cobj-user-userfunc}

-   **userFunc**

    -   *Type:* [function name](https://docs.typo3.org/permalink/t3tsref:data-type-function-name@main)

    The name of the function, which should be called. If you specify the
    name with a '->' in it, then it is interpreted as a call to a method in
    a class.

    Three parameters are sent to the PHP function: First a `string $content` variable
    (which is empty for USER/USER_INT objects, but not when the user
    function is called from stdWrap functions .postUserFunc or
    .preUserFunc). The second parameter is an array (`$configuration`) with the properties
    of this cObject, if any. As third parameter, the current `ServerRequestInterface $request`
    is passed.

    PHP functions called via TypoScript **must** use the PHP
    attribute `#[AsAllowedCallable]`
    (`\TYPO3\CMS\Core\Attribute\AsAllowedCallable`).

> [!NOTE]
> The `$request` object should be used to access request related
> variables instead of directly accessing the superglobal variables like
> `$_GET` / `$_POST` / `$_SERVER`, or TYPO3’s API method
> `GeneralUtility::_GP()`.

### (properties you define) {#cobj-user-defined-properties}

-   **(properties you define)**

    -   *Type:* (the data type you want)

    Apart from the properties "userFunc" and "stdWrap", which are defined for
    all USER/USER_INT objects by default, you can add additional properties
    with any name and any data type to your USER/USER_INT object. These
    properties and their values will then be available in PHP; they will be
    passed to your function (in the second parameter). This allows you to
    process them further in any way you wish.

### stdWrap {#cobj-user-stdwrap}

-   **stdWrap**

    -   *Type:* [stdWrap](https://docs.typo3.org/permalink/t3tsref:stdwrap@main).

### cache {#cobj-user-cache}

-   **cache**

    -   *Type:* [cache](https://docs.typo3.org/permalink/t3tsref:cache@main)

    See [cache function description](https://docs.typo3.org/permalink/t3tsref:cache@main) for details.

## Examples {#cobj-user-int-examples}

> [!WARNING]
> **Attention**
>
> For the best result you should *always*, without exception, place your class files in
> an extension, define composer class loading for this extension and add this extension as
> a dependency of your project. Then, your classes will load without issues when you refer
> to them by their class name.

### Example 1 {#cobj-user-examples-example-1}

This example shows how to include your own PHP script and how to use it
from TypoScript. Use this TypoScript configuration:

**EXT:site_package/Configuration/Sets/Main/setup.typoscript**

```typoscript
page = PAGE
page.10 = USER_INT
page.10 {
  userFunc = MyVendor\SitePackage\UserFunctions\ExampleTime->printTime
}

```

The file `EXT:site_package/Classes/UserFunctions/ExampleTime.php` might
amongst other things contain:

**EXT:site_package/Classes/UserFunctions/ExampleTime.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\SitePackage\UserFunctions;

use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Attribute\AsAllowedCallable;

final class ExampleTime
{
  /**
   * Output the current time in red letters
   *
   * @param string Empty string (no content to process)
   * @param array TypoScript configuration
   * @param ServerRequestInterface $request
   * @return string HTML output, showing the current server time.
   */
  #[AsAllowedCallable]
  public function printTime(string $content, array $conf, ServerRequestInterface $request): string
  {
    return '<p style="color: red;">Dynamic time: ' . date('H:i:s') . '</p><br />';
  }
}

```

Here `page.10` will give back what the PHP function `printTime()`
returned. Since we did not use a `USER` object, but a
`USER_INT` object, this function is executed on every page hit.
Thus, in this example, the current time is displayed in red letters each time.

The method `printTime()` uses the PHP attribute
`#[AsAllowedCallable]` so that TypoScript is allowed to call is as a
user function.

### Example 2 {#cobj-user-examples-example-2}

Now let us have a look at another example:

We want to display all content element headers of a page in reversed
order. For this we use the following TypoScript:

**EXT:site_package/Configuration/Sets/Main/setup.typoscript**

```typoscript
page = PAGE
page.typeNum = 0

page.30 = USER
page.30 {
  userFunc = MyVendor\SitePackage\UserFunctions\ExampleListRecords->listContentRecordsOnPage
  # reverseOrder is a boolean variable (see PHP code below)
  reverseOrder = 1
}

```

The file `EXT:site_package/Classes/UserFunctions/ExampleListRecords.php`
may contain amongst other things:

**EXT:site_package/Classes/UserFunctions/ExampleListRecords.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\SitePackage\UserFunctions;

use Psr\Http\Message\ServerRequestInterface;
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
use TYPO3\CMS\Core\Attribute\AsAllowedCallable;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;

/**
 * Example of a method in a PHP class to be called from TypoScript
 *
 * The class is defined as public as we use dependency injection in
 * this example. If you do not need dependency injection, the
 * "Autoconfigure" attribute should be omitted!
 */
#[Autoconfigure(public: true)]
final class ExampleListRecords
{
  public function __construct(
    private readonly ConnectionPool $connectionPool,
  ) {}

  /**
   * Reference to the parent (calling) cObject set from TypoScript
   */
  private ContentObjectRenderer $cObj;

  public function setContentObjectRenderer(ContentObjectRenderer $cObj): void
  {
    $this->cObj = $cObj;
  }

  /**
   * List the headers of the content elements on the page
   *
   * @param  string Empty string (no content to process)
   * @param  array  TypoScript configuration
   * @return string HTML output, showing content elements (in reverse order, if configured)
   */
  #[AsAllowedCallable]
  public function listContentRecordsOnPage(string $content, array $conf, ServerRequestInterface $request): string
  {
    $connection = $this->connectionPool->getConnectionForTable('tt_content');
    $result = $connection->select(
      ['header'],
      'tt_content',
      ['pid' => $request->getAttribute('frontend.page.information')->getId()],
      [],
      ['sorting' => $conf['reverseOrder'] ? 'DESC' : 'ASC'],
    );
    $output = [];
    while ($row = $result->fetchAssociative()) {
      $output[] = $row['header'];
    }
    return implode('<br>', $output);
  }
}

```

Since we need an instance of the
`ContentObjectRenderer` class, we
are using the `setContentObjectRenderer()` method to get it and store it in
the `cObj` class property for later use.

`page.30` will give back what the function `listContentRecordsOnPage()` of
the class YourClass returned. This example returns some debug output
at the beginning and then the headers of the content elements on the
page in reversed order. Note how we defined the property
"reverseOrder" for this `USER` object and how we used it in the PHP code.

The method `listContentRecordsOnPage()` uses the PHP attribute
`#[AsAllowedCallable]` so that TypoScript is allowed to call is as a
user function.

### Example 3 {#cobj-user-examples-example-3}

Another example can be found in the documentation of the stdWrap
property [](https://docs.typo3.org/permalink/t3tsref:stdwrap-postuserfunc@main) There you can also see how to work with
`$cObj`, the reference to the parent (calling) cObject.

### Example 4 {#cobj-user-examples-example-4}

PHP has a function `gethostname()` to "get the standard host name for
the local machine". You can make it available like this:

**EXT:site_package/Configuration/Sets/Main/setup.typoscript**

```typoscript
page.20 = USER_INT
page.20 {
  userFunc = MyVendor\SitePackage\UserFunctions\Hostname->getHostname
}

```

Contents of `EXT:site_package/Classes/UserFunctions/Hostname.php`:

**EXT:site_package/Classes/UserFunctions/Hostname.php**

```php
<?php

declare(strict_types=1);

namespace Vendor\SitePackage\UserFunctions;

use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Attribute\AsAllowedCallable;

final class Hostname
{
  /**
   * Return standard host name for the local machine
   *
   * @param  string Empty string (no content to process)
   * @param  array  TypoScript configuration
   * @param  ServerRequestInterface The current PSR-7 request object
   * @return string HTML result
   */
  #[AsAllowedCallable]
  public function getHostname(string $content, array $conf, ServerRequestInterface $request): string
  {
    return gethostname() ?: '';
  }
}

```

The method `getHostname()` uses the PHP attribute
`#[AsAllowedCallable]` so that TypoScript is allowed to call is as a
user function.

### Example 5: Integrating a custom (non-Extbase) plugin via USER {#cobj-user-custom-plugin}

This example exposes custom rendering logic via `USER`
and makes it available in a Fluid template. This is useful for
**non-Extbase plugins** or logic that should not be registered as
a traditional Extbase plugin.

**Including a custom extension via TypoScript**

```typoscript
lib.myExtensionPlugin = USER
lib.myExtensionPlugin {
  userFunc = MyVendor\MyExtension\UserFunctions\MyClass->run
  vendorName = MyVendor
  extensionName = MyExtension
  pluginName = MyPlugin
  view =< plugin.tx_myextension.view
  view.partialRootPaths.10 = fileadmin/Resources/Private/Partials/
  view.templateRootPaths.10 = fileadmin/Resources/Private/Templates/
  settings =< plugin.tx_myextension.settings
}

```

This creates the TypoScript object `lib.myExtensionPlugin`.
Render it in Fluid with:

**Rendering the USER object in a Fluid template**

```html
<f:cObject typoscriptObjectPath="lib.myExtensionPlugin" />
```

The PHP method `run()` **must** be marked with
`#[AsAllowedCallable]`
(`\TYPO3\CMS\Core\Attribute\AsAllowedCallable`) to be callable
via TypoScript:

**EXT:my_extension/Classes/UserFunctions/MyClass.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\UserFunctions;

use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Attribute\AsAllowedCallable;

class MyClass
{
  #[AsAllowedCallable]
  public function run(string $content, array $configuration, ServerRequestInterface $request): string
  {
    // Custom rendering logic
    return 'Something';
  }
}

```

### Example 6: Converting a custom (non-Extbase) `USER` plugin dynamically into a `USER_INT` {#cobj-user-convert-userint-plugin}

An extension plugin can be defined as `USER` or `USER_INT` content object
(cObject). Whether a plugin's output is cacheable sometimes only becomes clear while it is
rendering - for example, once it decides to show personal or otherwise uncacheable content. In
that case, a plugin registered as `USER` needs to transition to `USER_INT`
dynamically. Calling `convertToUserIntObject()` marks the current object as
`USER_INT`; TYPO3 Core then substitutes it with a freshly rendered, non-cached
version of the same plugin in a later rendering pass, so the rest of the page can still be
cached.

**Configuration/Sets/Main/setup.typoscript**

```typoscript
# Define the custom content element / plugin as a standard cached USER object
tt_content.my_custom_plugin = USER
tt_content.my_custom_plugin {
  # Route the request to our PSR-11 compliant container service class and method
  userFunc = MyVendor\MyExtension\UserFunc\PluginRenderer->renderPlugin

  # Personalized output (for example a live search) must never be cached
  settings {
    showLiveSearch = 1
  }
}

```

This PHP class performs the dynamic transition from `USER` to `USER_INT`.

**Classes/UserFunc/PluginRenderer.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\UserFunc;

use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Attribute\AsAllowedCallable;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;

final class PluginRenderer
{
  /**
   * @param string $content Empty string from the TypoScript pipeline
   * @param array $conf TypoScript configuration array passed to this object
   * @param ServerRequestInterface $request The PSR-7 server request object
   */
  #[AsAllowedCallable]
  public function renderPlugin(string $content, array $conf, ServerRequestInterface $request): string
  {
    /** @var ContentObjectRenderer $cObj */
    $cObj = $request->getAttribute('currentContentObject');
    $showLiveSearch = (bool)($conf['settings.']['showLiveSearch'] ?? false);

    if (!$showLiveSearch) {
      return '<div>Standard list view (cached)</div>';
    }

    // Live search results are personal and must never be cached: promote this
    // cObject to USER_INT, unless this call is already the non-cached re-render.
    if ($cObj->getUserObjectType() === ContentObjectRenderer::OBJECTTYPE_USER) {
      $cObj->convertToUserIntObject();
    }

    return '<div>Live search result</div>';
  }
}

```
