-
Notifications
You must be signed in to change notification settings - Fork 0
Dev.Coding Standard PHP
Based on Zend Framework 2 Coding Standards.
#PHP File Formatting
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 MUST consist of 4 spaces. Tabs MUST NOT be used for indentation.
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 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.
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 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, whileZend\Pdf
is acceptable. - the namespace
Zend\XMLRPC
would not be allowed, whileZend\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.
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
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 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 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.
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.
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.
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.
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.
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.
When a string is literal (contains no variable substitutions), the apostrophe or "single quote" SHOULD be used to demarcate the string:
$a = 'Example String';
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 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!";
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 ";
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
.
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.
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.
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 ...
}
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 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
{
}
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).
NONE
NONE
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 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 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 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.
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.
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.
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)
{
}
}
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.
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>
*/
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 thepublic
,private
, orprotected
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]