Login / Status
developer.Resource
Home . Documentation . Document Library . Core Documentation
Sponsors
hosted by punkt.deTYPO3 and Open Source Magazine

3.4. Hooks

The concept of "hooks"

Hooks are basically places in the source code where a user function will be called for processing if a such has been configured. Hooks provide a way to extend functionality of TYPO3 and extensions easily and without blocking for others to do the same.

Hooks vs. XCLASS extensions

Hooks are the recommended way of extending TYPO3 compared to extending the PHP classes with a child class (see "XCLASS extensions"). It is so because only one extension of a PHP class can exist at a time while hooks may allow many different user processings to occur. On the other hand hooks have to be implemented in the core before you can use them while extending a PHP class via the XCLASS method allows you to extend anything spontaneously.

Proposing hooks

If you need to extend something which have no hook yet, then you should suggest implementing a hook. Normally that is rather easily done by the author of the source you want to extend.

How a hook looks

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

require_once(t3lib_extMgm::extPath('myext').'class.myext_cacheProc.php');

$TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'][]='myext_cacheProc->proc';

Line 1 includes a class which contains the user defined PHP code to be called by the hook.

Line 2 registers the class/method name from the included file with a hook inside of "t3lib_TCEmain". The hook will call the user function after the clear-cache command has been executed. 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.

If we take a look inside of t3lib_TCEmain we find the hook to be activated like this:

   1:     // Call post processing function for clear-cache:

   2: if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc']))    {

   3:     $_params = array('cacheCmd'=>$cacheCmd);

   4:     foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] as $_funcRef)    {

   5:         t3lib_div::callUserFunction($_funcRef,$_params,$this);

   6:     }

   7: }

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

In line 3 the contents 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 possibly be passed by reference and thereby possible to manipulate from your hook function.

Finally, notice how the array $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 the same place. The hooks can even rearrange the calling order if they dare.

The syntax of a function reference (or object reference if t3lib_div::getUserObj is used in the hook instead) can be seen in the API documentation of t3lib_div.

Hook configuration

There is no complete index of hooks in the core. But they are easy to search for and find. And typically it comes quite naturally since you will find the hooks in the code you want to extend - if they exists.

This index will list the main variable spaces for configuration of hooks. By the names of these you can easily scan the source code to find which hooks are available or might be interesting for you.

The index below also includes some variable spaces which does not only carry hook configuration but might be used for other purposes as well.

$TYPO3_CONF_VARS['EXTCONF']

Configuration space for 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.

$TYPO3_CONF_VARS['EXTCONF'][ extension_key ][ sub_key ] = value

  1. extension_key : The unique extension key

  2. sub_key : Whatever the script defines. Typically it identifies the context of the hook

  3. value : It is up to the extension what the values mean, if they are mere configuration 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.

Notice: $TYPO3_CONF_VARS["EXTCONF"] is the recommended place to put hook configuration that are available inside your extensions!

Here is an example of how the EXTCONF array is used inside an extension. Notice, this example is not a hook (sorry, couldn't find a better example) but it is based on the same principles. It is just an example of configuration of additional "root line fields" that can be used during indexing (line 8-12). It shows the versatility of the EXTCONF array:

   1: function getRootLineFields(&$fieldArr)    {

   2:     $rl = $this->rootLevel;

   3:

   4:     $fieldArr['rl0'] = intval($rl[0]['uid']);

   5:     $fieldArr['rl1'] = intval($rl[1]['uid']);

   6:     $fieldArr['rl2'] = intval($rl[2]['uid']);

   7:

   8:     if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['addRootLineFields']))    {

   9:         foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['addRootLineFields'] as $fieldName => $rootLineLevel)    {

  10:             $fieldArr[$fieldName] = intval($rl[$rootLineLevel]['uid']);

  11:         }

  12:     }

  13: }

$TYPO3_CONF_VARS['SC_OPTIONS']

Configuration space for core scripts.

This array is created as an adhoc 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.

$TYPO3_CONF_VARS['SC_OPTIONS'][ main_key ][ sub_key ][ index ] = function_reference

  1. 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 )

  2. sub_key : Whatever the script defines. Typically it identifies the context of the hook.

  3. index : Integer index typically. Can be 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)

  4. function_reference : A function reference using the syntax of t3lib_div::callUserFunction() or t3lib_div::getUserObj() depending on implementation of the hook.

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.

The following example shows a hook from tslib_fe. In this case the function t3lib_div::getUserObj() is used for the hook. The function_reference is referring to the class name only since the function returns an object instance of that class. The method name to call is predefined by the hook, in this case "sendFormmail_preProcessVariables()". This method allows to pass any number of variables along instead of the limited $params and $pObj variables from t3lib_div::callUserFunction().

   1:     // Hook for preprocessing of the content for formmails:

   2: if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['sendFormmail-PreProcClass']))    {

   3:     foreach($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['sendFormmail-PreProcClass'] as $_classRef)    {

   4:         $_procObj = &t3lib_div::getUserObj($_classRef);

   5:         $EMAIL_VARS = $_procObj->sendFormmail_preProcessVariables($EMAIL_VARS,$this);

   6:     }

   7: }

In this example we are looking at a special hook, namely the one for RTE transformations. Well, maybe this is not a "hook" in the normal sense, but the same principles are used. In this case the "index" key is defined to be the transformation key name, not a random integer since we do not iterate over the array as usual. In this case t3lib_div::getUserObj() is also used.

   1: if ($_classRef = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['transformation'][$cmd])    {

   2:     $_procObj = &t3lib_div::getUserObj($_classRef);

   3:     $_procObj->pObj = &$this;

   4:     $_procObj->transformationKey = $cmd;

   5:     $value = $_procObj->transform_db($value,$this);

   6: }

A classic hook also from tslib_fe. This is also based on t3lib_div::callUserFunction() and it passes a reference to $this along to the function via $_params. In the user defined function $_params['pObj']->content is meant to be manipulated in some way. The return value is insignificant - everything works by the reference to the parent object.

   1:     // Hook for post-processing of page content cached/non-cached:

   2: if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-all']))    {

   3:     $_params = array('pObj' => &$this);

   4:     foreach($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-all'] as $_funcRef)    {

   5:         t3lib_div::callUserFunction($_funcRef,$_params,$this);

   6:     }

   7: }

$TYPO3_CONF_VARS['TBE_MODULES_EXT']

Configuration space for backend modules.

Among these configuration options you might find entry points for hooks in the backend. This somehow overlaps the intention of "SC_OPTIONS" above but this array is an older invention and slightly outdated.

$TBE_MODULES_EXT[ backend_module_key ][ sub_key ] = value

  1. backend_module_key : The backend module key for which the configuration is used.

  2. sub_key : Whatever the backend module defines.

  3. value : Whatever the backend module defines.

The following example shows TBE_MODULES_EXT being used for adding items to the Context Sensitive Menus (Clickmenu) in the backend. The hook value is an array with a key pointing to a file reference to class file to include. Later each class is instantiated and a fixed method inside is called to do processing on the array of menu items. This kind of hook is non-standard in the way it is made.

   1:     // Setting internal array of classes for extending the clickmenu:

   2: $this->extClassArray = $GLOBALS['TBE_MODULES_EXT']['xMOD_alt_clickmenu']['extendCMclasses'];

   3:

   4:     // Traversing that array and setting files for inclusion:

   5: if (is_array($this->extClassArray))    {

   6:     foreach($this->extClassArray as $extClassConf)    {

   7:         if ($extClassConf['path'])    $this->include_once[]=$extClassConf['path'];

   8:     }

   9: }

The following code listings works in the same way. First, a list of class files to include is registered. Then in the second code listing the same array is traversed and each class is instantiated and a fixed function name is called for processing.

   1:     // Setting class files to include:

   2: if (is_array($TBE_MODULES_EXT['xMOD_db_new_content_el']['addElClasses']))    {

   3:     $this->include_once = array_merge($this->include_once,$TBE_MODULES_EXT['xMOD_db_new_content_el']['addElClasses']);

   4: }

   1:     // PLUG-INS:

   2: if (is_array($TBE_MODULES_EXT['xMOD_db_new_content_el']['addElClasses']))    {

   3:     reset($TBE_MODULES_EXT['xMOD_db_new_content_el']['addElClasses']);

   4:     while(list($class,$path)=each($TBE_MODULES_EXT['xMOD_db_new_content_el']['addElClasses']))    {

   5:         $modObj = t3lib_div::makeInstance($class);

   6:         $wizardItems = $modObj->proc($wizardItems);

   7:     }

   8: }

Creating hooks

You are encouraged to create hooks in your sources of extensions if they seem meaningful. Typically someone would request a hook somewhere. Before you implement it, consider if it is the right place to put it etc. On the one hand we want to have many hooks but not more than needed. Redundant hooks or hooks which are implemented in the wrong context is just confusing. So put a little thought into it first, but be generous.

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

  1. t3lib_div::callUserFunction() - The classic way. Takes a file/class/method reference as value and calls that function. The argument list is fixed to a parameter array and a parent object. So this is the limitation. The freedom is that the reference defines the function name to call. This method is mostly useful for small-scale hooks in the sources.

  2. t3lib_div::getUserObject() - Create an object from a user defined file/class. The method called in the object is fixed by the hook, so this is the non-flexible part. But it is cleaner in other ways, in particular that you can even call many methods in the object and you can pass an arbitrary argument list which makes the API more beautiful. You can also define the objects to be singletons, instantiated only once in the global scope.

Here follows some examples.

Hook made with t3lib_div::getUserObj()

    // Hook for preprocessing of the content for formmails:

if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['sendFormmail-PreProcClass']))    {

    foreach($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['sendFormmail-PreProcClass'] as $_classRef)    {

        $_procObj = &t3lib_div::getUserObj($_classRef);

        $EMAIL_VARS = $_procObj->sendFormmail_preProcessVariables($EMAIL_VARS,$this);

    }

}

Hook made with t3lib_div::callUserFunction()

    // Call post processing function for constructor:

if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['tslib_fe-PostProc']))    {

    $_params = array('pObj' => &$this);

    foreach($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['tslib_fe-PostProc'] as $_funcRef)    {

        t3lib_div::callUserFunction($_funcRef,$_params,$this);

    }

}