Skip to content

Dev.Coding Standard PHP

Taiwen Jiang edited this page Jul 15, 2013 · 22 revisions

PHP Coding Standard for Pi Engine

Based on Zend Framework 2 Coding Standards.

#PHP File Formatting

General

For files that contain only PHP code, the closing tag ?> MUST NOT be used. It is not required by PHP, and omitting it prevents the accidental injection of trailing white space into the response.

Indentation

Indentation MUST consist of 4 spaces. Tabs MUST NOT be used for indentation.

Maximum Line Length

The target line length is 80 characters. Developers SHOULD strive keep each line of their code under 80 characters. The maximum length of any line of PHP code is 120 characters.

Line Termination

Line termination follows the Unix text file convention. Lines MUST end with a single linefeed (LF) character. Linefeed characters are represented as ordinal 10, or hexadecimal 0x0A.

Lines MUST NOT have whitespace characters preceding the linefeed character.

Naming Conventions

Class naming convention:

  • Namespaces have a 1:1 relationship to the filesystem.
  • Classes are stored within the directory determined by the namespace. The root directory is "lib", and contains the Pi/, Zend/, vendor directory, which corresponds to the top-level namespaces within Pi, Zend Framework and corresponding vendor libraries.

Namespaces

Namespaces MUST contain only alphanumeric characters, the underscore, and the namespace separator {{ }}.

Namespaces SHOULD be MixedCase, and acronyms used in namespaces SHOULD as well. As examples:

  • the namespace Zend\PDF would not be allowed, while Zend\Pdf is acceptable.
  • the namespace Zend\XMLRPC would not be allowed, while Zend\XmlRpc is acceptable.

Code deployed alongside Pi Engine libraries but which is not part of the Pi or Zend Framework standard distribution SHOULD utilize separate namespaces.

Namespace Aliases

When aliasing within ZF library code, the aliases SHOULD typically follow these patterns:

  • If aliasing a namespace, use the final segment of the namespace; this can be accomplished by simply omitting the as portion of the alias:
use Zend\Filter;       // Alias is then "Filter"
use Zend\Form\Element; // Alias is "Element"
  • If aliasing a class, either use the class name (no as clause), or suffix the class with the subcomponent namespace preceding it:
use Zend\Controller\Action\HelperBroker; // aliased as "HelperBroker"
use Zend\Filter\Int as IntFilter;        // using suffix

Classes

Class names MUST contain only alphanumeric characters. Numbers are permitted in class names but are discouraged in most cases. Underscores are not permitted in Pi.

If a class name is comprised of more than one word, the first letter of each new word MUST be capitalized. Successive capitalized letters are not allowed, e.g. a class "Pi\MVC" is not allowed while "Pi\Mvc" is acceptable.

Abstract Classes

Abstract classes follow the same conventions as classes, with one additional rule: abstract class names SHOULD begin with the term, Abstract. As examples, AbstractAdapter and AbstractWriter are both considered valid abstract class names.

Abstract classes SHOULD be in the same namespace as concrete implementations. The following would be considered invalid usage:

namespace Zend\Log;
 
abstract class AbstractWriter implements Writer
{
}
namespace Zend\Log\Writer;
 
use Zend\Log\AbstractWriter;
 
class StreamWriter extends AbstractWriter
{
}

While the next example displays proper usage:

namespace Zend\Log\Writer;
 
use Zend\Log\Writer;
 
abstract class AbstractWriter implements Writer
{
}
 
class StreamWriter extends AbstractWriter
{
}

The ·Abstract· prefix MAY be omitted from the class name in the case of abstract classes that implement only static functionality, such as factories. As an example, the following is valid usage:

namespace Zend\Uri;
 
abstract class UriFactory
{
    public static function factory($uri)
    {
        // work goes in here...
    }
}

Interfaces

Interfaces follow the same conventions as classes, with two additional rules: interface MUST be nouns or adjectives and interface class names SHOULD end with the term, Interface. As examples, ServiceLocationInterface, EventCollectionInterface, and PluginLocatorInterface are all considered appropriate interface names.

Interfaces MUST be in the same namespace as concrete implementations.

Filenames

All other PHP files MUST only use alphanumeric characters, underscores, and the dash character ("-"). Spaces are strictly prohibited.

Any file that contains PHP code SHOULD end with the extension ".php", with the notable exception of view scripts.

The following examples show acceptable filenames for Zend Framework classes:

Zend/Db.php
Zend/Controller/Front.php
Zend/View/Helper/FormRadio.php

File names MUST map to class names as described above.

Functions and Methods

Function names MUST contain only alphanumeric characters. Underscores are not permitted. Numbers are permitted in function names but are discouraged.

Function names MUST always start with a lowercase letter. When a function name consists of more than one word, the first letter of each new word MUST be capitalized. This is commonly called "camelCase" formatting.

Verbosity is encouraged. Function names should be as verbose as is practical to fully describe their purpose and behavior.

These are examples of acceptable names for functions:

filterInput()
getElementById()
widgetFactory()

For object-oriented programming, accessors for instance or static variables SHOULD be prefixed with "get" or "set". In implementing design patterns, such as the singleton or factory patterns, the name of the method SHOULD contain the pattern name where practical to more thoroughly describe behavior.

For methods on objects that are declared with the "private" or "protected" modifier, the first character of the method name MAY be an underscore. This is the only acceptable application of an underscore in a method name, and is discouraged (as it makes refactoring to public visibility more difficult). Methods declared "public" SHOULD NOT contain an underscore.

Functions in the global scope (a.k.a "floating functions") MAY be used, but are discouraged. Consider wrapping these functions in a static class, or within a specific namespace.

Variables

Variable names MUST contain only alphanumeric characters. Underscores are not permitted. Numbers are permitted in variable names but are discouraged in most cases.

For variables that are declared with private or protected visibility, the first character of the variable name MAY be a single underscore. This is the only acceptable application of an underscore in a variable name, and is discouraged (as it makes refactoring to public visibility more difficult). Member variables declared with public visibility SHOULD NOT start with an underscore.

As with function names, variable names MUST always start with a lowercase letter and follow the "camelCaps" capitalization convention.

Verbosity is encouraged. Variables should always be as verbose as practical to describe the data that the developer intends to store in them. Terse variable names such as $i and $n are discouraged for all but the smallest loop contexts. If a loop contains more than 20 lines of code, the index variables should have more descriptive names.

Constants

Constant names MAY contain both alphanumeric characters and underscores.

All letters used in a constant name MUST be capitalized, while all words in a constant name MUST be separated by underscore characters.

For example, EMBED_SUPPRESS_EMBED_EXCEPTION is permitted but EMBED_SUPPRESSEMBEDEXCEPTION is not.

Constants MUST be defined as class members with the const modifier. Constants MAY be defined in the global scope or within namespaces.

Coding Style

PHP Code Demarcation

PHP code MUST always be delimited by the full-form, standard PHP tags <?php ?>.

The closing PHP tag ?> MUST be omitted if no markup or code follows it.

Short tags MUST NOT be used within Pi code, but MAY be used within view scripts.

Strings

String Literals

When a string is literal (contains no variable substitutions), the apostrophe or "single quote" SHOULD be used to demarcate the string:

$a = 'Example String';

String Literals Containing Apostrophes

When a literal string itself contains apostrophes, you MAY demarcate the string with quotation marks or "double quotes". This is especially useful for SQL statements:

$sql = "SELECT {{id}}, {{name}} from {{people}} "
     . "WHERE {{name}}='Fred' OR {{name}}='Susan'";

This syntax is preferred over escaping apostrophes as it is much easier to read.

Variable Substitution

Variable substitution SHOULD use either of the following forms:

$greeting = "Hello $name, welcome back!";
$greeting = "Hello {$name}, welcome back!";

For consistency, this form SHOULD NOT be used:

$greeting = "Hello ${name}, welcome back!";

String Concatenation

Strings MUST be concatenated using the "." operator. A space MUST always be added before and after the "." operator to improve readability:

$company = 'Zend' . ' ' . 'Technologies';

When concatenating strings with the "." operator, one SHOULD break the statement into multiple lines to improve readability. In these cases, each successive line SHOULD be padded with white space such that the "." operator is aligned under the "=" operator:

$sql = "SELECT {{id}}, {{name}} FROM {{people}} "
     . "WHERE {{name}} = 'Susan' "
     . "ORDER BY {{name}} ASC ";

Class Names In Strings

When referencing a class name in a string, the fully qualified class name MUST be provided, and MUST NOT include a preceding namespace separator. As an example, if in the namespace Zend\Log, and referencing the class StreamWriter in the Writer subnamespace, you would use the string Zend\Log\Writer\StreamWriter.

Arrays

Numerically Indexed Arrays

When declaring indexed arrays with the array function, a trailing space MUST be added after each comma delimiter to improve readability:

$sampleArray = array(1, 2, 3, 'Zend', 'Studio');

It is permitted to declare multi-line indexed arrays using the array construct. In this case, each successive line MUST be padded with spaces such that the beginning of each line is aligned with the initial element of the array:

$sampleArray = array(1, 2, 3, 'Zend', 'Studio',
                     $a, $b, $c,
                     56.44, $d, 500);

Alternately, the initial array item MAY begin on the following line. If so, it MUST be padded at one indentation level greater than the line containing the array declaration, and all successive lines MUST have the same indentation; the closing paren MUST be on a line by itself at the same indentation level as the line containing the array declaration:

$sampleArray = array(
    1, 2, 3, 'Zend', 'Studio',
    $a, $b, $c,
    56.44, $d, 500,
);

When using this latter declaration, you SHOULD use a trailing comma for the last item in the array; this minimizes the impact of adding new items on successive lines, and helps to ensure no parse errors occur due to a missing comma.

Associative Arrays

When declaring associative arrays with the array construct, one SHOULD break the statement into multiple lines. In this case, each successive line MUST be padded with white space such that both the keys and the values are aligned:

$sampleArray = array('firstKey'  => 'firstValue',
                     'secondKey' => 'secondValue');

Alternately, the initial array item MAY begin on the following line. If so, it MUST be padded at one indentation level greater than the line containing the array declaration, and all successive lines MUST have the same indentation; the closing paren MUST be on a line by itself at the same indentation level as the line containing the array declaration. For readability, the various "=>" assignment operators SHOULD be padded such that they align.

$sampleArray = array(
    'firstKey'  => 'firstValue',
    'secondKey' => 'secondValue',
);

When using this latter declaration, one SHOULD use a trailing comma for the last item in the array; this minimizes the impact of adding new items on successive lines, and helps to ensure no parse errors occur due to a missing comma.

Namespaces

Namespace Declaration

Files SHOULD contain a single namespace; composing multiple namespaces in a single file is strongly discouraged.

Namespace declarations MUST be the first statement in a file, unless multiple namespaces are declared; the only code that should precede the declaration should be a file-level documentation block, with a single empty line between the docblock and the namespace declaration.

Namespace declarations done according to the above MUST NOT have any indentation.

In the case that multiple namespace declarations must be placed in the same file, such declarations MUST utilize blocks and not single-line declarations. The opening brace MUST be placed on the following line at the same level of indentation, and all code within the block MUST receive an extra level of indentation. For example, the following is invalid:

namespace Foo;
 
// some code...
 
namespace Bar;
 
// more code...

But the following is correct:

namespace Foo
{
    // some code ...
}
 
namespace Bar
{
    // more code ...
}

Import Statements

All explicit dependencies used by a class MUST be imported. These include classes and interfaces used in method typehints and explicit instanceof type checks, classes directly instantiated, etc. Exceptions include:

  • Code within the current namespace or subnamespaces of the current namespace
  • Class names that are dynamically resolved (e.g., from a plugin broker) There MUST be one use keyword per declaration.

There MUST be one blank line after the use block.

use Zend\Log\Logger;
use Zend\Event\Eventmanager as Events;
use Zend\View\PhpRenderer as View;

Additionally, import statements SHOULD be in alphabetical order, to make scanning for entries predictable.

Classes

Class Declaration

Classes MUST be named according to Zend Framework's naming conventions.

The brace MUST be written on the line underneath the class name, at the same level of indentation as the class declaration.

Every class MUST have a documentation block that conforms to the PHPDocumentor standard.

All code in a class MUST be indented with four spaces additional to the level of indentation of the class declaration.

PHP files declaring classes MUST contain a single PHP class only.

Placing additional code in class files MAY be done, but is discouraged. In such files, two blank lines MUST separate the class from any additional PHP code in the class file.

The following is an example of an acceptable class declaration:

/**
 * Documentation Block Here
 */
class SampleClass
{
    // all contents of class
    // must be indented four spaces
}

Classes that extend other classes or which implement interfaces SHOULD declare their dependencies on the same line when possible.

class SampleClass extends AbstractFoo implements Bar
{
}

If as a result of such declarations, the line length exceeds the maximum line length, break the line after implements keywords, and pad those lines by one indentation level.

class SampleClass extends AbstractFoo implements
    Bar
{
}

If the class implements multiple interfaces and the declaration exceeds the maximum line length, break after each comma separating the interfaces, and indent the interface names such that they align.

class SampleClass implements
    BarInterface,
    BazInterface
{
}

Class Member Variables

Member variables MUST be named according to Zend Framework's variable naming conventions.

Any variables declared in a class MUST be listed at the top of the class, above the declaration of any methods.

The var construct MUST NOT be used. Member variables MUST declare their visibility by using one of the private, protected, or public visibility modifiers. Giving access to member variables directly by declaring them as public MAY be done, but is discouraged in favor of accessor methods (set & get).

Exceptions

NONE

Using Exceptions

NONE

Functions and Methods

Function and Method Declaration

Functions MUST be named according to Zend Framework's function naming conventions.

Methods inside classes MUST always declare their visibility by using one of the private, protected, or public visibility modifiers.

As with classes, the brace MUST always be written on the line underneath the function name. Space MUST NOT be inserted between the function name and the opening parenthesis for the arguments.

Functions SHOULD NOT be declared in the global scope.

The following is an example of an acceptable function declaration in a class:

/**
 * Documentation Block Here
 */
class Foo
{
    /**
     * Documentation Block Here
     */
    public function bar()
    {
        // all contents of function
        // must be indented four spaces
    }
}

In cases where the argument list exceeds the maximum line length, you MAY introduce line breaks. Additional arguments to the function or method MUST be indented one additional level beyond the function or method declaration. A line break MUST occur before the closing argument paren, which MUST be placed on the same line as the opening brace of the function or method with one space separating the two, and at the same indentation level as the function or method declaration. The following is an example of one such situation:

/**
 * Documentation Block Here
 */
class Foo
{
    /**
     * Documentation Block Here
     */
    public function bar($arg1, $arg2, $arg3,
        $arg4, $arg5, $arg6
    ) {
        // all contents of function
        // must be indented four spaces
    }
}

Pass-by-reference is the only parameter passing mechanism permitted in a method declaration.

/**
 * Documentation Block Here
 */
class Foo
{
    /**
     * Documentation Block Here
     */
    public function bar(&$baz)
    {}
}

Call-time pass-by-reference MUST NOT be used.

The return value MUST NOT be enclosed in parentheses. This can hinder readability, in addition to breaking code if a method is later changed to return by reference.

/**
 * Documentation Block Here
 */
class Foo
{
    /**
     * WRONG
     */
    public function bar()
    {
        return($this->bar);
    }
 
    /**
     * RIGHT
     */
    public function bar()
    {
        return $this->bar;
    }
}

Closure Definitions

Closure definitions generally follow the same rules as for functions and methods:

  • Space MUST NOT be inserted between the "function" keyword and the opening parenthesis for the arguments.
  • A space MUST be added between any "use" statement used in declaration and the opening parenthesis for its arguments.
  • In cases where the argument list exceeds the maximum line length, you MAY introduce line breaks. Additional arguments to the function or method MUST be indented one additional level beyond the function or method declaration. A line break MUST occur before the closing argument paren, which MUST be placed on the same line as the opening brace of the function or method with one space separating the two, and at the same indentation level as the function or method declaration.
  • Call-time pass-by-reference MUST NOT be used.
  • The return value MUST NOT be enclosed in parentheses. This can hinder readability, in addition to breaking code if a method is later changed to return by reference.

The primary difference is that closures MUST retain the initial brace on the same line in which they are defined (not the following line). Code MUST be indented one additional level.

/**
 * Opening brace:
 */
$foo = function($x)
{
    // WRONG
};
 
$foo = function($x) {
    // RIGHT
};
 
/**
 * Use statement declaration
 */
$foo = function($x) use($y) {
    // WRONG
};
 
$foo = function($x) use ($y) {
    // RIGHT
};
 
/**
 * Indentation
 */
$foo = array_map(function($x) {
// WRONG
return strtolower($x);
}, $array);
 
$foo = array_map(function($x) {
    // RIGHT
    return strtolower($x);
}, $array);

Function and Method Usage

Function arguments MUST be separated by a single trailing space after the comma delimiter. The following is an example of an acceptable invocation of a function that takes three arguments:

threeArguments(1, 2, 3);

Call-time pass-by-reference MUST NOT be used. See the function declarations section for the proper way to pass function arguments by-reference.

In passing arrays as arguments to a function, the function call MAY include the array declaration and MAY be split into multiple lines to improve readability. In such cases, the normal guidelines for writing arrays still apply:

threeArguments(array(1, 2, 3), 2, 3);
 
threeArguments(array(1, 2, 3, 'Zend', 'Studio',
                     $a, $b, $c,
                     56.44, $d, 500), 2, 3);
 
threeArguments(array(
    1, 2, 3, 'Zend', 'Studio',
    $a, $b, $c,
    56.44, $d, 500
), 2, 3);

Control Statements

If/Else/Elseif

Control statements based on the if and elseif constructs MUST have a single space before the opening parenthesis of the conditional and a single space after the closing parenthesis.

Within the conditional statements between the parentheses, operators MUST be separated by spaces for readability. Inner parentheses SHOULD be used to improve logical grouping for larger conditional expressions.

The opening brace MUST be written on the same line as the conditional statement if the conditional statement does not contain a line feed. The closing brace MUST be written on its own line. Any content within the braces MUST be indented using four spaces.

if ($a != 2) {
    $a = 2;
}

If the conditional statement causes the line length to exceed the maximum line length and has several clauses, you MUST break the conditional into multiple lines. In such a case, break the line prior to a logic operator, and pad the line such that it aligns under the first character of the conditional clause. The closing paren in the conditional will then be placed on a line with the opening brace, with one space separating the two, at an indentation level equivalent to the opening control statement.

if (($a == $b)
    && ($b == $c)
    || (Foo::CONST == $d)
) {
    $a = $d;
}

The intention of this latter declaration format is to prevent issues when adding or removing clauses from the conditional during later revisions.

For if statements that include elseif or else, the formatting conventions are similar to the if construct. The following examples demonstrate proper formatting for if statements with else and/or elseif constructs:

if ($a != 2) {
    $a = 2;
} else {
    $a = 7;
}
 
if ($a != 2) {
    $a = 2;
} elseif ($a == 3) {
    $a = 4;
} else {
    $a = 7;
}
 
if (($a == $b)
    && ($b == $c)
    || (Foo::CONST == $d)
) {
    $a = $d;
} elseif (($a != $b)
          || ($b != $c)
) {
    $a = $c;
} else {
    $a = $b;
}

PHP allows statements to be written without braces in some circumstances. This coding standard makes no differentiation; all if, elseif or else statements MUST use braces.

Switch

Control statements written with the switch statement MUST have a single space before the opening parenthesis of the conditional statement and after the closing parenthesis.

All content within the switch statement MUST be indented using four spaces. Content under each case statement MUST be indented using an additional four spaces.

switch ($numPeople) {
    case 1:
        break;
 
    case 2:
        break;
 
    default:
        break;
}

The construct default MAY be omitted from a switch statement, but the code MUST contain a comment indicating deliberate omission in such cases.

Inline Documentation

Documentation Format

All documentation blocks ("docblocks") MUST be compatible with the phpDocumentor format. Describing the phpDocumentor format is beyond the scope of this document. For more information, visit: http://phpdoc.org/

All class files MUST contain a "file-level" docblock at the top of each file and a "class-level" docblock immediately above each class. Examples of such docblocks can be found below.

General Notes

Classes and interfaces referenced by annotations MUST follow the same resolution order as PHP. In other words:

If the class is in the same namespace, simply refer to the class name without the namespace:

namespace Foo\Component;
 
class Bar
{
    /**
     * Assumes Foo\Component\Baz:
     * @param Baz $baz
     */
    public function doSomething(Baz $baz)
    {
    }
}

If the class is in a subnamespace of the current namespace, refer to it relative to the current namespace:

namespace Foo\Component;
 
class Bar
{
    /**
     * Assumes Foo\Component\Adapter\Baz:
     * @param Adapter\Baz $baz
     */
    public function doSomething(Adapter\Baz $baz)
    {
    }
}

If the class is imported, either via a namespace or explicit class name, use the name as specified by the import:

namespace Foo\Component;
 
use Zend\EventManager\EventManager as Events,
    Zend\Log;
 
class Bar
{
    /**
     * Assumes Zend\EventManager\EventManager and Zend\Log\Logger:
     * @param Events $events
     * @param Log\Logger $log
     */
    public function doSomething(Events $events, Log\Logger $log)
    {
    }
}

If the class is from another namespace, but not explicitly imported, provide a globally resolvable name:


namespace Foo\Component;
 
class Bar
{
    /**
     * Assumes \Zend\EventManager\EventManager:
     * @param \Zend\EventManager\EventManager $events
     */
    public function doSomething(\Zend\EventManager\EventManager $events)
    {
    }
}

Files

Every file that contains PHP code MUST have a docblock at the top of the file that contains these phpDocumentor tags at a minimum:

/**
 * Pi Engine (http://pialog.org/)
 *
 * @link      http://github.com/pi-engine/pi for the canonical source repository
 * @copyright Copyright (c) Pi Engine (http://pialog.org/)
 * @license   http://www.pialog.org/license.txt New BSD License
 * @package   Pi\[PackageName]
 * @author    [Author Name] <[author_email_address]>
 */

Note: Change above author name and email to your own.

Classes

Every class MUST have a docblock that contains these phpDocumentor tags at a minimum:

/**
 * Short description for class
 *
 * Long description for class (if any)...
 *
 * Sample code:
 * <code>
 *   $someCodeForUseExample;
 * </code>
 */

Functions

Every function, including object methods, MUST have a docblock that contains at a minimum:

A description of the function

  • All of the arguments
  • All of the possible return values (even void) It is not necessary to use the @access annotation because the access level is already known from the public, private, or protected visibility modifier used to declare the function.

If a function or method may throw an exception, use @throws for all known exception classes:

@throws exceptionclass [description]
Clone this wiki locally