PHP

Owner: Development Last revision: 12.11.2021

General PHP Rules#

Code style must follow PSR-1 and PSR-2. In some cases we might introduce even stricter rules. Generally speaking, everything string-like that’s not public-facing should use camelCase.

PHP Tags#

PHP code MUST use the long <?php ?> tags; it MUST NOT use the other tag variations, including the short echo <?= ?> tags.

Use Declarations#

Unutilised use declarations should not be left in place.

When more than one use declaration is present, it is preferred they are ordered alphabetically.

Docblocks#

Don’t use docblocks for methods that can be fully type hinted (unless you need a description).

Only add a description when it provides more context than the method signature itself. Use full sentences for descriptions, including a period at the end.

// Good
class Url
{
    public static function fromString(string $url): Url
    {
        // ...
    }
}

// Bad: The description is redundant, and the method is fully type-hinted.
class Url
{
    /**
     * Create a url from a string.
     *
     * @param string $url
     *
     * @return \Further\Url\Url
     */
    public static function fromString(string $url): Url
    {
        // ...
    }
}

Always use fully qualified class names in Docblocks.

// Good
/**
 * @param string $url
 *
 * @return \Further\Url\Url
 */

// Bad
/**
 * @param string $foo
 *
 * @return Url
 */

Docblocks for class properties are required, as there’s currently no other way to to typehint these.

// Good
class Foo
{
    /** @var \Further\Url\Url */
    protected $url;

    /** @var string */
    protected $name;
}

// Bad
class Foo
{
    protected $url;
    protected $name;
}

When possible, docblocks should be written on one line.

// Good
/** @var string */
/** @test */

// Bad
/**
 * @test
 */

If a variable has multiple types, the most common occurring type should be first.

// Good
/** @var \Further\Goo\Bar|null */

// Bad
/** @var null|\Further\Goo\Bar */

Comments#

Comments should be avoided as much as possible by writing expressive code. If you do need to use a comment format it like this:

// There should be space before a single line comment.

/*
 * If you need to explain a lot you can use a comment block. Notice the
 * single * on the first line. Comment blocks don't need to be three
 * lines long or three characters shorter than the previous line.
 */

Whitespace#

Statements should have to breathe. In general always add blank lines between statements, unless they’re a sequence of single-line equivalent operations. This isn’t something enforceable, it’s a matter of what looks best in its context.

// Good
public function getPage($url)
{
    $page = $this->pages()->where('slug', $url)->first();

    if (!$page) {
        return null;
    }

    if ($page['private'] && !Auth::check()) {
        return null;
    }

    return $page;
}

// Bad: Everything's cramped together.
public function getPage($url)
{
    $page = $this->pages()->where('slug', $url)->first();
    if (!$page) {
        return null;
    }
    if ($page['private'] && !Auth::check()) {
        return null;
    }
    return $page;
}
// Good: A sequence of single-line equivalent operations.
public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->string('email')->unique();
        $table->string('password');
        $table->rememberToken();
        $table->timestamps();
    });
}

Don’t add any extra empty lines between {} brackets.

// Good
if ($foo) {
    $this->foo = $foo;
}

// Bad
if ($foo) {

    $this->foo = $foo;

}

Class Properties and Array Keys#

Both class properties and array keys should be in camelCase.

// Good
$array = ['someValue', 'someOtherValue'];
$value = $obj->someValue;

// Bad
$array = ['some_value', 'some_other_value'];
$value = $obj->some_value;

Exceptions are configuration variables and language string keys, which should be snake_case.

Trailing commas

// Good
const person = [
    'Adrian',
    'Sebastian',
];

// Bad, no trailing comma.
const person = [
    'Adrian',
    'Sebastian'
];

If Statements#

Always use curly brackets.

// Good
if ($condition) {
   ...
}

// Bad
if ($condition) 

Since according to PSR-2, the opening brace should be separated from the if() condition by a space character, and the else should be on the same line and next to the preceding closing brace, comments for the entire if or else statement should be at the top of the body and formatted as a block comment, like so:

if ($condition) {
    /* What happens in if */
    ...
} else {
    ...
}

Ternary Operators#

Every portion of a ternary expression should be on its own line unless it’s a really short expression.

// Good
$result = $object instanceof Model
    ? $object->name
    : 'A default value';

$name = $isFoo ? 'foo' : 'bar';

// Bad
$result = $object instanceof Model ?
    $object->name :
   'A default value';

Null Coalesce Operators#

Use null coalesce operators where possible.

// Good
$username = $_GET['user'] ?? 'nobody';

// Bad
$username = $_GET['user'] ?: 'nobody';

$username = isset($_GET['user']) ? $_GET['user'] : 'nobody';

Happy Path#

Generally a function should have its unhappy path first and its happy path last. In most cases this will cause the happy path being in an unindented part of the function which makes it more readable.

// Good

if (!$goodCondition) {
    throw new Exception;
}

// do work
// Bad

if ($goodCondition) {
    // do work
}

throw new Exception;

Avoid Else#

In general, else should be avoided because it makes code less readable. In most cases it can be refactored using early returns. This will also cause the happy path to go last, which is desirable.

// Good

if (!$conditionA) {
   // condition A failed
   
   return;
}

if (!$conditionB) {
   // condition A passed, B failed
   
   return;
}

// condition A and B passed

Compound Ifs#

In general, separate if statements should be preferred over a compound condition. This makes debugging code easier.

// Good
if (!$conditionA) {
   return;
}

if (!$conditionB) {
   return;
}

if (!$conditionC) {
   return;
}

// do stuff
// bad
if ($conditionA && $conditionB && $conditionC) {
  // do stuff
}

Infix Operators#

Always use spaces before and after infix operators in expressions.

Organizing Classes#

Property and method order#

All properties and methods in a class should be ordered alphabetically. Constants should preceed variables. The constructor should always be the first method.

// Good
class Person
{
    public const $id;

    private $address;
    private $age;
    private $name;
    private $title;

    public function __construct() {
        // ...
    }
    
    public function changeAddress($newAddress) {
        // ...    
    }

    public function incrementAge() {
        // ...    
    }
}

// Bad
class Person
{
    private $name;
    private $title;
    private $address;
    private $age;

    public const $id;

    public function __construct() {
        // ...
    }

    public function incrementAge() {
        // ...    
    }
    
    public function changeAddress($newAddress) {
        // ...    
    }
}

Traits#

Traits should always come first in the class definition.

// Good
class Person
{
    use IsMammal;
    
    // ...
}

// Bad
class Person
{
    public $name;

    use IsMammal;
    
    // ...
}

Code Clean-up#

No debugging code can be left in place, i.e. dd(), var_dump(), print_r(), die().