By Sebastiaan de Jonge, published on Thursday, September 9, 2010 at 07:00

When there are no hooks available to get the job done, it's most of the times possible to extend core and extension classes by using XCLASS. This article will describe the steps that need to be taken to do so.

What is XCLASS?

XCLASS basically stands for eXtend CLASS, it allows you to extend TYPO3 core and extension PHP classes through a simple configuration. This will give you some extra flexibility in case you want to extend some functionalities in TYPO3 itself or some extension.

An example

To show what we can achieve I have made a little example extension. How this extension works, will be explained now. If you want to find out yourself, you can just scroll down and download the extension yourself.

The Extension

The extension does something very simple. It replaces the icons in Web > List inside the TYPO3 backend with the flag icons (if any are specified). It's a fun little trick, just to show what is possible.

The result will be like this:

When the original is like this (a little more boring):

Getting started

First of all, to get started, we need to figure out what XCLASS we should use. I know for example, that the list view itself is already XCLASS-ed several times. And not to kill any changes, we will need to find XCLASS on XCLASS on XCLASS (still following?).

The functionality we want to actually extend, is the one from t3lib_recordList. If you look in your TYPO3 folder, you will find several files carrying the name db_list. In order to find which class we will XCLASS we have to reverse engineer a little. First we are going to look for a class that extends t3lib_recordList. Which is in fact the class recordList, in the file: class.db_list.inc. Now we have to see if something is XCLASS-ing recordList. In fact there is, localRecordList. This one is found inside class.db_list_extra.inc. Now we should check if there is another level of extending, but luckily there isn't.

The next we need to do is create a tiny dummy extension. Easiest way to do this is by using the kickstarter extension. All you will need to enter is an extension key, a name and a description. You don't need to add anything else, no plugins or modules.

Setting up our extension

To setup our extension there's a couple of things we need to do. If you haven't already created an ext_localconf.php inside your extension folder, please do so. This is where we will make the XCLASS reference. The reference looks like this (remember to change sdj_xclass to your extension key):

$TYPO3_CONF_VARS['BE']['XCLASS']['typo3/class.db_list_extra.inc'] = t3lib_extMgm::extPath('sdj_xclass').'class.ux_localRecordList.php';

Next thing to do will be to create file that will contain the actual XCLASS. The naming is important, in this name the file should be called class.ux_localRecordList.php, with the class inside ux_localRecordList.  If you don't set the classname correctly, it will simply not work. Extended classes should be the original classname, prefixed with ux_.

Creating our extended class

Now that we have created our file, let start adding some code.

class ux_localRecordList extends localRecordList {
	protected $s_flagIconPath;
	/**
	 * Constructor. Needed to get the proper flag path. Since there are extensions that can overrule the original flags.
	 */
	public function __construct() {
		parent::__construct();
		$s_flagPath = $GLOBALS['TCA']['sys_language']['columns']['flag']['config']['fileFolder'];
		if(strstr($s_flagPath,'EXT:')) {
			$a_flagPathParts = t3lib_div::trimExplode('/',$s_flagPath);
			$a_flagPathParts[0] = substr($a_flagPathParts[0],4);
			$a_flagPathParts[0] = t3lib_extMgm::extRelPath($a_flagPathParts[0]);
			$s_flagPath = implode('/',$a_flagPathParts);
		} else {
			$s_flagPath = '../'.$s_flagPath;
		}
		$this->s_flagIconPath = $s_flagPath;
	}
	/**
	 * Extending renderListRow to make the changes
	 *
	 * @param string $s_table
	 * @param array $a_row
	 * @param int $cc
	 * @param string $s_titleCol
	 * @param string $s_thumbsCol
	 * @param int $i_indent
	 */
	public function renderListRow($s_table,$a_row,$cc,$s_titleCol,$s_thumbsCol,$i_indent=0) {
		// First we will render the list row with the original render function
		$s_listRow = parent::renderListRow($s_table,$a_row,$cc,$s_titleCol,$s_thumbsCol,$i_indent);
		// Now we will check if this a row for te sys_language table, and if the flag is set.
		if($s_table == 'sys_language' && !empty($a_row['flag'])) {
			// We generate the new image
			$s_newIcon = '<img alt="" src="'.$this->s_flagIconPath.$a_row['flag'].'" />';
			// And we replace the original using regular expressions (TYPO3 4.4)
			$s_listRow = preg_replace('|<span title="([^"]*)" style="" class="([^"]*)t3-icon-x-sys_language"></span>|U',$s_newIcon,$s_listRow,1);
			// Replace for previous TYPO3 versions (4.4 >)
			$s_listRow = preg_replace('|<img src="gfx\/i\/sys_language.gif"([^>]*)>|U',$s_newIcon,$s_listRow,1);
		}
		// Finallay we will return the rendered row
		return $s_listRow;
	}
}

As you may see, it's not that much code. The function that does the actual replacing, is (without comments) only 9 lines. I've also added an extra function to get the correct path to the flag icons. Since it's possible to overrule the standard flags with some extensions.

The renderListRow function is where all the real magic happens, here's how it works. First of all we will need to (in this case) render the data in the original way. Because we want to change only a small bit of it, and not rewrite the whole functionality. Next, we will do some checking if the record should be effected, followed by the actual replacements and returning of the result.

Some extras

The constructor is just used to set the correct path to the flags. Theres a little extra here, it will also check if the flag path has changed. Since it's possible for another extension to have changed it. Since this only needs to be defined once, the easiest way is to do this in the constructor.

Next to that we come upon another small problem. The flag icon is not selected from the database by default. This we change with the following hook:

$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list.inc']['makeQueryArray']

I will not go into detail about this little extra, since I want to focus this article just on XCLASS. If you are interested in how it's done though, I suggest you download the demo extension at the bottom of this post.

Some advise about the use of XCLASS

Although XCLASS may seem to give an awesome flexible solution, it doesn't. Before you consider using XCLASS you should first check if there really is no other possibility to do what you need to do with a hook. The problem lies in the fact that XCLASS can only be applied once on a class. Meaning that they can overwrite each other. Other things might go wrong when an extension or even the core is updated. Since some functions might change, be added or disappear. This may result into a broken TYPO3 installation.

So when you have the possibility not to use XCLASS, don't use it. Use hooks instead! 🙂

Official documentation for XCLASS can be found here:
typo3.org/documentation/document-library/core-documentation/doc_core_api/4.3.0/view/3/9/

Update!

2010-09-09 11:00 - Funny fact I just discovered in the TYPO3 core mailing list. This feature (flags as icons for sys_language) is actually a feature request for the TYPO3 core.

See http://bugs.typo3.org/view.php?id=15666 for more details.


2010-09-09 11:34 - I have added parent::__construct(); to the constructor of my extended class. Since t3lib_recordlist also has a constructor, we don't want to lose any of the functionalities 🙂 Attached file is also updated. Thanks to Jigal for spotting this.

Comments

Benjamin Serfhos
Benjamin Serfhos - Thursday, September 9, 2010 at 09:11

Never tried this trick, good article!

But when you want to add extra functionality and there is no hook, I think its better to just ask for a hook inside the core or extension 🙂

Jigal
Jigal - Thursday, September 9, 2010 at 09:11

Nice to see an article with a short example.

The formatted code hides quite a few parts of several lines (public function ...., etc.)

Was there a reason to not call parent::__construct() ? The grandparent t3lib_recordlist::__construct() seems to do some useful work.

Sebastiaan
Sebastiaan - Thursday, September 9, 2010 at 09:25

@benjamin: There is some sense in that, but plenty of times a feature or hook request is so specific that it's not added.

@jigal: I know about the code formatting, still have to add a show plain/download functionality for the code snippets 😉 Concerning the parenting constructor, you are absolutely right. I overlooked. Added the code.