Coding Standards

This document describes rules and recommendations for developing Nette.

Nette follows the recommendations defined in the PSR-1 and PSR-2 documents with two differences

  • tabs are used for indenting

When contributing code to Nette, you must follow its coding standards. The easiest way how to do it is to imitate the existing Nette code. The main advantage is that every piece of code looks and feels familiar.

General rules

  • Code must be compatible with PHP version declared in composer.json.
  • Code must use 1 tab for indenting, not spaces. Tabs are allowed only at the begin of line.
  • The recommended line length limit is 120 characters.
  • PHP code must use only UTF-8 without BOM; ASCII encoding is preferred.
  • PHP code must always be opened by the full-form PHP tag: <?php
  • The closing ?> tag must be omitted from files containing only PHP.
  • Blank lines should be added to improve readability and to indicate related blocks of code.
  • There should not be more than one statement per line.
  • There must not be trailing whitespace at the end of lines.
  • All files must end with a single blank line.
  • File name should match class name if possible.
  • Any file that contains any PHP code must end with the extension .php, or .phtml in case of templates, or .phpt in case of PHP tests.
  • Defining constants or functions in the global scope is not permitted.
  • The reason for using shut-up operator must be commented: @mkdir($dir); // @ - directory may exist.

For all file names, only alphanumeric characters, underscores, dots, and the dash character (-) are permitted.

Common Naming Conventions

  • Always choose meaningful and specific names.
  • Avoid using abbreviations unless the full name is excessive.
  • Use uppercase for two-letter abbreviations, and Pascal Case for longer abbreviations.
  • Use only alphanumeric characters in names.
  • Variables should always be as verbose as practical. Terse variable names such as $i and $n are discouraged for anything other than the smallest loop contexts.

File Header

Header in PHP files with a class definition is following. Other files are specific by its type and contents.

  • PHP opening tag <php.
  • Empty line.
  • Copyright doc block.
  • Empty line.
  • declare(strict_types=1);
  • Empty line.
  • Namespace definition.
  • Empty line.
  • The use statements, once per line. The use keyword on every line. Lines are alphabetically ordered.
  • Empty line.
  • Empty line.
  • (content)
<?php

/**
 * This file is part of the Nette Framework (https://nette.org)
 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
 */

declare(strict_types=1);

namespace Nette\Application;

use Latte;
use Nette\Http;


...

Basic expressions

Strings

When a string is literal (contains no variable substitutions), the single quote should be used to demarcate the string, except when a literal string itself contains apostrophes, it is recommended to demarcate the string with double quotes.

Strings may be concatenated using the . operator. A space must always be added before and after the . operator to improve readability. It is permitted to break the statement into multiple lines:

$sql = 'SELECT `id`, `name` FROM `people`'
    . 'WHERE `name` = ?'
    . 'ORDER BY `name`';

Arrays

  • Arrays must be written by short notation.
  • Trailing space must be added after each comma delimiter to improve readability.
$sampleArray = [1, 2, 3, 'test'];
  • When declaring associative arrays, it is encouraged to put each key and value pair on separated line, indented by 1 tab. After last pair the comma must be added.
$sampleArray = [
    'firstKey' => 'firstValue',
    'secondKey' => 'secondValue',
];

Keywords and True/False/Null

  • PHP keywords must be in lower case.
  • The PHP constants true, false, and null must be in lower case.

Closures

  • Closures must be declared with a space after the function keyword, and with a space before and after the use keyword.
  • The opening brace must go on the same line, and the closing brace must go on the next line following the body.
  • In the argument list, there must be one space after each comma, and there must no be a space before each comma.

A closure declaration looks like the following:

$closureWithArgs = function ($arg1, $arg2) {
    // body
};

$closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2) {
    // body
};

Method and Function Calls

  • There must not be a space between the method or function name, the opening parenthesis, after the opening parenthesis and before the closing parenthesis.
  • In the argument list, there must be one space after each comma, and there must no be a space before each comma.
  • Call-time pass by-reference is prohibited.

Examples:

bar();
$foo->bar($arg1);
Foo::bar($arg1, $arg2);

For functions whose arguments permitted arrays, the function call may include [] and can be split into multiple lines to improve readability. In these cases, the standards for writing arrays still apply:

fooBar([1, 2, 3], 2, 3);

$foo->bar(1, 2, [
    'firstKey' => 'firstValue',
    'secondKey' => 'secondValue',
], 3, 4);

Argument lists may be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list must be on the next line, and there must be only one argument per line:

$foo->bar(
    $arg,
    $arg2,
    $arg3
);

Classes

The term “class” refers to all classes, interfaces, and traits.

  • Class names must be declared in PascalCase.
  • Code written for PHP >= 5.4 must use formal namespaces.
  • Use a noun or noun phrase for class name.
  • Add an appropriate class-suffix when sub-classing another type when possible. (TexyParser, HttpRequest)
  • Interface classes must follow the same conventions as other classes, however should begin with I letter. Example: IPresenter
  • The extends and implements keywords must be declared on the same line as the class name.
  • The opening brace for the class must be on the line underneath the class name; the closing brace for the class must go on the next line after the body.
  • Every class must have a documentation block.
  • Any code within a class must be indented by 1 tab.

Placing additional code in a class file is not permitted.

Class members implementation should follow this order as it is practical:

  1. Constants
  2. Properties (Member Variables)
    1. public
    2. protected
    3. private
  3. Factories
  4. Constructor, Destructor
  5. Methods

All instantiable classes should use Nette\SmartObject trait and non-instantiable Nette\StaticClass trait.

Constants

  • Constants always have all letters capitalized, separated by underscore characters (example: CONTENT_TEXTUAL, FIELD_NUMERIC).

Class properties

  • Properties must be declared in camelCase.
  • The var keyword must not be used to declare a property.
  • Property names must not be prefixed with a single underscore to indicate protected or private visibility.
  • Properties must describe an entity not the type or size.
  • Properties must have documentation block with @var directive and specified type.

Methods

  • They must be declared in camelCase.
  • Visibility must be declared on all methods (with exception of interface declarations).
  • Method names must not be prefixed with a single underscore to indicate protected or private visibility.
  • Like classes, the brace is always written on the line underneath the function name. There is no space between the function name and the opening parenthesis for the arguments. All arguments should have type hint if possible.
  • Functions and methods must have documentation block with specified types of returned value.
  • Names must be as verbose as is practical to enhance the understandability of code (examples: fetchPairs(), getElementById(), isSubmitted()).
  • Try to use a verb or verb-object pair.
  • If method returns boolean, try to prefix name with “is”, “can”, “has” or similar meaningful prefix.
  • The return value must not be enclosed in parentheses.
  • Methods are seperated by 2 blank lines.

A method declaration looks like the following:

/**
 * Documentation block here.
 */
class Foo
{
    /**
     * Documentation block here.
     */
    public function verb($arg1, TypeHint $arg2 = null): void
    {
        // content must be indented by 1 tab
    }
}

abstract, final, and static

  • When present, the abstract and final declarations must precede the visibility declaration.
  • When present, the static declaration must come after the visibility declaration.

Control Statements

The general style rules for control structures are as follows:

  • There must be one space after the control structure keyword.
  • There must not be a space after the opening parenthesis and before the closing parenthesis.
  • There must be one space between the closing parenthesis and the opening brace.
  • The structure body must be indented once.
  • The closing brace must be on the next line after the body.

The body of each structure must be enclosed by braces. This standardizes how the structures look, and reduces the likelihood of introducing errors as new lines get added to the body.

if, elseif, else

  • Comparison “strong typed” operators (=== and !==) are preferred before “weak typed” ones (== and !=).
  • If weak typed comparison operator is used, the intention must be documented with a comment.
  • The keyword elseif must be used instead of else if.

A structure looks like the following:

if ($a === 2) {
    $x = 2;

} elseif ($a == 0) { // intentionally ==, $a may be null
    $x = 4;

} else {
    $x = 7;
}

switch, case

A switch structure looks like the following:

switch ($numPeople) {
    case 1:
        echo 'First case, with a break';
        break;

    case 2:
        echo 'Second case, which falls through';
        // break intentionally omitted

    default:
        break;
}

There must be a comment when fall-through is intentional in a non-empty case body.

while, do while

A while statement looks like the following:

while ($expr) {
    // structure body
}

Similarly, a do while statement looks like the following:

do {
    // structure body;
} while ($expr);

for

A for statement looks like the following:

for ($i = 0; $i < 10; $i++) {
    // for body
}

foreach

A foreach statement looks like the following:

foreach ($iterable as $key => $value) {
    // foreach body
}

try, catch

A try catch block looks like the following:

try {
    // try body
} catch (FirstExceptionType $e) {
    // catch body
} catch (OtherExceptionType $e) {
    // catch body
}

Documentation Blocks and Annotations

There are two main places for documentation blocks. Documentation block before class definition:

  • Starts by a class description.
  • Empty line follows.
  • The @property (or @property-read, @property-write) annotations follow, one by line.
  • Syntax of property annotation is: annotation, space, type, space, $name. The property-write has not type.
  • The @method annotations follow, one by line. They are used to declare magic methods.
  • Syntax of method annotation is: annotation, space, return type, space, name(type list).
  • The @author annotation is omitted. The authorship is kept in a source code history.
  • The @internal or @deprecated annotations can be used.
/**
 * MIME message part.
 *
 * @property string $encoding
 * @property-read array $headers
 * @property-write $contentType
 * @method string getSomething(string, TypeHint)
 */

Documentation block before class method definition:

  • Starts by a short method description.
  • No empty line.
  • The @param annotations, one by line. Only if native annotation cannot be used.
  • The @return annotation. Only if native annotation cannot be used.
  • The @throws annotations, one by line.

Every annotation is followed by one space and its contents, except for the @param which is quite complex:

  • The @param annotation is followed by two spaces, type and optionally by space and description.
  • It is primarily meant for scalar method parameters without type hint.
  • The main rule is: never duplicate any method signature information.
  • Description is used only if parameter name is not self-descriptive enough.
/**
 * @param  int|null
 * @return string
 * @throws DirectoryNotFoundException
 */
public function run($iterations, Logger $logger)


/**
 * @param  Container
 * @param  bool
 * @param  callable function(string $value)
 * @return string[]
 */
public function find(Container $container, $need, $filter = null) {}

Some annotations like @param or @return require data type. These can be:

  • int, float, bool, string for scalars.
  • resource, iterable
  • callable with a following function signature as a description.
  • null for nullable arguments
  • ClassName
  • static for fluent interface
  • array for common arrays, but
  • string[], int[], 'ClassName[]` for single-type arrays.
  • Dynamic type list is concatenated by | like int|string|ClassName.