General Coding Guidelines

Publicado el 11/6/2010 por Andrzej Tucho?ka Lead Code Architect

1. Names

Proper naming convention is crucial to achieve readability of the code. If you don't create a proper name for the class/method/property, you can be sure that the next person trying to read your code will start asking stupid questions. Worst case scenario includes yourself analyzing your own source code and trying to figure out what you had in mind while writing this.

Remember, creation of naming convention is not only meant to increase code readability. It will ensure that the implementation is semantically correct and while the semantics match, our brains will be able to easily and intuitively understand the structures incorporated in the source code along with relations between different parts of the system. Also, when you switch the sides of this equation, you'll be able to base on your speech analysis to define and check the correctness of the source code.

1.1. Good naming practices

You have to create names basing on the context in which the code is being implemented. This means that, you need to visualize a way the class, method or property names will be used and name them accordingly. Calls made using your names have to make sense in a semantical way. Also, since PHP is not handling namespaces, you need to carefully pick names that will fit both the design and that will be as much specific as possible (to prevent repetitions).

This is a list of things you need to have in mind while trying to come up with a good name for your code:

* Never repeat the class name in the names of it's methods. * Always write in English. If you don't know the proper English word, consult the dictionary or a native English speaker, both you and the code will benefit from it. * Name things precisely. If you can't think of a precise name, you probably need to rethink code design. * Avoid using more then 3 words to describe the subject. Again, if you can't do that, you probably need to rethink code design. * Class and property names have to be nouns and methods have to be verbs. * In method names don't be afraid to use prefixes (is, get, set) and suffixes (Max, Count, Value). * When describing a collection of entities, always use plural, in all other cases use singular version.

A common misunderstanding arises, when developers derive the name of the subclass directly from its parent. It's not a big issue, but decreases readibility of the source code and therefore is undesired. Imagine, that in the below example instead of naming the class "Message" we'd use "MessageDataObject?". It doesn't mean that you can't reuse part of the parent class' name in the child. Just think if it makes sense and another developer will know why did you have added a specific suffix/prefix to the class name. Remember, that class names should be self-sufficient and precise in describing the purpose of the class.

And above all remember: all names have to be descriptive and accurate in describing the purpose of the source code.

Examples:


class Message extends BasicDataObject {

public function getTitle() { // ... }

private function isOffensive() { // ... }

private function getReceivers() { // ... } }


1.2. How to write a name

Always use camel caps to separate words in the name. Class names may only contain alphanumeric characters. Numbers are permitted in class names but are strongly discouraged. If you have a neat chance of using a "pro-abbreviation", skip it. Write all words capitalizing only the first letter.

For the first letter of code entity, different rules apply:

* Class names: first letter should be uppercase. * Method names: first letter should be lowercase. * Field names: first letter should be lowercase. * Method arguments: first letter should be lowercase. * Properties (virtual): first letter is assumed to be uppercase.

Examples:


class MessagePrinter extends BasicPrinter {

private $messageToPrint;

public function __construct(Message $message) { $this->messageToPrint = $message; }

public function printTitle() { // ... } }


1.3. Exception: constants

All constants should be written using all caps. Words should be separated with underscore "_".

Examples:


class MySqlConnection extends Connection {

const ERROR_SERVER_CONNECTION = 0; const ERROR_DATABASE_CONNECTION = 1;

// ...

}

In case of constants defining rate limiting, their names should clearly indicate not only the subject of rate limiting, but also the boundaries. This should be acheived using suffixes in constant names. These suffixes include:

* ..._PERCENT - meaning the scope is defined in range 0..100 with 0 meaning none and 100 all. * ..._PERMIL - meaning the scope is defined in range 0..1000 with 0 meaning none and 1000 all.

It is also expected that in constants you will use standard suffixes like:

* ..._PER_HOUR, ..._PER_MINUTE, ..._PER_SECOND etc. - define constant number placed in time context * ..._PER_USER, ..._PER_CONNECTION etc. - define a constant number placed in entity context

If you find a situation when you lack a standard suffix for this use, contact with the content owner to expand the list accordingly.

1.4. Literal values

Don't use them. Use named constants for any literal value other than obvious and special cases. Basically, you can check if an array has 0 elements by using the literal 0. But you should not assign a special meaning to a number and then use it everywhere as a literal. Also, accessing array value, you should use either a constant or a variable. The constants TRUE and FALSE should be used rather then literals 1 and 0. Typecast variables where it is needed, do not rely on the correct variable type (PHP is currently very loose on typecasting which can lead to security problems if a developer does not have a very close eye to it).

Examples:


class User {

const EMPTY_PASSWORD = 0; const INVALID_PASSWORD = 1;

public function setPassword($newPassword) { if (!empty($newPassword)) { $isPasswordValid = $this->checkPassword($newPassword); if ($isPasswordValid) { // ... } else { throw new CantChangePassword(self::INVALID_PASSWORD); } } else { throw new CantChangePassword(self::EMPTY_PASSWORD); } } }


1.5. Loop variables

In the most unlikely event, when you will be forced to define a "for" loop you should use variables i, j, k for consecutive loop levels. This kind of variables should also be applied when using loop indexing in while and do..while loops.

If you happen to get over 3 levels of loops, you should seriously reconsider your code design.

Examples:


// ...
    while ($i messagesCount) {
        $deletedMessageId = $this->deleteMessage($i);
        if ($deletedMessageId !== FALSE) {
            $this->log(sprintf(LANG_DELETED_MESSAGE_ID, $deletedMessageId));
            for ($j = 0; $j observersCount; ++$j) {
                $this->observers[$j]->refresh();
            }
        } else {
            $i++;
        }
    }
// ...

Due to the ease of exchanging for and foreach loops, it is required to:

* use foreach when keys or values that you're going to iterate with are semantically meaningful * use for when values have no other meaning then being the n-th item in the set

1.6. Standard names

Methods of the classes that will be written will have a set of similar prefixes and/or suffixes. It is important that they mean the same thing in the whole code and correspond to these definitions:

* assert... - defines an assertion method that will check the correctness of the concept that is being asserted. For example to assert that the variable $field actually contains a field. A call to the assertField($field) will break the execution if the value passed in the parameter is not a field. Assertions always throw exceptions on failure. * assure... - defines a method that will validate and fix if neccesary the variable given by the parameter. It has to return a fixed value, or throw an exception if it is not possible to fix it. For example a call to assureFieldIsLoaded($field) should check if the field has all the data that can be loaded, if that is not the case load it and return a fixed $field variable. * is... - performs the check on parameters and returns a TRUE / FALSE result. Does not throw any exceptions or break execution. Using above example, method isField($field) will return TRUE if the variable $field contains a valid field object and FALSE otherwise. * get... - just make sure that the name actually corresponds to what is returned. For example if one needs an id of the user it can be either getUser()->getId() or getUserId(). * set... - just make sure that the name actually corresponds to what is being changed. * can... - returns a boolean result with answer to a question asked in the method name. For example: canAddField($field) will return TRUE if the field can be added to the object. * as... - this prefix should be followed by the type/class name that will define a format in which the data of the object will be returned. For example: asArray(), asString. * from... - this prefix should be followed by the type/class name that will define an input format. Method will replace internal fields of the object basing on data provided to the method. Examples: fromArray, fromString. * has... - method has to return a boolean result that will provide information if the object(usually representing a set) contains/has an item basing on a given search item type (shown in the 2nd part of the method name) is found for a given value (provided via a parameter).

You should also join prefixes into forms like getBy..., getFrom... etc.

1.7. Directory names

Folder names should ideally contain only one word, but in case it's impossible use "-" to separate the words in the name.

2. Coding practices

2.1. Scoping variables

Both, the effectiveness and readability of the source code requires developers to use proper variables handling in their source code. Each of the variables has a lifespan defined within the scope of a method that defines it. As long as the class fields are tied into the existence of an object, the regular variables are tied into the methods they're defined in. Defining a reasonably sized method (in terms of lines of code) also has a positive impact on the amount of memory used in each execution of the method.

The variable usage can and will overlap, but we can optimize this by moving parts of the code into separate private methods that will narrow the scope of availability of the value to the part where it is actually needed.

2.2. Lazy developer effect

Don't be lazy writing the source code. Always make it complete in terms of error handling to the extent of your current knowledge. Actively fight the temptation of leaving something "as-is" and if you see a way of improving, do it. The benefit of this approach not only affects the source code, but also yourself. With time, the good practice will be an obvious thing to do from the beginning and your coding experience will be much more satisfactory.

2.3. Passing data through parameters

Do not use arrays to pass many parameters into a method. Instead of that, personalized collections should be implemented to handle groups of objects. If you need to define much more complex way of handling things within a method that requires to pass many input parameters to it, that means that you should define a specialized class to handle that kind of routines.