Good habits while coding in Contao

I want to dedicate this post to all Contao module developers, which are either novice or messy programmers. I have recently checked quite a number of extensions in the official repository, and it becomes a real challenge if you woud like to adjust some of them to your needs.
There are some snippets of code that are taken as they are from various extensions. The point of bringing them out is not to blame its author, but to show mistakes that need can be improved. Therefore I will not reveal either the author or name of the module.
Most of the good habits covered below are taken directly from Contao core code, and although you have your own style coding, it would be great if you could consider checking them.
Comment, comment and comment!
There are basically three ways to comment your code in the php:
/* This comment style is known as a multi line */ // This one is called a one-line c++ style comment # And this a one-line shell-style comment
Okay, so how these three types relate to Contao? Actually only two of them are present, because Leo decided to discard shell-style comments. Personally, I use this style only at the time of development to temporarily comment some lines of the code, but this is rather a personal choice.
Anyway, the other two are strictly assigned to their purposes and they should never be switched. A multi line comment are used only to describe classes, their functions and variables. I also have seen this style present in configuration, languages or DCA files – there, they are probably used to increase readibility.
A c++ style comment perfectly suits to comment single pieces of code which might not be understood at first sight. Note that they are never placed after the code, but a line above it.
Good:
/**
* Autogenerate a news alias if it has not been set yet
* @param mixed
* @param object
* @return string
*/
public function generateAlias($varValue, DataContainer $dc)
{
$autoAlias = false;
// Generate alias if there is none
if (!strlen($varValue))
{
$autoAlias = true;
$varValue = standardize($dc->activeRecord->headline);
}
$objAlias = $this->Database->prepare("SELECT id FROM tl_news WHERE alias=?")
->execute($varValue);
// Check whether the news alias exists
if ($objAlias->numRows > 1 && !$autoAlias)
{
throw new Exception(sprintf($GLOBALS['TL_LANG']['ERR']['aliasExists'], $varValue));
}
// Add ID to alias
if ($objAlias->numRows && $autoAlias)
{
$varValue .= '-' . $dc->id;
}
return $varValue;
}
Bad:
public function getPageIdsByPid($pid) {
$res = $this->Database->prepare('SELECT * from tl_page where pid=? AND published=? AND ( (start = "" || start < NOW()) && (stop = "" OR stop > NOW()))')->execute($pid, 1);
if($res->numRows == 0) return(array());
while($res->next()) {
$arrPages[] = $res->id;
$subPages = $this->getPageIdsByPid($res->id);
if($subPages != false)
$arrPages = array_merge($arrPages, $subPages);
}
if(count($arrPages) > 0)
return($arrPages);
return(false);
}
How Contao relates to phpDocumentator principles?
Contao actually uses only a few tags from the complete set of that tool. And these are:
- @copyright
- @author
- @package
- @license
- @param
- @return
What’s more, only one feature of @param and @return is used – the variable type. It should also contain a variable name and its purpose. Leo didn’t put that info, probably because the variables names are self explanatory. According to phpdoc standards, the comment should look like this:
/**
* Autogenerate a news alias if it has not been set yet
* @param mixed $varValue An empty value or a custom string.
* @param object $dc Data Container object.
* @return string Usually a standardized news headline.
*/
public function generateAlias($varValue, DataContainer $dc)
{
// ...
}
Variable names
This may not only refer to coding in Contao, but in any other project. Correctly named variables are the key to fast and efficient development. As far as I am concered, there are two main ways to do that. As you certainly know, a variable can be either string, integer, object, array or any other type. OOP naming convention aims to provide self explanatory variable types basing on its name. Let’s check the examples below.
The first way, which isn’t used in Contao, is to name variables with a prefix of one letter depending on its type. As the example we’ll take a code that retrieves some data from the database and puts it into array.
Acceptable:
$oArticles = $this->Databse->execute("SELECT * FROM tl_article");
while ($oArticles->next())
{
$aArticles[$oArticles->id] = $oArticles->title;
}
I know some poeple that prefer this style, but in my opinion it’s not that readable as the second way of naming…
This one is used throughout whole Contao and actually is very similar to first. The only difference is that it uses three letters as prefix, instead of one.
Good:
$objArticles = $this->Databse->execute("SELECT * FROM tl_article");
while ($objArticles->next())
{
$arrArticles[$objArticles->id] = $objArticles->title;
}
Both ways are okay to do that, decide which one suits you most. But do a favour for all programmers in the world and do not use variables that do say absolutely nothing about their type.
Bad:
$res = $this->Database->execute("SELECT * FROM tl_article");
while ($res->next())
{
$articles[$res->id] = $res->title;
}
Statements
How often do I see a poor use of statements. Not only that it decreases the readibility, but in some cases it may also run into trouble. Try to always enclose all statements and loops within brackets. Don’t be afraid of using the ternary operator! It is made to shorten the code, and so it should be used.
Good:
$blnAjax = ($this->Input->get('isAjax') == 1) ? true : false;
if (!is_array($ac))
{
continue;
}
Bad:
if($this->Input->get('isAjax') == 1)
$blnAjax = true;
if(!is_array($ac)) continue;
Imitate the core code style
Browsing through core modules, you will always find some code that might come very useful. There are some neat solutions, which not only can save your time, but also resources (example below). Unless you are an experienced programmer, you should always look for a hint in the Contao source code. The more times you will imitate those solutions, the sooner you will get a better developer.
Good:
$objNews = $this->Database->execute("SELECT * FROM tl_news WHERE pid IN(" . implode(',', array_map('intval', $this->news_categories)) . ")");
$arrNews = $objNews->row();
Bad:
foreach ($this->news_categories as $category)
{
$objNews = $this->Database->prepare("SELECT * FROM tl_news WHERE pid=?")->execute($category);
$arrNews[] = $objNews->row();
}
Lol, this code would kill the database some day
Make use of the provided utilities
Duplicating some code that already exists (even that you may not know about) is against DRY principle. Although you are not repeating yourself, you should always use available Contao functions.
There are plenty of examples I could provide, unfortunately most of them you will have to find on your own, because at the moment there is no comprehensive documentation which covers full list of functions. However, if you need to lookup some functions at the time of development, you can use my script here.
Good:
$arrTags = trimsplit(',', $strTags);
Bad:
foreach (explode(',', $strTags) as $tag)
{
$arrTags[] = trim($tag);
}
Take those to your heart
The last point – always keep these few clues in your mind when developing extensions for Contao, even if those are never published. You may sometimes return back to the code written months ago – you don’t want to waste time for recalling what’s that code for, do you?
About Author
Bart van Asperen March 15th
Great post Kamil! There should be a checking system for extensions before they get published. So quality is guaranteed. What do you think?
Kamil Kuzminski March 15th
Thanks! Well the idea is good, but I am afraid that 80% of extensions wouldn’t pass such quality test
Cyrus munyao March 25th
thanks qzy
Andreas May 19th
Hey Kamil,
what do you think about intending with spaces vs tabs?
I prefer to intend with 2 spaces. Because different Editors are intending tabs in a dirrerent width. With spaces you allways have the same width. But I think it’s a matter of taste. What I don’t like is a mix of spaces and tabs.
Andreas
Kamil Kuzminski May 19th
Hi Andreas
That is true that different editors have different tab width. I remember I had some issues in Netbeans because of that. 6 or even 12 months ago I switched to Notepad++, and I am very happy because it reads Contao code indents very correctly.
Frankly I prefer to use Tab button to make indents because it is way faster than spaces, and because it became my habit. I heard that editors have option to set tab width, but I never used it.
Here on my blog I use two spaces as a regular indent since WordPress didn’t want to accept a tabbed one
Add Yours
YOU