PHP Coding Standard

Posted on 7/30/2010 by Andrzej Tucho?ka Lead Code Architect

Notice:

In most of the examples the rule of writing comments has been omitted due to consistency and clarity of this document.

1. General rules

This document includes all rules defined in a General Coding Standard

2. Code layout

All of us highly value our time, so we want the results of your work (especially when confronted with other people) to comply with the accepted quality standards. This is meant to assure that you don't build a castle that is accessible only by yourself. Guides defined in this chapter address not only the quality of the visual side of the source code, but also allow us to avoid common errors that are created by poorly structured source code. It is our intention to create a friendly and healthy work environment, so it is neccessary to communicate with each other in an elegant way.

2.1. Brackets { }

Always include braces in the source code. Even if the body of some construct is only one line long, do not drop the braces. Opening braces never go on their own line and are separated by a single space from previous block. Closing braces always have their own line, except for if..else and try..catch statements where else and catch are preceeded by closing and succeeded by opening brackets.

Examples:


class Parser {

public function execute() { if (...) { foreach (...) {

} } else {

} } }


2.2. Brackets ( )

Direct usage of parentheses is allowed only after methods. Keywords (like if, while...) should be separated with a space from the opening bracket.

Note that when instantiating a class, we are actually calling its constructor method, so this is the only exception from rules defining parentheses usage.

You are not allowed to use parentheses with the "return" statement.

Examples:


class TuentiControl {

// ...

public function draw() { if ($this->isVisible) { foreach ($this->elements as $drawableElement) { $drawableElement->draw(); } } } }


2.3. Logical operators

All operators need to be separated from corresponding values with spaces. Also, the equation defined by usage of every single operator has to be enclosed within parentheses.

Above rules do not apply to operator NOT (!). This operator should be preceded with a space and should be applied directly before the value to be negated. Operator NOT does not have to be preceeded with space if there is an opening parenthesis just before it.

You should only use C-style operators (&&, ||).

Examples:


// ...
    if (($this->isVisible && !$this->isEmpty) || $this->forceDraw) {

} // ...


In case of the need to break the line, the line break should be done on the operator and the operator should start the next line. Next line that continues expression from the previous one, always has to be be indented one more level (with regards to general indenting rules).

2.4. Quotes

You should always use single quotes unless you specifically need variable interpolation to be done on that string. Remember that all literals should be used in conjuction with constants. Double quoted strings will be evaluated/parsed by PHP creating an unnecessary performance decrease.

Examples:


class Image {

const LOG_IMAGE_ERROR = 'Image error'; const LOG_IMAGE_WARNING = 'Image warning'; const LOG_IMAGE_NOT_STORED = "Image could not be stored.\nFull error information:\n%s";

// ... } catch (ImageNotStoredException $e) { $this->logService->noteThis(Image::LOG_IMAGE_ERROR, sprintf(Image::LOG_IMAGE_NOT_STORED, $e->getMessage())); } // ... }


2.5. Shortcuts

  • Operators - it is allowed to use a shortcut operator (++, --) only in separate line and for loops.
  • Conditionals - it is allowed to use short form of conditionals only in assignments that are one-liners.
  • PHP tags - it is allowed to use only this form of <?php tag. All other forms are strictly forbidden.
  • strings concatenation - it is allowed to use "." operator for string concatenation. Always add a space before and after the operator. It is allowed to break the lines while concatening strings, but in this case each new line has to start with "." operator (as with all operators).

Examples:


class ChatRoom {

// ...

public function getUserList() { $output = (isset($this->userList)) ? $this->userList->getString() : Lang::EMPTY;

return Lang::USER_LIST_TITLE . Lang::USER_LIST_COLUMNS . $output; } }


2.6. Indenting

Use an indent of 1 tab character. Do not align your code other then adding additional indentation level. It is advised to have editors configured to expand a tab character to the width of 4 spaces. All additional tools displaying source code will have this configured. The target line length is 80 characters; i.e., developers should aim to keep code as close to the 80-column boundary as is practical. However, longer lines are acceptable. The maximum length of any line of PHP code is 120 characters.

In a special case when there is a need to break the line within a logical equation, it should be indented by one more level. In this case each new line should be started by a logical operator that should be applied to the equation.

When passing arguments to a method, they must be separated by comma-space (', ').

Examples:


class Foo {

// ...

public function Bar($name, $surname) { $foo = ''; if ((isset($name) && (count($name) > Foo::MIN_NAME_LENGTH) && (count($name) forceNamePrint && ($name != Foo::EMPTY_NAME))) {

$foo = $this->prefix.$name; } return $foo; } }


2.7. Code blocks

This subchapter defines a standard to write code blocks. It contains mostly self-explenatory examples of specific blocks of code with comments where neccessary.

2.7.1. switch..case

Do not enter too many routines in each case statement. switch..case is a control block, not a group block. Usually we should limit it to 4-5 commands, but it cannot exceed 15 lines per switch. Since this block will be contained inside the methods, see also a point about defining proper method for additional size limits.

The default case has always to be defined and the break statement has to have the same indentation level as the source code.

Examples:


// ...
    switch ($entityType) {
        case Entity::TYPE_LOG:
            $this->logInformation($value);
            break;

case Entity::TYPE_FILE: $this->deleteFile($value); break;

default: throw new EntityTypeNotDefined($entityType); break; }


Falling through case statements is strongly discouraged except where several options share the exact same code. If you have few similarities in your case statements, most of the times an additional method encapsulating these routines should be defined.

Examples:


// ...

case a: case b: case c: doStuff(); break;


2.7.2. if..

Examples:


// ...
    if ($this->isCallable()) {
// ...
    } else {
// ...
    }

// ...

if ($this->getPreviousItem() == $this->currentItem) { // ... } else if ($this->getPreviousItem()->getOrder() currentItem->getOrder()) { // ... } else { // ... }


All comparisons should be positive whenever possible.

Examples:


// Instead of:
    if (!($this->isDisabled())) {
        $this->render();
    } else {
        throw new RenderingDisabledControl($this->getId(), $this->getType());
    }

// Use: if($this->isDisabled()) { throw new RenderingDisabledControl($this->getId(), $this->getType()); } else { $this->render(); }


Do not implement checks like "if ($myVariable)" if $myVariable might not be a boolean value. Also, the variable has to be named with accordance to bool semantics ($is...) to make this even more clear.

It is strongly suggested to use a "else if" form instead of "elseif".

2.7.3. while

Examples:


// ...
    while ($this->getCount() > 0) {
// ...
    }

2.7.4. do..while

Examples:


// ...
    do {
// ...
    } while ($this->getCount() > 0);

2.7.5. foreach

Define iterators for collection classes that are being used in your code.

Examples:


class CommentsIterator implements Iterator
{
    private $commentsCollection;

public function __construct(CommentsCollection $collection) { $this->commentsCollection = $collection; }

public function rewind() { $this->commentsCollection->rewind(); }

public function current() { return $this->commentsCollection->getCurrent(); }

public function key() { return $this->current()->getId(); }

public function next() { $this->commentsCollection->next(); return $this->current(); }

public function valid() { $isValid = ($this->current() !== false); return $isValid; } }

// ...

foreach ($myComments as $id => $comment) { // ... }


2.7.6. class

Do not write code that is not encapsulated within a class.

As a rule each class should be created within a separate file. It is acceptable to create proper Exception classes inside the class "owning" them. See exception handling for more details on exceptions.

2.7.7. method

Do not write routines that are not encapsulated in methods.

A public method should preserve the class invariants of the object it is associated with, and should always assume that they are valid when it commences execution (private methods do not necessarily need to follow this recommendation). To this effect, preconditions are used to constrain the method's parameters, and postconditions to constrain method's output, if it has one. If any one of either the preconditions or postconditions is not met, a method may raise an exception. If the object's state does not satisfy its class invariants on entry to or exit from any method, the program is considered to have a bug.

A size (in lines) of the method content should not exceed 25 lines of code.

Examples:


class Foo {

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

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

protected function execute() { // ... } }


See Proper method code for more information on methods.

2.7.8. try..catch

If your code can fix the exception, then always encapsulate unsafe code in a try..catch clause and handle all exceptions that can be thrown by enclosed routines. Do not catch base exception class, but provide specific behavior for each type of exception. Exceptions are meant to communicate the developer information about the problem, not to hide information about an error from end-user. This is why we need to be specific when throwing and catching exceptions. Also, if a try..catch clause encloses too much of the code it usually means your code design is probably broken.

Remember that, the catch clause always has to be sorted from most detailed (in terms of inheritance) exception to most general one.

Examples:


// ...

try { // ... } catch (MySqlException $e) { // ... } catch (DbException $e) { // ... } catch (Exception $e) { // ... } // ...

/** * This is a general exception for all mysql related errors * * @author Andrzej Tucholka */ class MysqlException extends DbException { private $errorMessage = 'A general mysql error occurred';

public function __construct($message = NULL) { parent::__construct(($message !== NULL)? $message: $this->errorMessage, ExceptionCodes::STORAGE); } }


2.8. Documentation and comments

It is our intention to create a preconfigured development environment that will help developers in documenting their source code.

Consider your comments a story describing the system. Expect your comments to be extracted by a robot and formed into a manual page. Class comments are one part of the story, method signature comments are another part of the story, method arguments another part, and method implementation yet another part. All these parts should weave together and inform someone else at another point in time just exactly what you did and why. Do not swear in comments or use words that are considered vulgar.

Comments shoud document decisions you made while writing the code. See missing code for a good example of that kind of decisions.

All documentation blocks ("docblocks") must be compatible with the phpDocumentor format. For more information, visit phpDocumentor webpage.

Some general rules about writing comments:

  • The sharp, '#', character should not be used to start comments.
  • Each comment has to be indented to the left.
  • Always write comments before the statement.
  • Code commented out should be deleted before merging back into the main branch.
  • Don't commit commented out code without writing a reason why it shoudl stay there

If a method or a class is deprecated from a specific point in time onwards, a leading comment has to be expanded by @deprecated command containing information about the date from when this code is deprecated, the deprecating reason and should point to the new valid code.

Every file that contains PHP code must have a header block at the top of the file that contains these information (add parameters if they apply):

Examples:


/**
 * <>
 *
 * LICENSE: This file can only be stored on servers belonging to Tuenti Technologies S.L.
 *
 * @copyright 2008, (c) Tuenti Technologies S.L.
 * @author Andrzej Tucholka
 * @package General
 * @subpackage Config
 * @todo <>
 */

If there are multiple people working on the file, list each author in a separate doc-param providing general scope of responsibilities in addition to your name.

Every class must have a docblock that contains these information (add parameters if they apply):

Examples:


// ..

/** * <> * * @author Andrzej Tucholka * @todo <> */


If there are multiple people working on the class, list each author in a separate doc-param providing general scope of responsibilities in addition to your name.

Every method must have a docblock that contains these information (add parameters if they apply):

Examples:


// ..

/** * <> * * @author Andrzej Tucholka * @throws <> * @param TypeOfParameter NameOfTheParameter Comment * @return TypeOfReturn NameOfReturn Comment * @todo <> */


If there are multiple people working on the method, list each author in a separate doc-param providing general scope of responsibilities in addition to your name.

If your method is not returning any result and therefore it's result should be discarded your documentation tag should be @return void

It is possible, that the return values of your methods will not be simple types nor objects. In case of arrays you should apply more detailed rules for describing your possible return values. Below you'll find examples demonstrating how the TypeOfParameter? and Description should be expanded to provide full information for array based results.

Examples:


// ..

/** * Returned value is a array of objects with meaningless keys * @return User[] An array containing a list of banned users * * Returned value is an array of objects with meaningful keys * @return User[int] Format: array($userId => User) * * Returned value is an array of arrays * @return int[int][] Format: array($userId => array($inviteCount)) * * Returned value is an array of arrays * @return string[string][int] Format: array($userName => array($friendId => $friendName)) * * Returned value is an array of arrays with meaningful positions * @return string[string][int] Format: array($userName => array(BEST_FRIEND(0) => $friendName, WORST_FRIEND(1) => $friendName)) */


A special case of the method is one that we consider to be an entry point to the system. These methods need to have an extended description including additional tags documenting an entry point:

  • @epoint-public-action - Defines if the method is an entry point. If it's not, the tag is optional.
  • @epoint-changes-state - Description defines a scope of changes done to the user state
  • @epoint-privacy-control - Description defines a way in which the privacy check is being done on the entry point.
  • @epoint-summary - Describes the risks, how it should be tested and any additional information associated with the entry point.

Examples:


// ..

/** * Shows general canvas * * Three params are available to use by others controllers, in order to reuse * this action (p.e. photo edit) * * @epoint-public-action NO (this doc tag is optional but must go here when an action is public) * * @epoint-changes-state YES * - It deletes notifications when: a) you are viewing a new tagged photo where you appear in, * b) you are viewing a photo where you are tagged in and that photo has new comments * * @epoint-privacy-control YES * Possible cases, a photo can be viewed when: * - if current user is friend with the owner of the photo * - if current user has common friend with the owner and the owner allows friends-of-friends * to see his photos * - if owner has photos as public * - if current user is viewing a photo in a page and the page allows it * * @epoint-summary Displays a photo and receives a key which may be altered by attackers, * tests should pass wrong keys or privacy-closed photo keys. * More information: (you should provide useful information here when necessary) * * @author Mauricio Morales * @param Photo $item * @param boolean $performCleanUp defaults to true * @param boolean $doPreload specifies whether a preload is required or not * @return View */ public function viewPhotoAction($item = NULL, $performCleanUp = TRUE, $doPreload = TRUE) {

}


When commenting imported modules, you should provide a comment block describing the usage scope of that module (see below). In other cases - general imports etc, the comment block is not necessary.

Examples:


// ..

/** * <> * * @see */


Documenting class fields is optional, but should be used in all non-obvious cases - usually with arrays describing the contents of keys and values. In case of documenting the field use a doc block like this: Examples:


// ..

/** * Contains a database configuration $serverIp => $weight; where weight is used for load balancing * * @var array contains database configuration */ private $databaseConfig = array();


2.9. Packages/subpackages structure and usage

The names of packages and subpackages can be only one word containing only letters, digits and "_", "-", "[" or "]" characters.

If below structure is not sufficient, feel free to create new subpackages to address your documentation needs but keep the same naming style and update A-Team on the new packages.

  • Api
    • Exceptions
  • Control
    • Base

    • Interfaces
    • Exceptions
  • Domain
    • Base

    • Interfaces
    • Exceptions
    • ... name of the domain module
  • General
    • Config

    • Exceptions
  • Helpers
    • Domain

    • Storage
    • ... other groups by area
  • Storage
    • Mysql

    • Memcache
    • Sphinx
    • Queues
    • Cached-collections
    • Exceptions
    • ... other storage types
  • Tests
    • Base <-- all framework tests go into this subpackage

    • ... name of the domain/control module.

3. Coding practices

This chapter introduces practices that have to be respected within the source code. They aim in increasing safety of the code and its performance.

We hope to expand this list with time, so do not hesitate to submit new ideas to the content owner (defined on top of this document).

3.1. Lazy initialization

Do not do any real work in an object's constructor. Inside a constructor initialize variables only and only perform actions that can't fail and do not require too much time to compute. It is not our intention to always define open method. This situation should be only happening if the object needs to be prepared by performing some specific routines at runtime. Nevertheless, those routines cannot be put into the constructor.

Create an open() method for an object which completes construction. open() should be called after object instantiation.

Examples:


    class Device
    {
        const UNINITIALIZED_DEVICE_NAME = 'Uninitialized device name';

private $name = ''; private $type = 0;

public function __construct($deviceType) { $this->name = Device::UNINITIALIZED_DEVICE_NAME; $this->type = $deviceType; }

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


3.2. Proper method code

Methods are not designed to remember data. Each of them should work strictly on the arguments passed through parameters. It is acceptable for methods to access state of the object in which they are defined.

Method, being associated with a particular object, may access or modify the data private to that object in a way consistent with the intended behavior of the object. Consequently, rather than thinking "a method is just a sequence of commands", a programmer using an object-oriented language will consider a method to be "an object's way of providing a service" (its "method of doing the job", hence the name); a method call is thus considered to be a request to an object to perform some task.

Always define methods with lowest visibility possible, to ensure the clarity of object's interface. Remember, that after you define a method public, it is probably going to stay that way for a long time.

Try to avoid usage of PHP magic methods (get, set, call) due to performance reasons. It is acceptable to use them only in situations that bring a huge value to the clarity of the code, and where they are not called too frequently.

See method description for more information on methods.

3.3. Usage of continue and break

Continue and break should be used very cautiously.

The two main problems with these commands are:

  • They implicitly bypass the test conditions of the loops
  • They might bypass the increment/decrement expressions
  • They break the default code structure defined by regular expressions

Mixing continue with break in the same loop is a sure way to disaster. In general you are strongly discouraged from using these commands.

3.4. "Missing code"

When writing an empty code block, always insert a comment describing why this block is (and should continue to be) empty. This rule also applies to code that was intentionally omitted. If you see a possibility of the code to execute and decide to ignore it (because of some environment boundaries) write a comment about it so the reader of the code will have the same knowledge as you have.

3.5. Safe code

Communication between classes on the level of sending messages and polymorphic calls, should be properly secured. Each message sent from different class inheritance level or a different object should be checked for validity upon arrival. Also, calls to subclass routines have to be enclosed in exception handling routines to prevent unexpected termination of execution.

Examples:

abstract class BasicObjectFactory {

abstract protected function instantiateObject($className);

protected function getObject($objectType) { $result = FALSE; if ($objectType == BasicObject::PROTOTYPE_OBJECT) { try { $result = instantiateObject(BasicObject::PROTOTYPE_CLASS_NAME); } catch (CreateObjectInstanceException $e) { // If the object doesn't get created, return default value } }

return $result; } }


3.6. Floats

Don't use floating-point variables where discrete values are needed. Using a float for a loop counter is a great way to shoot yourself in the foot. Always test floating-point numbers as =, never use an exact comparison (== or !=).

It is typical that simple decimal fractions like 0.1 or 0.7 cannot be converted into their internal binary counterparts without a small loss of precision. This can lead to confusing results: for example, floor((0.1+0.7)*10) will usually return 7 instead of the expected 8, since the internal representation will be something like 7.9.

This is due to the fact that it is impossible to express some fractions in decimal notation with a finite number of digits. For instance, 1/3 in decimal form becomes 0.3.

If higher precision is necessary, the arbitrary precision math and gmp functions are available.

Also, it is important to remember about locale settings (0.1 vs 0,1) when converting floating values to strings.

Examples:


/*
 * Two floating point numbers should be considered equal if their absolute
 * difference does not exceed a certain value epsilon. Epsilon in this case
 * defines the precision of the comparison
 */
    $epsilon = 0.000001;

if (abs($firstFloat - $secondFloat) < $epsilon) { // It can be assumed that the numbers are equal. }


3.7. Usage of NULL

Remember that NULL is not a value. It should be treated and identified with unknown state rather then empty. This is especially important with SQL queries (check SQL Coding Standard for details).

Handling NULL in your code:

  • when declaring a variable that will be checked against NULL and is not set in the constructor, you should initialize it with NULL
  • when verifing if something isn't NULL always use a strict comparison

Examples:


//  ...
    private $targetFilter;

// ... if ($this->targetFilter !== NULL) { $this->targetFilter = new TargetFilter(); } // ...


3.8. Type hinting

The use of type hinting is encouraged where possible with respect to the module design.

Examples:


    class MyClass {

public function test(OtherClass $otherclass) { echo $otherclass->var; }

public function test_array(array $input_array) { print_r($input_array); } }


3.9. Require / include

If a module uses another module, then the using module is responsible for loading the other one. If the use is conditional, then the loading should also be conditional.

If the file(s) for the other component should always load successfully, regardless of input, then use PHP's require_once statement.

The include, include_once, require, and require_once statements should not use parentheses.

Defined paths for including/requiring modules should always start with LIB_PATH constant to avoid lookups during the execution. If you are including/requiring files that are placed in a different root directory, use different constant to define a full path pointing to the file.

In general you should use only use require_once. Other forms are permitted (with a strong preferrence of require), but only in special cases like in templating, configuration loading etc.

3.10. One statement per line

Each line of the code can contain only one statement. The exception from this rule are logical statements, that contain execution and logic substatements. Still, in this case it is required to preserve as much clarity as possible. Also, the usage of time-consuming execution substatements is highly discouraged in logical statements.

3.11. Execution path breaking

The only acceptable way to break your code execution in the middle of the routine is by throwing an exception. Do not put return in the middle of the method / code.

Examples:


// (BAD) Instead of:
    public function getCoolKids() {
        // ... do some stuff

if ($this->isDisabled()) { return FALSE; }

// ... do some more stuff

return $coolKidsArray; }

// (GOOD) Use: public function getCoolKids() { // ... do some stuff

if ($this->isDisabled()) { throw new CannotRetrieveDataFromDisabledSet(); }

// ... do some more stuff

return $coolKidsArray; }


3.12. Passing one/many items through a parameter

If the method you have just written can accept a parameter to be a single value or an array of these values, you need to assure that the method will perform properly in both cases. To assure that check if the variable that represents the input parameter is an array. If not, wrap in into one and proceed like in the case of an array. This approach is meant to assure that there is only one processing code in your method.

Examples:


//  ...

public function delete($ids) { if (!is_array($ids)) { $ids = array($ids); }

foreach ($ids as $id) { // ... do some stuff } }


3.13. Closing PHP tag

Do not use the closing PHP tag. Ever.

3.14. Profanity

Do not use improper language in your source code. Ever. It is advised to put in funny comments though.

4. Miscellaneous

This chapter describes general rules that didn't fit in previous chapters.

4.1. File extensions

All files containing code or being inserted into PHP files / parsed by the PHP have to have a ".php" extension.

4.2. Globals usage

Introducing global variables is forbidden.

4.3. Optimizations

Always try to optimize your loops if operations are going on at the comparing part, since this part is executed every time the loop is parsed through. For assignments a descriptive name should be chosen.

Examples:


    for($i = 0, $size = count($post_data); $i < $size; $i++) {
// ...
    }

Also, avoid using in_array() on huge arrays, and do not place them into loops if the array to check consist of more than 20 entries. in_array() can be very time consuming and uses a lot of cpu processing time. For little checks it is not noticable, but if checked against a huge array within a loop those checks alone can be a bunch of seconds. If you need this functionality, try using isset() on the arrays keys instead, actually shifting the values into keys and vice versa. A call to isset($array[$var]) is a lot faster than in_array($var, array_keys($array)) for example.

Golden rules of optimization:

  • First make it work, then make it fast. Beware of premature optimization.
  • Optimize only the slow stuff.

4.4. Common mistakes

This is a list of functions that are commonly mistaken to do something else. The list also includes the recommendation for function usage.

  • empty() - commonly mistaken due to different results with different content. Most of the time it will not be neccesary to use this function. Usage of this function is discouraged.
  • isset() - commonly misused to verify if the variable has a value. In case of standard variables check against a NULL should be performed. In case of array values, array_key_exists check should be performed. Do not use this function.
  • unset() - commonly misused to reset a variable. In case of standard variables assign a NULL value rather then unset it. Only use this function to remove an array value.
  • goto - that was introduced in PHP 5.3. Do not use it.
  • is_a - do not use at all. Leaves instances of objects in the process' memory

General Coding Guidelines

Posted on 6/11/2010 by 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.

Follow us