Table of Contents
Table of Contents
Coding Standards are an important factor for achieving a high code quality. A common visual style, naming conventions and other technical settings allow us to produce a homogenous code which is easy to read and maintain. However, not all important factors can be covered by rules and coding standards. Equally important is the style in which certain problems are solved programmatically - it's the personality and experience of the individual developer which shines through and ultimately makes the difference between technically okay code or a well considered, mature solution.
These guidelines try to cover both, the technical standards as well as giving incentives for a common development style. These guidelines must be followed by everyone who creates code for the FLOW3 core. Because TYPO3 is based on FLOW3, it follows the same principles - therefore, whenever we mention FLOW3 in the following sections, we equally refer to TYPO3. We hope that you feel encouraged to follow these guidelines as well when creating your own packages and FLOW3 based applications.
The visual style of programming code is very important. In the TYPO3 project we want many programmers to contribute, but in the same style. This will help us to:
Easily read/understand each others code and consequently easily spot security problems or optimization opportunities
It is a signal about consistency and cleanliness, which is a motivating factor for programmers striving for excellence
Some people may object to the visual guidelines since everyone has his own habits. You will have to overcome that in the case of FLOW3; the visual guidelines must be followed along with coding guidelines for security. We want all contributions to the project to be as similar in style and as secure as possible.
Almost every PHP file in FLOW3 contains exactly one class and
does not output anything if it is called directly. Therefore you
start your file with a <?php tag and end it with the
closing ?>.
Every file must contain a header stating encoding, namespace, copyright and licensing information
Declare your namespace. The namespace must start with "F3"!
Because it is likely that more than one person will work on a class in the long run, we recommend adding a copyright statement like “Copyright belongs to the respective authors” and add yourself to the list of authors for the methods you implemented or changed (significantly).
The copyright header itself must not start with
/**, as this may confuse documentation
generators!
Example A.1. The FLOW3 standard file header
<?php declare(ENCODING = 'utf-8'); namespace F3\Your\Stuff\Here; /* * * This script belongs to the FLOW3 framework. * * * * It is free software; you can redistribute it and/or modify it under * * the terms of the GNU Lesser General Public License as published by the * * Free Software Foundation, either version 3 of the License, or (at your * * option) any later version. * * * * This script is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- * * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * * General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with the script. * * If not, see http://www.gnu.org/licenses/lgpl.html * * * * The TYPO3 project - inspiring people to share! * * */
Code lines are of arbitrary length, no limitations to 80 characters or something similar (wake up, graphical displays have been available for decades now...)
Lines end with a newline a.k.a chr(10) - UNIX
style
Files must be encoded in UTF-8
Make sure you use the correct license and mention the correct package in the header, See http://forge.typo3.org/wiki/flow3-overview/Licensing_boilerplates_to_use_with_your_code for some boilerplate text.
Indentation is done with tabs - and not spaces! The beginning of a line is the only place where tabs are used, in all other places use spaces. Always trim whitespace off the end of a line.
Here's a code snippet which shows the correct usage of tabs and spaces:
Example A.2. Correct use of tabs and spaces
/** * Returns the name of the currently set context. * * @return string Name of the current context * @author Your Name <your@email.here> */ public function getContextName() { » return $this->contextName; }
There seem to be very passionate opinions about whether TABs or spaces should be used for indentation of code blocks in the scripts. If you'd like to read more about this religious discussion, you find some nice arguments in the Joels on Software forum.
Naming is a repeatedly undervalued factor in the art of software development. Although everybody seems to agree on that nice names are a nice thing to have, most developers choose cryptic abbreviations in the end (to save some typing). Beware that we TYPO3 core developers are very passionate about naming (some people call it fanatic, well ...). In our opinion spending 15 minutes (or more ...) just to find a good name for a method is well spent time! There are zillions of reasons for using proper names and in the end they all lead to better readable, manageable, stable and secure code.
As a general note, english words (or abbreviations if neccessary) must be used for all class names, method names, comments, variables names, database table and field names. Although PHP6 allows for using funny japanese, tibetian or don't-know-what characters, the consensus is that english is much better to read for the most of us.
All package names are start with an uppercase character and usually are written in UpperCamelCase. In order to avoid problems with different filesystems, only the characters a-z, A-Z, 0-9 and the dash sign "-" are allowed for package names – don't use special characters.
Only the characters a-z, A-Z and 0-9 are allowed for namespace and class names.
Namespaces are usually written in UpperCamelCase but variations are allowed for well established names and abbreviations.
Class names are always written in
UpperCamelCase.
The unqualified class name must be meant literally even without the namespace.
The main purpose of namespaces is categorization and ordering
Class names must be nouns, never adjectives.
The name of abstract classes must start with the word "Abstract", class names of aspects must end with the word "Aspect".
A few examples:
Table A.1. Incorrect naming of namespaces and classes
| Fully qualified class name | Unqualified class name | Remarks |
|---|---|---|
| \F3\FLOW3\Session\Php | Php | The class is not a representation of PHP! |
| \F3\FLOW3\Cache\Backend\File | File | The class doesn't represent a file! |
| \F3\FLOW3\Session\Interface | Interface | Not allowed, "Interface" is a reserved keyword |
| \F3\Foo\Controller\Default | Default | Not allowed, "Default" is a reserved keyword |
| \F3\FLOW3\Object\Manager | Manager | Just "Manager" is too fuzzy |
Table A.2. Correct naming of namespaces and classes
| Fully qualified class name | Unqualified class name | Remarks |
|---|---|---|
| \F3\FLOW3\Session\PhpSession | PHPSession | That's a PHP Session |
| \F3\FLOW3\Cache\Backend\FileBackend | FileBackend | A File Backend |
| \F3\FLOW3\Session\SessionInterface | SessionInterface | Interface for a session |
| \F3\Foo\Controller\StandardController | StandardController | The standard controller |
| \F3\FLOW3\Object\ObjectManager | ObjectController | "ObjectManager" is clearer |
Table A.3. Edge cases in naming of namespaces and classes
| Fully qualified class name | Unqualified class name | Remarks |
|---|---|---|
| \F3\FLOW3\MVC\ControllerInterface | ControllerInterface | Consequently the interface belongs to all the controllers in the Controller sub namespace |
| \F3\FLOW3\MVC\Controller\ControllerInterface | Better | |
| \F3\FLOW3\Cache\AbstractBackend | AbstractBackend | Same here: In reality this class belongs to the backends |
| \F3\FLOW3\Cache\Backend\AbstractBackend | Better |
When specifying class names to PHP, always reference the
global namespace inside namespaced code by using a leading
backslash. When referencing a class name inside a string (e.g. given
to the create-Method of the
ObjectFactory, in pointcut expressions or in
YAML files), never use a leading backslash. This follows the native
PHP notion of names in strings always being seen as fully
qualified.
Only the characters a-z, A-Z and 0-9 are allowed for interface names – don't use special characters.
All interface names are written in
UpperCamelCase. Interface names must be
adjectives or nouns and have the Interface suffix. A few examples
follow:
Example A.3. Correct naming of interfaces
\F3\FLOW3\Object\ObjectInterface
\F3\FLOW3\Object\ObjectManagerInterface
\F3\MyPackage\MyObject\MySubObjectInterface
\F3\MyPackage\MyObject\MyHtmlParserInterface
Exception naming basically follows the rules for naming classes.
There are two possible types of exceptions: generic exceptions and
specific exceptions. Generic exceptions should be named "Exception"
preceeded by their namespace. Specific exceptions should reside in
their own sub-namespace end with the word
Exception.
Example A.4. Correct naming of exceptions
\F3\FLOW3\Object\Exception
\F3\FLOW3\Object\Exception\InvalidClassNameException
\F3\MyPackage\MyObject\Exception
\F3\MyPackage\MyObject\Exception\OutOfCoffeeException
All method names are written in lowerCamelCase. In order to avoid problems with different filesystems, only the characters a-z, A-Z and 0-9 are allowed for method names – don't use special characters.
Make method names descriptive, but keep them concise at the same
time. Constructors must always be called
__construct(), never use the class name as a
method name.
A few examples:
Example A.5. Correct naming of methods
myMethod()
someNiceMethodName()
betterWriteLongMethodNamesThanNamesNobodyUnderstands()
singYmcaLoudly()
__construct()
Variable names are written in lowerCamelCase
and should be
self-explaining
not shortened beyond recognition, but rather longer if it makes their meaning clearer
The following example shows two variables with the same meaning but different naming. You'll surely agree the longer versions are better (don't you ...?).
As a special exception you may use variable names like
$i, $j and $k
for numeric indexes in for loops if it's clear
what they mean on the first sight. But even then you should want to
avoid them...
All constant names are written in
UPPERCASE. This includes
TRUE, FALSE and
NULL. Words can be separated by underscores - you
can also use the underscore to group constants thematically:
Example A.8. Correct naming of constants
STUFF_LEVEL
COOLNESS_FACTOR
PATTERN_MATCH_EMAILADDRESS
PATTERN_MATCH_VALIDHTMLTAGS
It is, by the way, a good idea to use constants for defining regular expression patterns (as seen above) instead of defining them somewhere in your code.
These are the rules for naming files:
All file names are
UpperCamelCase.
Class and interface files are named according to the class or interface they represent
Each file must contain only one class or interface
Names of files containing code for unit tests must be the same as the class which is tested, appended with "Test.php".
Files are placed in a directory structure representing the namespace structure.
Here are some examples:
Example A.9. File naming in FLOW3
TemplateEngine/TemplateEngineInterface.php
Contains the interface
\F3\TemplateEngine\TemplateEngineInterface
which is part of the package
\F3\TemplateEngine
Error/RuntimeException.php
Contains the
\F3\Error\RuntimeException being a
part of the package \F3\Error
DataAccess/Manager.php
Contains class
\F3\DataAccess\Manager which is part
of the package \F3\DataAccess
FLOW3/Package/Manager.php
Contains the class
\F3\FLOW3\Package\PackageManager
which is part of the package
\F3\FLOW3
Package/ManagerTest.php
Contains the class
\F3\FLOW3\Package\ManagerTest which
is a test case for PHPUnit.
In general, we use single quotes to enclose literal strings:
If you'd like to insert values from variables, concatenate strings:
A space must be inserted before and after the dot for better readability:
You may break a string into multiple lines if you use the dot operator. You'll have to indent each following line to mark them as part of the value assignment:
There needs to be one space between the
if keyword and the opening brace "(" of the
test expression
After the closing brace ")" of the test expression follows one space before the curly brace "{"
else and elseif
are on the same line as their corresponding curly braces
Example A.15. if statements
if ($something || $somethingElse) { doThis(); } else { doSomethingElse(); } // one liners - exception for throw statements! if (allGoesWrong() === TRUE) throw new \Exception('Hey, all went wrong!', 123); if (weHaveALotOfCriteria() === TRUE && notEverythingFitsIntoOneLine() === TRUE || youJustTendToLikeIt() === TRUE) { doThis(); } else { ... }
In a nutshell: before coding a feature or fixing a bug, write an unit test.
Whatever you do: before committing changes to the repository, run all unit tests to make sure nothing is broken!
To have a clear and focused history of code changes is greatly helped by using a consistent way of writing commit messages. Because of this and to help with (partly) automated generation of change logs for each release we have defined a fixed syntax for commit messages that is to be used.
Never commit without a commit message explaining the commit!
The syntax is as follows:
One line per "issue". If the change breaks things on the user side, start the line with
A breaking change that needs human action when updating.
Then followed by one or more of the following codes:
A feature change. Most likely it will be an added feature, but it could also be removed. For additions there should be a corresponding ticket in the issue tracker.
A fix for a bug. There should be a ticket corresponding to this in the issue tracker as well as a (new) unit test for the fix.
An API change, that is methods have been added or removed; method signatures or return types have changed. This only refers to the public API, i.e. methods tagged with @api (currently we still use it inverted, everything not in the public API is tagged @internal).
Some configuration change. That could be a changed default value, a new setting or the removal of some setting that used to exist.
Anything not covered by the above categories, e.g. coding style cleanup. Usually only used if there's no corresponding ticket.
The codes are prefixed by one of the following modifiers:
Something has been added: a new method, configuration setting or feature. For bug fixes it means the bug was positively fixed (fixes the ticket).
Something has been removed. For bug fixes it means the big fix has been reverted (need to re-open the ticket).
Something has been changed. For bug fixes it means some change related to the issue, possibly even after it was closed.
The prefix is followed by the package key and (in round brackets) the subpackage if applicable. For FLOW3 the subpackage corresponds to the category in the issue tracker.
Then follows a custom message explaing what was done. It should be written in a style that serves well for a change log read by users. In case of breaking changes give a hint on what needs to be changed by the user.
If a corresponding ticket exists, mention it's ticket number and use the following actions if applicable:
<number>If the change fixes a bug.
<number>If the change resolves a feature request or task.
<number>If the change relates to an issue but does not resolve or fix it.
What follows are some examples showing the syntax in various combinations.
Example A.16. Some commit messages following the rules...
[!!!][-API] FLOW3 (Core): The Bootstrap's run() method has been removed. Please don't run FLOW3, write your own framework. Resolves #1, relates to #2.
[+FEATURE] FLOW3 (AOP): Added foobar interceptors to AOP. This allows for some awesome buzz when baz hits the foo. Resolves #3.
[+BUGFIX] SomePackage (SomeSubpackage): Bar no longer breaks when Baz is used as input, fixes #4.
[~TASK] OtherPackage: Removed trailing whitespace in all source files.
[+API][+FEATURE] FLOW3 (Core): Method initializeDrupal() has been added to the Bootstrap
All code must be documented with inline comments. The syntax is similar to that known from the Java programming language (JavaDoc). This way code documentation can automatically be generated using PHP_UML.
A file contains different documentation blocks, relating to the class in the file and the members of the class. A documentation block is always used for the entity it precedes.
Classes have their own documentation block describing the classes purpose, assigning a package and subpackage. Very often the code within a class is expanded and modified by a number of authors. We therefore recommend to add the names of the developers to the method documentation. An exception should be the documentation for interfaces where you list all authors in the interface documentation. Exceptions itself never have an author annotation.
Example A.17. Standard class documentation block
/** * First sentence is short description. Then you can write more, just as you like * * Here may follow some detailed description about what the class is for. * * Paragraphs are seperated by a empty line. * * @version $Id$ * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public ↩ License, version 3 or later */ class SomeClass { ... }
Example A.18. Standard interface documentation block
/** * First sentence is short description. Then you can write more, just as you like * * Here may follow some detailed description about what the interface is for. * * Paragraphs are seperated by a empty line. * * @version $Id$ * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public ↩ License, version 3 or later * @author Your Name <your@email.here> */ interface SomeInterface { ... }
Example A.19. Standard exception documentation block
/** * First sentence is short description. Then you can write more, just as you like * * Here may follow some detailed description about what the exception is for. * * Paragraphs are seperated by a empty line. * * @version $Id$ * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public ↩ License, version 3 or later */ class SomeException extends \Exception { ... }
The $Id$ in the version tag will be expanded with information
about the file version by Subversion. This so-called keyword
expansion needs to be explicitly enabled, though! We recommend to
put this into your ~.subversion/config
file:
Example A.20. Suggested configuration for Subversion in
~/.subversion/config
[miscellany]
global-ignores = #*# *.rej *.orig *.bak *~ .*
log-encoding = utf-8
enable-auto-props = yes
[auto-props]
*.php = svn:keywords=Id Revision
*.yaml = svn:keywords=Id Revision
This does a little more than just enable the keyword expansion, it also sets the character encoding for the log messages and makes Subversion ignore some standard backup and metadata filenames.
Additional tags or annotations, such as @see or @aspect, can be added as needed.
Properties of a class should be documented as well. We use the short version for documenting them:
Example A.21. Standard variable documentation block
/** * A short description, very much recommended * @var string */ protected $title = 'Untitled';
For a method, at least all parameters and the return value must be documented. Please also add your name by using the @author tag. The @access tag must not be used as it makes no sense (we're using PHP >= 5 for some reason, don't we?)
Example A.22. Standard method documentation block
/** * A description for this method * * Paragraphs are seperated by a empty line. * * @param \F3\Blog\Domain\Model\Post $post A post * @param string $someString This parameter should contain some string * @return void * @author Your Name <your@email.here> */ public function addStringToPost(\F3\Blog\Domain\Model\Post $post, $someString) { ... }
A special note about the @param tags: The parameter type and name are seperated by one space, not aligned. Do not put a colon after the parameter name. Always document the return type, even if it is void - that way it is clearly visible it hasn't just been forgotten (only constructors never have a @return annotation!).
Not all methods with a public visibility are neccessarily part of the intended public API of a project. For FLOW3, only the methods explicitly defined as part of the public API will be kept stable and are intended for use by developers using FLOW3. Also the API documentation we produce will only cover the public API.
When something in a class or an interface is annotated with
@api make sure to also annotate the class or
interface itself! Otherwise it will be ignored completely when
official API documentation is rendered!
To mark a method as part of the public API, include an @api annotation for it in the docblock.
Example A.24. Defining the public API
/** * This method is part of the public API. * * @return void * @author Your Name <your@email.here> * @api */ public function fooBar() { ... }
There are not only documentation annotations that can be used. In FLOW3 annotations are also used in the MVC component, for defining aspects and advices for the AOP framework as well as for giving instructions to the Persistence framework. See the individual chapters for information on their purpose and use.
Here is a list of annotations used within the project. They are grouped by use case and the order given here should be kept for the sake of consistency.
Interface Documentation
@version
@license
@author
@api
@since
@deprecated
Class Documentation
@version
@license
@api
@since
@deprecated
@entity
@valueobject
@scope
@aspect
Property Documentation
@var
@introduce
@uuid
@identity
@transient
@lazy
@api
@since
@deprecated
Constructor Documentation
@param
@throws
@author
@api
@since
@deprecated
Method Documentation
@param
@return
@throws
@validate
@dontvalidate
@author
@signal
@api
@since
@deprecated
@pointcut
@afterreturning
@afterthrowing
@around
@before
Testscase Documentation
@test
@dataProvider
@expectedException
@author
Additional annotations (more or less only the
@todo and @see come to mind
here), should be placed after all other annotations.
This section gives you an overview of FLOW3's coding rules and best practices.
FLOW3 makes use of a hierarchy for its exception classes. The general rule is to throw preferably specific exceptions and usually let them bubble up until a place where more general exceptions are catched. Consider the following example:
Some method tried to retrieve an object from the object manager.
However, instead of providing a string containing the object name, the
method passed an object (of course not on purpose - something went
wrong). The object manager now throws an
InvalidObjectName exception. In order to catch
this exception you can, of course, catch it specifically - or only
consider a more general Object exception (or an
even more general FLOW3 exception). This all
works because we have the following hierarchy:
+ \F3\FLOW3\Exception
+ \F3\FLOW3\Object\Exception
+ \F3\FLOW3\Object\Exception\InvalidObjectNameException
When throwing an exception, make sure to provide a clear error
message and an error code being the unix timestamp of when you write
the throw statement. That error code must be
unique, so watch out when doing copy and paste!
For every exception there should be a page on the TYPO3 wiki, as exception messages link to that page, identified by the error code (unix timestamp).
Some notes for a start:
Never use the object manager or factory in unit tests! If they are needed, mock them.
Avoid tests for the scope of an object. Those tests test the object factory, rather then the test target. Such a test should be done by checking for the presence of an expected @scope annotation – eventually we will find an elegant way for this.
When concatenating paths, always use
\F3\FLOW3\Utility\Files::concatenatePaths()
to avoid trouble.
All code should be object oriented. This means there should be no functions outside classes if not absolutely necessary. If you need a "container" for some helper methods, consider creating a static class.
All code must make use of PHP5 / PHP6 advanced features for object oriented programming.
Use PHP namespaces (see http://www.php.net/manual/language.namespaces.php)
Always declare the scope (public, protected, private) of methods and member variables
Make use of iterators and exceptions, have a look at the SPL (see http://www.php.net/manual/ref.spl.php and http://www.php.net/~helly/php/ext/spl/)
Make use of type-hinting wherever possible (see http://www.php.net/manual/language.oop5.typehinting.php)
Always use <?php as opening tags (never only
<?)
Always use the closing tag ?> at the end of a
file, don't leave it out (this ain't no Zend Framework, dude)
Add an encoding declaration as the first line of your PHP code, followed by the namespace declaration. For TYPO3 the encoding must be UTF-8
Example A.25. Encoding and namespace statements for .php files
<?php declare(ENCODING = 'utf-8'); namespace F3\Your\Stuff\Here; ...
Prefer strict comparisons whenever possible, to avoid problems with truthy and falsy values that might behave different than what you expect. Here are some examples:
Example A.26. Examples of good and bad comparisons
if ($template) // BAD if (isset($template)) // GOOD if ($template !== NULL)) // GOOD if ($template !== '')) // GOOD if (strlen($template) > 0) // BAD! strlen("-1") is greater than 0 if (is_string($template) && strlen($template) >0) // BETTER if ($foo == $bar) // BAD, avoid truthy comparisons if ($foo != $bar) // BAD, avoid falsy comparisons if ($foo === $bar)) // GOOD if ($foo !== $bar)) // GOOD
Order of methods in classes. To gain a better overview, it helps if methods in classes are always ordered in a certain way. We prefer the following:
constructor
injection methods
initialization methods (including initializeObject())
public methods
protected methods
private methods
shutdown methods
destructor
Avoid double-negation. Instead of
exportSystemView(..., $noRecurse) use
exportSystemView(..., $recurse). It is more
logical to pass TRUE if you want recursion
instead of having to pass FALSE. In general,
parameters negating things are a bad idea.
In general, comments are a good thing and we strive for creating a well-documented source code. However, inline comments can often be a sign for a bad code structure or method naming.[12]As an example, consider the following code:
Example A.27. Bad coding smell: Comments
// We only allow valid perso if (is_object($p) && strlen($p->lastN) > 0 && $p->hidden === FALSE && ↩ $this->environment->moonPhase === MOON_LIB::CRESCENT) { $xmM = $thd; }
This is a perfect case for the refactoring technique "extract method": In order to avoid the comment, create a new method which is as explanatory as the comment:
Bottom line is: You may (and are encouraged to) use inline comments if they support the readability of your code. But always be aware of possible design flaws you probably try to hide with them.
[12] This is also referred to as a bad "smell" in the theory of Refactoring. We highly recommend reading "Refactoring" by Martin Fowler - if you didn't already.