Skip to content
Sergey Chernyshev edited this page Apr 6, 2016 · 4 revisions

This document describes coding style for the project and is loosely based on PEAR coding standards.


Style Enforcement

Conformance with these guidelines should be automated - run

make code

to run an automated style linter to see what needs to be changed.


Indentation

All PHP code in the application must use tabs for indentation.

vim users can include following comment at the top of every PHP page, right after first opening PHP tag:

<?php
/* vim: set noexpandtab tabstop=8 shiftwidth=8 softtabstop=8: */

This will allow vim users to automatically enforce indentation of the code.

For formatting the page automatically using VI, use 1GVG= command sequence.


Comments

Complete inline documentation comment blocks (docblocks) must be provided. Further information can be found on the phpDocumentor website.

Non-documentation comments are strongly encouraged. A general rule of thumb is that if you look at a section of code and think "Wow, I don't want to try and describe that", you need to comment it before you forget how it works.

C style comments /* */ and standard C++ comments // are both fine. Use of Perl/shell style comments (#) is discouraged.


Control structures

Same as for PEAR project

Always use curly braces

Even if control block contains only one statement, always use curly braces around that statement.

@TODO Include detailed explanation for the benefits of this

No alternative styles

Alternative style for PHP control structures should never be used.


Use only classic PHP open tag

PHP has an option to enable a short open tag: <? and it's companion short echo tag <?=

While very convenient, it's not turned on by default and will likely be disabled on the servers people use.

Use <?php and regular echo command <?php echo instead of short open tag and configure your development environment not to accept short tags to avoid this bug sneaking in (it's very painful to remove them).


__DIR__

When you need to refer to another file within the project (e.g. to include it or read from disk), please use PHP __DIR__ magic constant (with dirname() function if necessary) to calculate the resulting path of the file.

For example to include file2.php located in the same folder:

require_once(__DIR__ . '/file2.php');

Going up the folder chain

In order to go up the folder chain, please do not use double dots (/../) to indicate parent folder, but use nested dirname() instead.

For example to include file3.php located one folder above current file:

require_once(dirname(__DIR__) . '/file3.php');

Objects, IDs and slugs

It is important to distinguish objects from object IDs and object IDs from object slugs in the code, in the database and in configuration files.

It is preferred to pass objects when passing from one class to another, but (ideally private) functions within the same class can pass object IDs or object slugs.

Object IDs MUST always be integers while object slugs MUST always be strings.

To allow for better code readability and to avoid confusion between objects, their IDs and their slugs, variables in the code and field names in database should use the following naming convention:

  • Object IDs must always have _id suffix, e.g. plan_id
  • Object slugs must always have _slug suffix, e.g. plan_slug
  • Variables without _id or _slug suffixes must always refer to objects, e.g. plan

When stored in the database, objects should be identified by ID or by slug.

If object IDs are defined in the code, named constants must be used to help readability, for example:

define('FREE_PLAN', 0);
...
Account::createAccount(FREE_PLAN, ...);

Slugs

Slugs should be URL-friendly, meaning that they should be the same as their URL-encoded version without resorting to %-encoding.

Slugs should not use camel case, but lower case latin letters and utilize dashes - (not underscores) in place of spaces.


Do not convert strings into variables

Do not convert strings into variables and vice versa, all variable assignments should be done through accessing methods that can have documentation with type definitions to allow autocompletion, static code analysis and etc.

This is very important for future debugging, tracing for source of errors and so on.

It's better to have more code and be explicit about your intentions then having smaller code that is harder to read and understand.

It's better to spend time adding more lines of code with type annotations and documentation when you need to add extra field then to spend more time trying to debug the issue and refactor the code to be self descriptive.

Do not overload __get() & __set() methods

Instead of using __get and __set magic functions, use explicit accessor (getter and setter) methods.

Best practice for read-only properties:

class MyWidget
{
     /**
      * @var string name Name of the widget
      */
     private $name;

     /**
      * @return string Name of the widget
      */
     function getName() {
          return $this->name;
     }
}

to allow for IDE autocompletion, automated configuration and so on.

Do not use extract() & compact()

PHP extract() function allows to convert arrays into variables which makes it impossible to track the origin of some variables, their scope and sometimes even dangerous when you don't control the source of data. Same applies to compact() function as result is usually used by extract().

For converting arrays of data into variables, instead of using a shortcut:

$my_data = array(name => 'John', age => 55, gender => 'male');

extract($my_data); // ambiguous, hides declaration of variables

echo $name . ' is ' . $age . ' years old ' . $gender;

use explicit setting of values as a best practice:

$my_data = array(name => 'John', age => 55, gender => 'male');

// verbose, but specific
$name = $my_data['name'];
$age = $my_data['age'];
$gender = $my_data['gender'];

echo $name . ' is ' . $age . ' years old ' . $gender;