Skip to content
thekid edited this page Apr 30, 2013 · 3 revisions

Note: These are the old coding standards, applicable for any existing source code using backwards-compatible un-namespaced code. See the new coding standards valid for namespaced code and RFC #0208 for the change.


Coding standards (Old)

Code conventions are important to programmers for a number of reasons:

  • 80% of the lifetime cost of a piece of software goes to maintenance.
  • Hardly any software is maintained for its whole life by the original author.
  • Code conventions improve the readability of the software, allowing engineers to understand new code more quickly and thoroughly.
  • If you ship your source code as a product, you need to make sure it is as well packaged and clean as any other product you create.
  • For the conventions to work, every person writing software must conform to the code conventions. Everyone.

Framework sourcecode

The coding standards for the XP framework classes are divided up into the following topics:

  • File layout - File naming, header and line length.
  • Naming - Classes, methods, fields, modifiers, variables and constants
  • Whitespace - Identing, blank lines, spaces and line wrapping
  • Comments - Block comments, single line comments, documentation comments
  • Literals - Numbers, special literals, strings, arrays
  • Statements - Control structures, loops, try / catch / throw
  • SQL - Modern web applications often have a database driven backend which can be used via the structured query language (SQL). Hence, we often find SQL statements within classes, in a script or in the source for a webpage. Just like a normal program, SQL also has to be written by many and must be understood by many, so we want to make sure SQL is written in a nice and clean way. It should - just like it is with program code - not be possible to tell who wrote the SQL just by looking at the code.
  • Further reading

File layout

The comment at the top of a file should contain the Id-Tag, that is at least $Id$ and a short description what's included. We recommend a C-notation style comment:

/* This class is part of the XP framework
 *
 * $Id$
 */

This comment is supposed to be written directly behind the opening <? php and is of exactly above form. The short description should not exceed one line and the Id-tag must be included before the first add / commit as $Id$ and just written like that.

Opening / closing tags

Next to <?php ... ?> there is no other form of opening tags allowed (e.g. <% %> or ?>). This form is XML-conform and thus future safe. <? will result in a "Not well formed" error message in any XML parser.

Filenames

Filenames look like following:

  • Classes Files containing a class have to be named like the class and always contain just one class. The suffix is .class.php. Examples: IOCollection.class.php, Object.class.php

  • Scripts These should be named with a sensible name which will be underlined and clearified by the path. Word separation should be done with the underline _. Examples: index.php, mail_notify.php.

Note: "Scripts" do no longer exist - every command is a class in XP Framework.

Line length

Lines with more than 80 chars should not be written. Examples for documentation purposes should even only contain no more than 70 chars (sending via E-Mail).

Example

This is the sourcecode of the IndexOutOfBoundsException class.

<?php
/* This class is part of the XP framework
 *
 * $Id: IndexOutOfBoundsException.class.php 8895 2006-12-19 11:54:21Z kiesel $ 
 */
  
  /**
   * Thrown to indicate that an index of some sort (such as to an array, 
   * to a string, or to a vector) is out of range.
   *
   * @purpose  Exception
   */
  class IndexOutOfBoundsException extends XPException {
  
  }
?>

Naming

Classes

Class names begin with an uppercase letter. Each following word again begins in uppercase ("Studly Caps"). Class names should be as short as possible while remaining significant for what the class does.

  class Raster { }
  class GenericTemplate { }

The same applies for interfaces and enums.

Static initializer block

The static initializer block is a special static method named __static.

  class Logger extends Object {
    protected static $instance= NULL;
  
    static function __static() {
      self::$instance= new self();
    }
  }

Constructors

A classes' constructor has to be named __construct(). Calling the parent's constructor when being overwritten is written as follows: parent::__construct().

Destructors

A classes' destructor is called __destruct() and does not take any parameter. Calling the parent's destructor is written as follows: parent::__destruct().

Methods

Methods should be verbs, with the first letter in lowercase. Every following word begins in uppercase.

  public function run() { ... }
  public function runBackground() { ... }
  public function getMessage() { ... }
  public function makeInstallation() { ... }

Exception: In generated DataSet classes, field accessors are generated from the database table's field names. They follow the rule that the method name is composed of the verb get or set and the field name with the first letter capitalized:

  public function getPerson_id() { ... }
  public function setPerson_id($person_id) { ... }

Interface methods and methods with the abstract modifier may not include a method body. The correct syntax is a semi-colon after the method declaration, without any whitespace:

  interface Filter {
    public function accept($e);
  }

  abstract class Command {
    public abstract function run();
  }

Modifiers

Method and fields modifiers should be given in the following order:

  [access] static abstract final

[access] is one of public, private or protected.

Examples:

  protected function __construct() { }
  public static function getInstance() { }
  public abstract function run();
  protected final function getMessage() { }

Variables

Variable names should be short and expressive and tell what they're there for. Prevent using variable names like $foo`, `$bar`, `$baz`, `$fuck and variable names with less than two chars. Exception: temporary one-way-variables like i,j,k,m,n (for integers) and c,d,e (for chars) for e.g. a counting variable. Names in variables should be divided by either mixed lower-/uppercase letters or by the underline (_).

  $myWidth;
  $i;
  $message_id;

Constants

Constants are written in uppercase letters only.

  define('TA_NOTFOUND',   0x0001);
  define('TA_OKAY',       0x0002);

If possible only define integers and preferably with an hexadecimal notation. Multiple defines keep their right part at the same indentation width.

Note

Use constants instead of integers or character return whereever possible. Instead of returning from a function with return 1, return "foo" or return -55, use constants - whereever possible. These should be defines preceding other definitions or declarations in a file (just following the opening comment). Exceptions are TRUE / FALSE which may be expressed by 0 or 1 (that is just common sence and is understandable for everyone).

Whitespace

Indenting

Indenting within one file must be unified, indenting width is two spaces. Tabs may never be used.

Blank lines

Blank lines improve the readability of code. A blank line should be inserted at the following situations:

  • Between methods
  • Between the static statement within a function and the first line of code of that function
  • Prior to a one-line- or a block-comment
  • Following the comment at the document's beginning
  • Between logical sections within a method to make code more readable and denote the belong-together sense of that piece of code.

Spaces

Spaces should be used in the following scenarios:

A keyword (if, else, while, for, ...), followed by a brace should be separated by a whitespace.

  while (TRUE) {
    ...
  }

Note: after a function call and their braces no whitespace shall be used. This helps distinguishing keywords and function calls from each other.

After the comment char of a one line (inline) comment

  // good
  //bad

After a comma in a list of arguments:

  $method->invoke(NULL, $arguments);    // Good
  $method->invoke(NULL,$arguments);     // Bad!

An array's key/value assignment has to be separated by a whitespace prior and following the =>.

The class instance and the class method or the class attribute have to be separated by ->, but never by any whitespace inbetween.

  $this->foo      // OK
  $this -> bar    // Unneccessary

Round braces may not be separated by whitespace

  while (($a > 0) && ($b < 1)) ...   // Correct
  while ( ($a > 0) && ($b < 1) ) ... // Unneccessary

All operations of (+ - * / % & | ~ && < > <= >= != == || etc.) should be separated by whitespaces at both sides. All operators in (++ -- etc.) do not have any preceding whitespace.

The string concatenation operator (.) is not preceded nor followed by any whitespace.

In a for-statement single expressions should be separated by whitespace.

  for ($init; $condition; $update) {
    ...
  }

Type-casting has no whitespace to the right side, but is directly connected to the casted variable.

  myMethod((int)$a, (array)$that);

Line wraps

When an expression does not fit into one line, it should be wrapped following these rules:

  • after a comma
  • before an operator
  • wrapped lines have to be indented

An example:

  $r= sprintf('<a href="http://%s:%d/%s/%s.bar" target="%s">%s</a>',
    $host, $port, $path, $file, $target, $anchor
  );

  $r= sprintf(
    '<a href="http://%s:%d/%s/%s.bar" target="%s">%s</a>',
    $host, 
    $port, 
    $path,
    $file,
    $target,
    $anchor
  );

In method declarations wrapped parameters should be indented as in the above sprintf example:

  public function reallyReallyLongFunctionNameWithParameters(
    $string, 
    $uriRequesterObject,
    $foobarbaz, 
    $longParamNameNumber4
  ) {
    ...
  }

(Note that you should resist to use overly long function names).

Long conditions in an if-statement should be indented this way:

  if (
    ('172.17.0.27' == $REMOTE_ADDR) ||
    ('172.17.0.26' == $REMOTE_ADDR) ||
    ('172.17.0.24' == $REMOTE_ADDR) ||
  ) {
    $group= 'root';
  }

In this example all conditions have been indented to the same column, but still remain separated from the "executing" code by one (almost) empty line.

Ternary expressions adhere to the following writing guidelines:

  $alpha= ($booleanExpression) ? $beta : $gamma;

  $alpha= ($reallyReallyLongbooleanExpression
    ? $beta 
    : $gamma
  );

Comments

In PHP three kinds of comments are supported: C-style /* */, C++'s // as well as Perl's # comment sign. From Java, we know the /** ... */ - documentational comment. We can use these to extract useful meta-information about the functions with the XP documentation system which was developed to supply an automated documentation system.

The # should be avoided, most other commments should be written using //. Commenting out multiline areas in a code part is no more possible as it will be rejected by the coding standards conformance checker script. Multiline commented out code most often is being used to test things. Tests, however, should not be committed. Via SVN an undo of a delete can be performed, so there just is no need for mutliline comments. Therefore comments should not be "drawn" in big comment boxes within /* ... */, a comment should always be located directly above the code line to be commented and explained.

The line above a comment has to be an empty line. Left and right side of a comment sign a space should be placed:

  // Correct
  $a= 0; // Correct as well, but just good and not "better"! :)
  $a= 0;     // Better
  
  //Avoid this!
  $a= 0;//Avoid this!

Block comments

Block comments may be used only to describe files (comments at the top of a file) - see File layout.

Single line comments

Single line comments must be written using //. Single line comments should be separated from the above parts by an empty line and keep the current indenting width:

  if ($condition) {
  
    // Fetching files here...
    ...
  }

If you need to comment more that just one line of code, the following notion is recommended:

  if ($condition) {
    ...
    
    // DEBUG
    // fsockopen('debug-srv', 0x9282);
    // ...
  }

For an even larger block, /* */ may be used while in development or for special cases (like temporary disabling of code portions). Before committing the files, remove it - that's what version control systems are for.

Comments at the end of a line

These comments have to be written using // for the same reasons as stated above. When multiple comments of this form exist in a code block, they should begin at the same indentation.

  if ($condition) {
    return FALSE;    // Reason
  }
  
  if ($condition) {
    $alpha= FALSE;                             // Reason
  } else {
    $alpha= substr($beta, 0, strlen($gamma));  // Why's that?
  }

Comments for documentation

A documentation comment starts with /** and ends with */.

/**
 * Documentation here
 * 
 * @see   xp://lang.Throwable
 */

Special comments

A FIXME: or TODO: at the beginning of a comment indicates some kind of unresolved problem at this place:

  // FIXME: A database error could possibly pop up here.
  // TODO: Implement improved backchecking

Furthermore, the following special words may be used:

  // HACK (fix this later)
  // TBD (to be discussed)
  // TBI (to be implemented)

Note: The frequency of comments sometimes reflects poor quality of code. When you feel compelled to add a comment, consider rewriting the code to make it clearer.

Literals

Numbers

Integer numbers should be written as follows:

  $number= 0;
  $number= -6100;
  
  $number= 0xFFFF;    // Hexadecimal notation

Floating point numbers follow this form:

  $number= 0.5;
  $number= -6.1;

The octal representation is to be avoided.

Booleans

The boolean values are written in upper-case:

  $t= TRUE;
  $f= FALSE;

Null

The special null literal is also to be written in upper-case:

  $n= NULL;

Strings

Strings should be written using single quotes. If a string must contain special escape sequences (\r, \n or \t), double quotes should be used. Variables in strings are to be prevented.

String concatenation vs. multiline strings

Especially for SQL, it is great that line breaks and whitespaces are ignores.

  $result= $dbo->query('
    select * from foo, bar
    where baz= 2
    and foo.i= bar.i
  ');

is easy to extend for example, easy to replace by anything else and just easy to copy'n'paste into an sql shell window (nonetheless, an additional indentation of two columns is being recommended).

  // Don't do this
  $result= $dbo->query('select * from foo, bar '.
    'where baz= 2 '.
    'and foo.i= bar.i '
  );

The preceding piece of code is more unreadable and difficult to reuse. And it is error prune when one forgets a whitespace at the end of each line (which leads to a syntax error by the DBMS).

Arrays

  $list= array();
  
  $list= array('Hello', 'World');
  
  $hash= array(
    'Bla' => 0,
    'Foo' => array('Bar', 'Baz'),
    'XP'  => 'Über-Framework'
  );

Hash keys

Always to be enclosed in quotes.

  echo $arr[helptext_domain_name];    // "undefined constant, assuming string"
  echo $arr['helptext_domain_name'];  // This makes the parser happy

Statements

One line should only contain one statement:

  $a++;           // Correct
  $b++;           // Correct
  $a--; $b--;     // Bad

Assignment and comparisons

Between an assignment of a variable and the value, a whitespace is at the right side of the assignment operator (=).

  // Assigning a variable to a constant
  $a= 'hello';
  
  // Assigning a variable to a method call's return value
  $instance= UserManager::getInstance();
  
  // Assigning variables
  $a= $b;
    
  // Creating an instance of a class
  $object= new Object();

When comparing, whitespace is on both sides of a comparison operator. This is supposed to prevent mixing up $a= 1 (an assignment) and $a == 1 (a comparison);

  if ($a == 'hello') ...
  
  // Even better, to prevent typos as well.
  if ('http://' = substr($uri, 0, 7)) ...   // Won't compile!
  if ('http://' == substr($uri, 0, 7)) ...

For arrays, their keys and values should be equally indented:

  $array= array(
    IT_STRING     => '<input type="text" value="%s"/>',
    IT_TEXTAREA   => '<textarea>%s</textarea>'
  );

Blocks

Blocks are enclosed in { and }.

  • Blocks have to be indented
  • The opening curly brace is placed at the end of the line beginning the block
  • The closing curly brace should be placed at the beginning of a line and be indented at the same width like the beginning of the block.
  • Every block must be enclosed in curly braces, even when you could obey it for single line statements. This decreases the danger of adding code and forgetting to add braces leading to breakage of the script as the added code will not execute.

Exceptions

There is an exception for statements in conjunction with a return / continue or break:

  $result= $dbo->select('...');
  if (!$result) return FALSE;
  
  foreach ($methods as $method) {
    if (!$method->hasAnnotation('inject')) continue;
    
    // ...
  }
  
  do {
    if (!$q->hasNext()) break;
    
    // ...
  } while ($r= $q->next());

Return

Return immediately leaves a function, thus instead of:

  public function open($file) {
    if ($fd= fopen($file, 'r')) {
        ...
    } else {
      return 0;
    }
  }

write:

  public function open($file) {
    if (!$fd= fopen($file, 'r')) return 0;
    ...
  }

Continue is a similar case:

  while ($entry= $dir->read()) {
    if('.' == $entry or '..' == $entry) continue;
    ...
  }

The ternary operator is, as far as possible, to be preferred to an if-else construct:

  // Unnecessary
  if ($condition == NULL) {
    return FALSE;
  } else {
    return TRUE;
  }

  // Better
  return ($condition == NULL) ? FALSE : TRUE;
  
  // Event better
  return ($condition != NULL);

When a binary operator (e.g. >=, <=, ==, etc.) occurs in a ternary expression, it should be enclosed in braces. A return statement should only use braces when they are neccessary or improve the readability (by grouping).

  return (FALSE);         // Bad
  return (isset($size)    // OK
    ? $size 
    : $defaultSize
  );

if, if-else, if else-if else Statements

if-else classes of statements should have the following form:

  if ($condition) {
    imageSize(...);
  }
  
  if ($condition) {
    imageSize(...);
  } else {
    imageCreate(...);
  }
  
  if ($condition) {
    imageSize(...);
  } else if ($condition2) {
    imageCreate(...);
  } else {
    imageDispose(...);
  }

for-Statements

A for-statement should have the following form:

  for ($init; $condition; $update) {
    doSomething();
  }
  
  for ($i= $dba->iterator(); $i->hasNext(); ) {
    $key= $i->next();
    // ...
  }

while-Statements

A while statement should have the following form:

  while ($condition) {
    doSomething();
  }
  
  do {
    doSomething();
  } while ($condition);

switch-Statements

A switch-statement should have the following form:

  switch ($condition) {
    case ENC_ISO_8859_1:
      $output= 'bla';
      $needsParsing= FALSE;
    
    case ENC_ISO_8859_2:
      $output= 'foo';
      break;
    
    default:
      statement();
      break;
  }

After the first case we suggest to add a comment to make clear no break is missing accidentally:

  switch ($condition) {
    case ENC_ISO_8859_1:
      $output= 'bla';
      $needsParsing= FALSE;
      // break missing intentionally
      
    case ENC_ISO_8859_2:
      // ...
      break;
  }

try/catch-blocks

A try/catch block should have the following form:

  try {
    doSomethinWhichMightRaiseAnException();
  } catch (WhatEverException $e) {
    handleException();
  } catch (XPException $e) {
    handleException();
  }

throw

Thrown objects are derived from the Throwable base class. It is encouraged not to directly inherit this class but one of the following:

  • XPException - for exceptions
  • Error - for error situation (stronger than exceptions)
  • ChainedException - for chained exceptions

A throw statement looks like this:

  throw new SQLException('Unable to connect');

SQL

This document gives an overview about a common way to write SQL, it is not a standard but a guideline (but should be complied with anyways).

SQL-Keywords

The XP database classes always prefix your queries with a SQL keyword whenever you use one of select, insert, update or delete, so you have to remove it from your query. This is simply to make sure noone writes an update within a select()-call. So, remove these keywords from the beginning of your statement.

Database API

The database API has been developed to provide an easy but powerful way to use a database. It has some special-purpose classes like e.g. Transaction which provides a way to control transactions. Use them!

Database classes

The framework allows for special classes that represent tables from a database. In many cases it may be better to use them than to write the plain SQL in the script.

Variable insertion

Almost every statement needs to contain values from variables from a script/program. Often programmers do not check their variables to make sure they are safe. Thus scripts are vulnerable to a technique called sql injection. With the XP database classes one does not have to check every value for malicious characters manually but he may give hints about the type of a variable to the class. It uses these hints to safely include the values at the given positions in the supplied statement and it will care for safe escaping if neccessary. With this technique you can write code more simple to read, so please use it. Additionally this way of inserting values from variables into your SQL makes your statements more beautiful and readable in your code.

Use comments

Many SQL dialects have the ability to place comments directly inside your statement. If your rdbms offers such a possibility - use it. Comments help you to find a paramter or a value in your code. Imagine a long insert-statement with insert table () values () - syntax. Once the variable list grows, you'll have severe problems to find a value in the values-part. With comments this would not be a problem:

  insert into foobar (primary_key,
    ts_init,
    valid_from,
    changedby
  ) values (
    %d,  -- primary key
    %s,  -- ts_init
    %s,  -- valid_from
    %s   -- changedby
  )

See?

Indentation

Indentation is something many people argue about, because there are many different indenting styles and sometimes coding standards force you to use a style you don't like. But indentation is imporant to let your code look good. We found the following style to be nice, thus we are recommending it: SQL keywords begin at the current depth of indentation, their parameters are listed below them, with one more level of indentatation depth. One parameter on one line with all additional modifiers (e.g. noholdlock). An exception to the rule is the WHERE-keyword which has its first parameter on the same line and any following restrictions on following lines, the prepended and should be indented with two spaces so all clauses begin on the same column. An example shows this:

  select
    a.category_id,
    a.count(*) as cnt
  from
    auftrag a noholdlock,
    vertrag v noholdlock
  where a.vertrag_id= v.vertrag_id
    and v.tarifgruppe_id= XYZ
    and a.category_id= 30001
    and a.valid_from > 'Jan 1 2003 0:00AM'
  order by 
    a.category_id
  having 
    a.count(*) > 1

The aim

The "superior" target we follow with these instructions is to have a common way to write SQL statements. Like coding standards this SQL-writing, or SQL-formatting guidelines are supposed to help this framework to be even more high quality code. In best-case one should not be able to tell who wrote the SQL.

SQL and database performance

Good sql code should not only be written in an elegant manner but also be tweaked to get the most performance out of your rdbms. Rdbms often include facilities for tweaking a query. When you utilize a Sybase Server or the MSSQL rdbms, you can enable "debugging" output by the option set showplan on|off. When enabled the server prints the plan of any query that is transmitted. Make sure your query is run on the available indices of the involved tabled. It is even better to use clustered indices or primary keys of tables.

Further reading

Clone this wiki locally