Book Image

TYPO3 Extension Development

Book Image

TYPO3 Extension Development

Overview of this book

Table of Contents (13 chapters)

Common TYPO3 API


As explained earlier, all TYPO3 API functions can be divided into three groups. The Common API group contains classes that are common in the other two groups, FE and BE API. These classes are essential for extension developers because they are either "most used" or "most must-be-used". We will examine these classes in the later sections. Since this chapter is called "About TYPO3 API", its purpose is to provide an overview, and not a fully detailed description. You can use this chapter as a guideline and learn more by looking at the source code of the discussed classes.

Database API

Database API is the most independent class of all the TYPO3 API classes. It contains functions to manipulate data in the MySQL database. The class name is t3lib_DB. There is no TYPO3-specific functionality, only a set of convenient methods to access the database. The instance of this class is available, both in FE and BE, as $GLOBALS['TYPO3_DB'].

All core classes and all extensions must work with the TYPO3 database by using t3lib_DB. This ensures that the database is accessed correctly. Using MySQL PHP functions directly is strongly discouraged and may, in certain cases, interfere with TYPO3 database access, or even break the TYPO3 database.

Besides convenient methods, t3lib_DB has another purpose. Since this is an API class, it can be XCLASSed to provide access to other databases transparent to the rest of TYPO3. This is what the extension DBAL does.

Here is an example of how the TYPO3 database can be accessed using t3lib_DB:

// Select number of non-deleted pages
$res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
'COUNT(*) AS t', 'pages',
'deleted=0');
if ($res !== false) {
// Fetch array with data
$row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
if ($row !== false) {
$numberOfDeletedPages = $row['t'];
}
// Free resultset (or memory leak will happen)
$GLOBALS['TYPO3_DB']->sql_free_result($res);
}

As you can see, these functions look very similar to MySQL PHP functions. This similarity is not accidental. The default database for TYPO3 is MySQL, and the database API is modeled using MySQL PHP functions.

Generally, all functions can be divided into several groups.

The first group consists of functions that generate various SQL queries. Functions accept certain parameters (such as field names, table name, SQL WHERE condition, and so on) and return a properly constructed SQL query. This is good for extensions that need compatibility with different database types. However, it should be noted that these functions cannot always be used, especially if the query is complex, such as with SQL JOIN constructs, and so on. Functions in this group are:

  • function INSERTquery($table,array $fields_values, $no_quote_fields=FALSE)

  • function UPDATEquery($table,$where,array $fields_values, $no_quote_fields=FALSE)

  • function DELETEquery($table,$where)

  • function SELECTquery($select_fields,$from_table,$where_clause,$groupBy='',$orderBy='',$limit='')

The next group is similar to the first group. It consists of functions that build and execute queries. Most functions in this group are built around functions belonging to the first group. Here you may ask a question: why does the first group exist then? The answer is simple. Sometimes, you want to get a query first, log it somewhere, and then execute it. In that case, you may want to use functions from the first group.

Here is a list of functions from the second group:

  • function exec_INSERTquery($table,array $fields_values, $no_quote_fields=FALSE)

  • function exec_UPDATEquery($table,$where,array $fields_values, $no_quote_fields=FALSE)

  • function exec_DELETEquery($table,$where)

  • function exec_SELECTquery($select_fields,$from_table,$where_clause,$groupBy='',$orderBy='',$limit='')

  • function exec_SELECT_mm_query($select,$local_table, $mm_table,$foreign_table,$whereClause='',$groupBy='', $orderBy='',$limit='')

  • function exec_SELECT_queryArray($queryParts)

  • function exec_SELECTgetRows($select_fields,$from_table,$where_clause,$groupBy='',$orderBy='',$limit='',$uidIndexField='')

The last three functions may raise questions. Two of them are rarely used. The exec_SELECT_mm_query function is a TYPO3 way to execute a query on tables with many-to-many relations. The exec_SELECT_queryArray function executes a SELECT SQL query, taking parameters from the passed array. This function is rarely used. The last one will perform a SELECT SQL query, fetch rows, and return them in an array. This function should be used with caution; if the result is big, it may exhaust PHP memory easily. But for some results, it is the most convenient way to fetch data from the database.

The next group includes functions to fetch the result, get the number of rows, and so on. They are identical to the corresponding MySQL PHP functions. They are so trivial that we will not discuss them much, only list them here for the sake of completeness:

  • function sql_query($query)

  • function sql_error()

  • function sql_num_rows($res)

  • function sql_fetch_assoc($res)

  • function sql_fetch_row($res)

  • function sql_free_result($res)

  • function sql_insert_id()

  • function sql_affected_rows()

  • function sql_data_seek($res,$seek)

  • function sql_field_type($res,$pointer)

One important note for extension authors: do not forget to call the sql_free_result() function! Though the PHP manual says that MySQL resources are automatically released when the script terminates, in practice, this does not always happen (especially, if persistent connections are enabled). Moreover, if DBAL is enabled, automatic freeing of resources may not always work. It is always better to clean them up in the code.

The last group of functions are the utility functions. While we are looking at it as the last group, functions in this group are used (or must be used) all the time. This group includes functions to escape database parameters and to do query manipulation. Here is a list:

  • function fullQuoteStr($str, $table)

  • function fullQuoteArray($arr, $table, $noQuote=FALSE)

  • function quoteStr($str, $table)

  • function escapeStrForLike($str, $table)

  • function cleanIntArray($arr)

  • function cleanIntList($list)

  • function stripOrderBy($str)

  • function stripGroupBy($str)

  • function splitGroupOrderLimit($str)

  • function listQuery($field, $value, $table)

  • function listQuery($searchWords, $fields, $table)

The fullQuoteStr() function is possibly the most used (or again—the most "must-be-used") function in this group. It escapes all special characters in the string and additionally wraps the string in quotes in a manner compatible with the current database. Therefore, extension developers should use this function for both escaping and quoting parameters, and not use any "home-made" solution. This is a good security-related function. Other functions do more or less the same but in more specialized way. The "strip" and "split" functions are specific for some TYPO3 core calls (but can be called by extensions too).

Now, we are ready to see more complex examples of database API functions. Notice that this example is focused on the database API only. You should not use this code in the extension because it lacks certain calls to other APIs, is not effective from the point of view of database performance, and uses echo (TYPO3 uses other ways of showing generated content). But this example gives a good idea of how to use the database API.

/**
* Retrieves all news written by the given author
*
* @param string $authorName Author name
* @return array A list of news items for author
*/
function findNewsByAuthor($authorName) {
$res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,title', 'tt_news', 'author=' . $GLOBALS['TYPO3_DB']->fullQuoteStr( $authorName, 'tt_news'));
$news = array();
while (false !== ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
$news[$row['uid']] = $row['title']
}
$GLOBALS['TYPO3_DB']->sql_free_result($res);
return $news;
}
/**
* Marks all news written by the given author as deleted
*
* @param string $authorName
* @return void
*/
function deleteNewsForAuthor($author) {
$news = findNewsForAuthor($author);
$fields = array('deleted' => 1);
foreach ($news as $uid => $title) {
echo 'Deleted news item with id=' . $uid . ' (' . $title . ')' . chr(10);
$GLOBALS['TYPO3_DB']->UPDATEquery('tt_news', 'uid=' . $GLOBALS['TYPO3_DB']->fullQuoteStr(
$uid, 'tt_news'), $fields);
if ($GLOBALS['TYPO3_DB']->sql_affected_rows() == 0) {
echo 'The above news item was already deleted';
}
}
}

Extension Management

Extension management is a class with functions that provides various levels of information about extensions. This class is not to be confused with TYPO3 Extension Manager.

The Extension management class is named t3lib_extMgm. The functions that developers are likely to use are:

  • function isLoaded($key,$exitOnError=0)

    Checks if an extension is loaded. Useful if the extension uses functionality of another extension.

  • function extPath($key,$script='')

    Returns the absolute path to the script $script in the extension identified by $key. The $script variable contains a path relative to the extension directory. This can be used in the require_once PHP function.

  • function extRelPath($key)

    Returns a path to the extension directory relative to the /typo3 directory. This can be used for <a>, <img>, <link>, or <script> tags to include the extension's files in Backend scripts.

  • function siteRelPath($key)

    Returns a path to the extension directory relative to the site root. It is used for the same purpose as extRelPath, but in the FE.

Here are some code examples:

$extkey = 'templavoila';
require_once(t3lib_extMgm::extPath($extkey,
'pi1/class.tx_templavoila_pi1.php'));
// From Backend:
$link = '<a href="' . t3lib_extMgm::extRelPath($extkey) .
'mod1/index.php">Reload</a>';
// From Frontend
$link = '<img src="' . t3lib_extMgm::siteRelPath($extkey) .
'res/image.gif" />';

There are also other functions to use form extensions, but we will discuss them in detail when we meet them because they have very specific usage.

Helper Functions (t3lib_div)

There is a class in TYPO3 that contains the largest collection of various public functions (138 at the time of writing). This class is probably the most used one in TYPO3. The class name is t3lib_div. It is located in t3lib/class.t3lib_div.php.

All functions in this class are divided into several groups. Most popular functions will be described in the forthcoming sections. They all need to be called statically (using t3lib_div::funcName() syntax). The t3lib_div function is always included and available in any extension. There is no need to include this file in your own scripts.

If you think you need a generic function, it is good to check if t3lib_div already has it, before writing your own.

GET/POST Functions

The Get and Post functions should be used in TYPO3 extensions to obtain values of request variables. While PHP contains variables such as $_GET and $_POST that can be used in scripts, TYPO3 coding guidelines require one to use TYPO3 functions to obtain values of request parameters. TYPO3 runs on many different web servers, and some of them have different ways of passing parameters to PHP scripts. Using TYPO3 functions ensures that the script always gets the parameter, regardless of the web server. The TYPO3 core team will fix the core if a new platform is found causing troubles with script parameters. This way extensions become independent of the differences in web server implementation. Extension authors can concentrate on their code instead of making workarounds for web servers.

The following functions are defined:

  • function _GP($var)

    This function retrieves the value from POST and, if not found, from GET request parameters.

  • function _GET($var=NULL)

    Retrieves value from GET request parameter.

  • function _POST($var=NULL)

    Retrieves value from POST request parameter.

  • function _GETset($inputGet,$key='')

    Sets the value of GET request parameter. There is no equivalent for POST.

  • function GParrayMerged($var)

    Many TYPO3 modules pass parameters in URLs as arrays. If PHP finds something like tx_extkey_pi1[param]=123 in the URL, tx_extkey_pi1 will be an array with a key named param and value, 123. This function will merge such an array for POST and GET, with POST taking preference, and returning this array.

String Functions

There are several functions in this group that perform common tasks:

  • function fixed_lgd_cs($string,$chars)

    This function will crop a string and append three dots to the string. The $chars variable indicates the length. If the value is negative, it will crop that amount of characters from the end of the string. This function will work well only if $GLOBALS['LANG'] (see section about localization) is set. So it is generally limited to BE scripts usage.

  • function testInt($var)

    This function checks if the passed variable is an integer. Variables in PHP can be freely converted between types. For example, '2'+'3' gives 5. This function will return true for 3 and '3', but not for '3x'. Thus, it is very convenient to validate some parameters (like database identifiers).

  • function isFirstPartOfStr($str,$partStr)

    The name of this function is self-explanatory.

  • function validEmail($email)

    This function checks if the passed email has the correct syntax. It does not check whether that domain is valid or whether the mail account exists.

Array Functions

Array functions manipulate array data. Here is the list of the most useful functions:

  • function implodeArrayForUrl($name,$theArray,$str='',$skipBlank=0, $rawurlencodeParamName=0)

    This function creates a URL query string from the passed array. Keys become parameter names, values become parameter values. If $name is not empty, each parameter becomes a value in the array with this name (see description of GParrayMerged above). The $str variable must be blank (TYPO3 uses it internally when calling this function recursively).

  • function array_merge_recursive_overrule($arr0,$arr1,$notAddKeys=0, $includeEmtpyValues=true)

    This function recursively merges two arrays. The second array overrides the first. If $notAddKeys is set, key/value pairs from the second array are ignored if the corresponding key does not exist in the first array.

  • function csvValues($row,$delim=',',$quote='"')

    Creates a comma-separated string of values and takes care of new line characters. The result can be imported into a spreadsheet application. Note that it works on a single row of data and should be called as many times as there are rows. Useful for dumping data into CSV files.

XML Processing

There are only two very specific functions that we will mention here. They are used to convert XML to a PHP array, and vice versa.

  • function array2xml($array,$NSprefix='',$level=0,$docTag='phparray',$spaceInd=0, $options=array(),$stackData=array())

    This function converts a PHP array into XML. We will not describe it in detail here. but recommend that you refer to the documentation inside this function to see all possible options (there are too many of them).

  • function xml2array($string,$NSprefix='',$reportDocTag=FALSE)

    Converts an XML string into an array.

Before using these functions, you should remember that PHP arrays cannot have multiple values with the same key. So, if there are repeating nodes in your XML, you have to add an attribute named index to them. This attribute becomes a key. Here is an example:

<data>
<node index="1"><value>value 1</value></node>
<node index="2"><value>value 2</value></node>
<node index="3"><value>value 3</value></node>
</data>

Running xml2array produces the following PHP array:

Array
(
[1] => Array
(
[value] => value 1
)
[2] => Array
(
[value] => value 2
)
[3] => Array
(
[value] => value 3
)
)

Without the index attribute, the array becomes:

Array
(
[node] => Array
(
[value] => value 3
)
)

File, Directory, and URL Functions

These functions perform the most common tasks for file, directory, and URLs. Here is the list:

  • function getURL($url, $includeHeader=0)

    This function fetches a URL and returns the result. This function must be used instead of fopen() or file_get_contents() because PHP functions may be disabled by security settings on the server. However, getURL may use other methods to fetch the content of the URL.

  • function writeFileToTypo3tempDir($filepath,$content)

    This function writes a file to the TYPO3 temporary directory.

  • function getFilesInDir($path,$extensionList='', $prependPath=0,$order='')

  • function getAllFilesAndFoldersInPath($fileArr,$path, $extList='',$regDirs=0,$recursivityLevels=99)

  • function fixWindowsFilePath($theFile)

  • function locationHeaderUrl($path)

    This function takes a path and returns a correct URL to be used for redirection using the header() PHP function.

Debug Functions

These functions dump passed variables. Note that the web IP address of website visitors must be included in the devIPmask parameter in the TYPO3 Install tool, so that these functions can be used for urgent debugging on a live site!

  • function print_array($array_in)

  • function debug($var="",$brOrHeader=0)

  • function debug_trail() Returns stack trace with entries separated by //.

  • function debugRows($rows,$header='')

System Functions

These are general-purpose functions. They do not fit into any other group. Therefore we list them here:

  • function getThisUrl()

  • function linkThisScript($getParams=array())

    Returns a link to this script. All the parameters are preserved. It is possible to override the parameters by passing an array with new name/value pairs.

  • function linkThisUrl($url,$getParams=array())

    This function is similar to the previous function except that it returns a link to the URL.

  • function getIndpEnv($getEnvName)

    This function is the way TYPO3 gets server variables such as HTTP_HOST or REMOTE_ADDR. TYPO3 runs on many platforms and under many web servers. Not all these servers return parameters in the same way. This function takes care of the differences, and will always return a server parameter, regardless of the server software. It must be used instead of $_SERVER and $_ENV PHP variables. In addition, this function can return values of TYPO3-defined variables. Here is a list of these variables:

    • TYPO3_HOST_ONLY

      Host name

    • TYPO3_PORT

      Port (blank if 80, taken from host value)

    • TYPO3_REQUEST_HOST

      [scheme]://[host][:[port]]

    • TYPO3_REQUEST_URL

      [scheme]://[host][:[port]][path]?[query]

    • TYPO3_REQUEST_SCRIPT

      [scheme]://[host][:[port]][path_script]

    • TYPO3_REQUEST_DIR

      [scheme]://[host][:[port]][path_dir]

    • TYPO3_SITE_URL

      [scheme]://[host][:[port]][path_dir] of the TYPO3 website Frontend

    • TYPO3_SITE_SCRIPT

      [script / Speaking URL] of the TYPO3 website

    • TYPO3_DOCUMENT_ROOT

      Absolute path of root of documents: TYPO3_DOCUMENT_ROOT.SCRIPT_NAME = SCRIPT_FILENAME (typically)

    • TYPO3_SSL

      Returns TRUE if this session uses SSL/TLS (https)

TYPO3-Specific Functions

There are a lot of functions here that also fall into categories we have already seen. However, the functions discussed earlier are universal. They do not depend on TYPO3 variables and can generally be reused outside TYPO3. Functions from this group take TYPO3 into account. There are lots of them. We list only the ones that are likely to be used in extensions:

  • function validPathStr($theFile)

  • function tempnam($filePrefix)

    Creates a temporary file in the typo3temp/ directory.

  • function loadTCA($table)

    Loads the table configuration array (TCA). We will discuss the TCA later in this book.

  • function callUserFunction($funcName,&$params,&$ref, $checkPrefix='user_',$silent=0)

    Calls the user function. The function name has a certain format (see "TYPO3 File References" section that follows).

  • function &getUserObj($classRef,$checkPrefix='user_',$silent=0)

    Instantiates a class and returns its instance.

  • function &makeInstance($className)

    Creates an instance of the class, given its name. This function must be used instead of the new PHP operator. It takes care of proper XCLASS handling. Use this function even for your internal classes!

  • function makeInstanceClassName($className)

    Checks if XCLASS is available for any given class and returns either the class or XCLASS name. It is useful when instantiating an object with parameters for the constructor. For example, function devLog($msg, $extKey, $severity=0, $dataVar=FALSE) logs messages to the developer log. By default, it does nothing. Requires logging extension (such as cc_devlog or rlmp_filedevlog) and SYS_DLOG enabled in TYPO3 Install tool. It is useful in recording a sequence of events on a production server. Beware of performance decline

TYPO3 File References

TYPO3 has a special syntax to refer files or classes and functions inside files. Typically, it is used for extensions. This syntax is similar to a URL. Check the following code fragment:

$params = array();
$result = t3lib_div::callUserFunction( 'EXT:myext/subdir/class.tx_myext_class.php:' . 'tx_myext_class->main', $params, $this);

The callUserFunction function will read the function name as follows:

  • EXT means that the function has an extension.

  • Extension key follows the EXT: prefix until the first slash.

  • Everything until the colon is the file path.

  • If there is a -> sequence, then the function is a method of the class. The class will be instantiated using t3lib_div::makeInstance() and a method of the class will be called. The class name must start with tx_ or ux_ or user_. If there is no -> sequence, then it is a function without a class, and its name must start with user_. If the class or function name does not start with a proper prefix, then callUserFunction will refuse to call them.

The getUserObj function is similar to the callUserFunction function except that it does not have a function part, but a class name. It returns a class instance and the extension can call methods of this class. Name restrictions apply to getUserObj as well.

Language Support

TYPO3 supports many languages and works very well with them. It has a set of functions and classes that provide access to localized (translated) strings. These strings are stored in XML files. Extensions need not care about finding the right language or parsing XML because TYPO3 has a very good API for it. While using this API, all strings from XML can be referenced by their index. The index is also a string, but it stays non-localized. A string changes, but its index does not. So it is always possible to find the value of the string by its index. To give a short example, the "Click here" string may have a "click_here" index (the code example at the end of this section will make it clearer).

Usually, localization is one of the areas that lacks a developer's attention. It is much easier to hard-code strings in the PHP code than to write them to an XML file and add an extra line to load that string. But supporting translations is one thing that makes a good extension. Do not postpone the creation of language files. Do it properly from the beginning; do not hard-code. Write a string to an XML file and use the API to get its value.

What happens if an extension is not translated to another language (partially or fully)? The labels in the default language (English) will be used instead.

The class that implements localization support for strings is an old class and, as many old classes, it is an exception to the naming rule described earlier in this chapter. The class name is language and it is located in typo3/sysext/lang (system extension lang). In the BE, it is usually available as $GLOBALS['LANG']. In the FE, it is not available directly, but there are similar ways to get localized strings.

The following methods are the most used ones:

  • function init($lang,$altPath='')

    This function initializes the language class for the given language code. Languge code is TYPO3-specific. The special code value default refers to the English language. You will rarely need to use this function. Generally, it is enough to know that you have to call this function if for any reason you need to create an instance of the language class yourself. But if you really have to, it means that you are doing something nontrivial, and you should be an experienced extension writer to use this function.

  • function getLL($index,$hsc=0)

    This function returns a label by its string index. Labels must be included using the includeLLFile function (see below).

  • function getLLL($index,$LOCAL_LANG,$hsc=0)

    Does the same as getLL, but uses the $LOCAL_LANG argument to search for the string.

  • function sL($input,$hsc=0)

    This function is the most complex but also the most powerful among all the functions in this class. It accepts string reference in a special format and returns the string. The string reference format is similar to the format described earlier in the section named "TYPO3 File References", but it must be prepended with the LLL: prefix, for example, LLL:EXT:lang/locallang_general.xml:LGL.image. This reference tells TYPO3 to load a string identified by LGL.image index from the file named locallang_general.xml in lang extension. The $hsc parameter allows automatic application of the htmlspecialchars PHP function to the returned string.

  • function includeLLFile($fileRef,$setGlobal=1, $mergeLocalOntoDefault=0)

    This function loads information from a language file into a global variable for use with the getLL function. It accepts EXT: file referencing format for files as well as the absolute path.

  • function readLLfile($fileRef)

    This function reads a language file and returns labels for the current language to the caller. It also accepts the EXT: syntax for $fileRef.

Here is a full code example:

require_once(t3lib_extMgm::extPath('lang', 'lang.php'));
$lang = t3lib_div::makeInstance('language');
/* @var $lang language */
$lang->init('default');
$fileRef = 'EXT:lang/locallang_general.xml';
$label1 = $lang->sL('LLL:' . $fileRef . ':LGL.image');
$LL = $lang->readLLfile($fileRef);
$label2 = $lang->getLLL('LGL.image', $LL);
$lang->includeLLFile($fileRef);
$label3 = $lang->getLL('LGL.image');

All three labels are identical in this example.

Reference Index

TYPO3 stores data in the database. Often, data records refer to other data records or files in the file system. If a data record is deleted and it has a reference to a file, the file stays in the file system. If another data record refers to the deleted data record, there will be a dead link.

To prevent these kinds of problems, TYPO3 maintains a separate list of references between data records and files in the system. This is called the "reference index". The class name is t3lib_refindex.

When data records are created, modified, or deleted in Backend using TCEmain (described in the Backend API section below), the system will update the reference index automatically. FE is different; TCEmain does not work there. FE plugins usually insert data directly into the database using the database API described earlier in this book. So, extension developers have to take care and update the reference index manually. Unfortunately, very few extensions do. Originally, the reference index was developed for BE usage, but its dependency on BE functions is minimal and solved by including class.t3lib_befunc.php in the FE plugin.

When the reference index is used, the system will show a number of references to the current record in the List module, and a clean-up script will be able to detect hanging files and remove them.

We are interested in the following function:

  • function updateRefIndexTable($table,$uid,$testOnly=FALSE)

This function will examine the record and update the reference index. Here are some code examples:

require_once(PATH_t3lib . 'class.t3lib_befunc.php');
require_once(PATH_t3lib . 'class.t3lib_refindex.php');
$fields = array(
// Fill array with fields
);
$GLOBALS['TYPO3_DB']->exec_INSERTquery('tx_myext_table',
$fields);
$uid = $GLOBALS['TYPO3_DB']->sql_insert_id();
$refIndex = t3lib_div::makeInstance('t3lib_refindex');
/* @var $refIndex t3lib_refindex */
$refIndex->updateRefIndexTable('tx_myext_table', $uid);

This is all that has to be done to update the reference index. The @var comment helps modern PHP IDEs show code assist for the variable.

Hooks

Hooks in TYPO3 are special functions that the system calls while it performs some actions. Using hooks, extensions can modify a process in the middle, observe system state, pre- and post-process data, and do many other things.

Typically, a hook function is defined either as a regular function with user_ prefix or as a class prefixed with tx_extkey_. Hooks are registered in the extension's ext_localconf.php file (see Chapter 2) in the following way:

$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']
['cms/layout/class.tx_cms_layout.php']
['list_type_Info']['extkey_pi1'][] =
'EXT:extkey/class.tx_extkey_cms_layout.php
:tx_extkey_cms_layout->getExtensionSummary';

This particular hook will provide additional information to the page module to display near the extension's instance.