TYPO3: Change page title from plugin

TYPO3 v9 introduced the Page Title API that should be used now.
This blog post is obsolete.


At work I had a web site that showed records on a listing page, and offered more information for each records on a detail page. The task was now to change the page title on the detail page to the record's own title.

The naive solution is to simply set the page title in the central frontend output object:

$GLOBALS['TSFE']->page['title'] = 'foo';

But this does not work on uncached plugins.

TYPO3 page rendering process

To understand why, we need to look how TYPO3's cache works together with uncached plugins:

  1. Page <head> and <body> is generated and combined to a single string of HTML.

    Uncacheable plugins are not executed yet; a placeholder is added instead:

    some html..<!--INT_SCRIPT.abcdef-->more html

    Additional placeholders are added for additionalHeaderData and additionalFooterData.

  2. This generated HTML is stored in the page cache.
  3. In TypoScriptFrontendController::INTincScript(), TYPO3 iterates over all plugin placeholders, executes the respective plugin code and replaces the placeholder with the plugin output

    It also replaces additional*Data placeholders with their values from $GLOBALS['TSFE'].

  4. This final HTML is send to the user.

When the user requests a cached page, only the last two steps 3 and 4 are executed. Thus there is no way to change the page title generated with TypoScript.

Solutions

There are three possible solutions to set the page title from an uncached plugin:

  1. Disable normal page title and insert it with additionalHeaderData
  2. Replace already generated <title> tag during plugin processing
  3. Replace already generated <title> tag in contentPostProc-output hook

I suggest option 1.

Disable title, add it with additionalHeaderData

This is the option I recommend: It works with both cached and uncached plugins, and it keeps your code in one place.

At first, disable the creation of the normal title tag via config.noPageTitle for the pages that contain the plugin:

[globalVar = TSFE:id = 23|42]
config.noPageTitle = 2
[global]

In your plugin's logic, add the page title to TSFE's additionalHeaderData:

$GLOBALS['TSFE']->additionalHeaderData['myCustomUserIntTitle']
    = '<title>' . $this->getTitle($newTitle) . '</title>';

That's all needed.

Other people recommending this solution:

Content replacement during plugin processing

When a cached plugin is processed, the cached HTML code is available in $GLOBALS['TSFE']->content. You might be tempted to simply modify it during plugin processing..

This works for uncached plugins only. In cached plugins, $content is not filled and changing it does not do anything since it gets overwritten later.

$GLOBALS['TSFE']->content = preg_replace(
     '#<title>.*<\/title>#',
     '<title>' . htmlspecialchars($newTitle) . '</title>',
     $GLOBALS['TSFE']->content
);

Some people recommend this:

Title replacement in post processing hook

TYPO3 allows you to register a hook that gets executed just before the content is sent to the user. Just as in option #2 you can search and replace on the HTML:

ext_localconf.php
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-output']['robots'] = \Vnd\Class::class . '::contentPostProc';

And now you can preg_replace your new title into the HTML:

function contentPostProcOutput(&$params, TypoScriptFrontendController &$pObj)
{
    $pObj->content = preg_replace(
        '#<title>.*<\/title>#',
        '<title>' . htmlspecialchars($newTitle) . '</title>',
        $pObj->content
    );
}

This does work for cached and uncached plugins.

The downside is that your title creation and title insertion code are in separate places now (plugin rendering vs. postproc-hook).

Additional notes

vhs has a <v:page.header.title> view helper that only works for cached plugins.

Written by Christian Weiske.

Comments? Please send an e-mail.