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.