Hooks

Hooks are basically places in the source code where a user function will be called for processing, if such has been configured. While there are conventions and best practises of how hooks should be implemented the hook concept itself does not prevent it from being used in any way.

Hooks are being phased-out and no new ones should be created. Dispatch a PSR-14 event instead.

Using hooks

The two lines of code below are an example of how a hook can be used for clear-cache post-processing. The objective of this could be to perform additional actions whenever the cache is cleared for a specific page:

EXT:site_package/ext_localconf.php
<?php

declare(strict_types=1);

use MyVendor\MyExtension\Hook\DataHandlerHook;

defined('TYPO3') or die();

$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'][] =
    DataHandlerHook::class . '->postProcessClearCache';
Copied!

This hook registers the class/method name to a hook inside of \TYPO3\CMS\Core\DataHandling\DataHandler. The hook calls the user function after the cache has been cleared. The user function will receive parameters which allows it to see what clear-cache action was performed and typically also an object reference to the parent object. Then the user function can take additional actions as needed.

The class has to follow the PSR-4 class name scheme to be available in autoloading.

If we take a look inside of \TYPO3\CMS\Core\DataHandling\DataHandler we find the hook to be activated like this:

\TYPO3\CMS\Core\DataHandling\DataHandler (excerpt)
<?php

namespace TYPO3\CMS\Core\DataHandling;

use TYPO3\CMS\Core\Utility\GeneralUtility;

class DataHandler
{
    protected function prepareCacheFlush($table, $uid, $pid)
    {
        // do something [...]
        // Call post processing function for clear-cache:
        $_params = ['table' => $table, 'uid' => $uid/*...*/];
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] ?? [] as $_funcRef) {
            GeneralUtility::callUserFunction($_funcRef, $_params, $this);
        }
    }
}
Copied!

This is how hooks are typically constructed. The main action happens in line 5 where the function \TYPO3\CMS\Core\Utility\GeneralUtility::callUserFunction() is called. The user function is called with two arguments, an array with variable parameters and the parent object.

In line 24 the content of the parameter array is prepared. This is of high interest to you because this is where you see what data is passed to you and what data might be passed by reference and thereby could be manipulated from your hook function.

Finally, notice how the array $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] is traversed and for each entry the value is expected to be a function reference which will be called. This allows many hooks to be called at once. The hooks can even rearrange the calling order if they dare.

The syntax of a function reference can be seen in the API documentation of \TYPO3\CMS\Core\Utility\GeneralUtility.

Creating hooks

There are two main methods of calling a user-defined function in TYPO3.

\TYPO3\CMS\Core\Utility\GeneralUtility::callUserFunction()
Takes a reference to a function in a PHP class reference as value and calls that function. The argument list is fixed to a parameter array and a parent object.
\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance()
Creates an object from a user-defined PHP class. The method to be called is defined by the implementation of the hook.

Here are some examples:

Using \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance()

Data submission to extensions:

EXT:my_extension/Classes/SomeClass.php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension;

use TYPO3\CMS\Core\Utility\GeneralUtility;

final class SomeClass
{
    public function doSomeThing(): void
    {
        // Hook for processing data submission to extensions
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['my_custom_hook']
                 ['checkDataSubmission'] ?? [] as $className) {
            $_procObj = GeneralUtility::makeInstance($className);
            $_procObj->checkDataSubmission($this);
        }
    }
}
Copied!

Using with \TYPO3\CMS\Core\Utility\GeneralUtility::callUserFunction()

Constructor post-processing:

EXT:my_extension/Classes/SomeClass.php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension;

use TYPO3\CMS\Core\Utility\GeneralUtility;

final class SomeClass
{
    public function doSomeThing(): void
    {
        // Call post-processing function for constructor:
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['Some-PostProc'])) {
            $_params = ['pObj' => &$this];
            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['Some-PostProc'] as $_funcRef) {
                GeneralUtility::callUserFunction($_funcRef, $_params, $this);
            }
        }
    }
}
Copied!

Hook configuration

Most hooks in the TYPO3 Core have been converted into PSR-14 events which are completely listed in the event list.

There is no complete index of the remaining hooks in the Core. The following naming scheme should be used:

$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']

Configuration space for third-party extensions.

This will contain all kinds of configuration options for specific extensions including possible hooks in them! What options are available to you will depend on a search in the documentation for that particular extension.

EXT:my_extension/ext_localconf.php
<?php

declare(strict_types=1);

defined('TYPO3') or die();

$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['<extension_key>']['<sub_key>'] = '<value>';
Copied!
<extension_key>
The unique extension key
<sub_key>
Whatever the script defines. Typically it identifies the context of the hook
<value>
It is up to the extension what the values mean, if they are mere configuration options or hooks or whatever and how deep the arrays go. Read the source code where the options are implemented to see. Or the documentation of the extension, if available.

$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']

Configuration space for Core extensions.

This array is created as an ad hoc space for creating hooks from any script. This will typically be used from the Core scripts of TYPO3 which do not have a natural identifier like extensions have their extension keys.

EXT:my_extension/ext_localconf.php
<?php

declare(strict_types=1);

defined('TYPO3') or die();

$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['<extension_key>']['<sub_key>'] = '<value>';
Copied!
<main_key>
The relative path of a script (for output scripts it should be the "script ID" as found in a comment in the HTML header)
<sub_key>
This is defined by the script. Typically it identifies the context of the hook.
<index>
Integer index typically. Can be a unique string, if you have a reason to use that. Normally it has no greater significance since the value of the key is not used. The hooks normally traverse over the array and uses only the value (function reference).
<function_reference>

A function reference using the syntax of \TYPO3\CMS\Core\Utility\GeneralUtility::callUserFunction() as a function or \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance() as a class name depending on implementation of the hook.

A namespace function has the format \Foo\Bar\MyClassName::class . '->myUserFunction'.

A namespace class should be used in the unquoted form, for example \Foo\Bar\MyClassName::class. The called function name is determined by the hook itself.

The above syntax is how a hook is typically defined but it might differ and it might not be a hook at all, but just configuration. Depends on implementation in any case.