Code Style

Code styles let the team write consistently styled code across our various projects. This section helps define our code style, with examples you can use as a reference.

Base Code Style

We use PSR-12 as our base code style. All new code should follow PSR-12. In some older projects, we may follow an older PSR-2 standard.

In general, when working in a project which uses the PSR-2 code style, you should stick with PSR-2 until the project is updated to PSR-12.

Some projects may use automatic code checkers to enforce the required standard. The code style checks must pass on GitHub before code can be merged.

Test Code Style

The only place we don't strictly follow PSR-12 is in the tests/ directory. There are some exceptions that we follow to make tests more readable.

<?php

    // ...

    /** @test */
    public function it_returns_the_claims_from_a_jwt_token_as_an_array()
    {
        //
    }
  • Tests are identified using the /** @test */ single line docblock. This makes it easier with IDE colouring to quickly differentiate between actual tests and helper methods.
  • The method name is written as a human-friendly, snake case string which describes exactly whats being tested. This helps the test act as another form of documentation that can be quickly read by developers. Additionally, there is no line length limit on test method names, but care should be taken to not make it overly verbose.
  • No return types on test methods. As mentioned in the previous point, test methods should be human-readable, and reduce the amount of visual distractions. It provides no value to add return types to test methods as they all return void.

Docblocks and Comments

We should not add file, class and method level doc blocks. They add noise to the code, require ongoing maintenance and quickly get out of date.

There are a few exceptions to class based docblocks:

  • Classes which make use of magic properties, for example eloquent model attributes, should define a class level docblock, with each magic property added with a @param tag
  • Classes which make use of magic methods, such as Enums where constants can be accessed via a method call to return an instance, should define a class level docblock with a @method tag.

Docblock examples

<?php

declare(strict_types=1);

/**
 * Written by XYZ
 * Version 1.0
 */

use \SomeBaseClass;

/**
 * @package Something
 */
class SomeClass extends SomeBaseClass
{
    /**
     * @param array $cache
     */
    private $cache = [];

    /**
     * Returns the sum of A and B
     * @param int $a
     * @param int $b
     * @return int
     */
    public function add(int $a, int $b): int
    {
        // ...
    }
}

Docblocks to explain the reasoning behind a particular bit of code are allowed, but avoid stating the obvious. If a method returns $a + $b, comments like Returns the sum of $a and $b are discouraged. Instead, they should be used to tell other developers why code was written in a certain way, or why a certain approach was taken.

Additionally, @todo comments are allowed with a brief but understandable description about what need to be done. Type declaration comments should also be used where the variable has no inferred type. For example, when calling app('request') in Laravel to fetch the Request instance, this is also fine to use a single line docblock to infer the type.

In projects using php 7.4 or higher, property type declarations should be used, and no property block added. In projects where property typehints are not supported, they should be defined using a single line doc block.

Examples of accepted comments

<?php

declare(strict_types=1);

use \SomeBaseClass;

class SomeClass extends SomeBaseClass
{
    /** @property int[] */
    private $calculationCache = [];

    public function calculateSomething(?int ...$args): int
    {
        /** @var Request $request */
        $request = app('request');

        /*
         * We need to strip all nulls and 0 values from the array
         * of $args provided into the method as they can affect the
         * average calculations. Some other meaningful explanation
         * is added here
         */
         $args = array_filter($args);

         // @todo Convert string based numbers like `one`, `two`, `three` to there numerical values

         return array_sum($args);
    }
}

Avoid commenting out code wherever possible. If it's unlikely to ever be required, or used, delete it. We can use GitHub history to retrieve if it's really required.

Arrays

  • Arrays should always be declared using the shorthand square bracket syntax.
  • the first element in an array should never have whitespace preceding it unless it's a newline, and the last element should never have whitespace after it unless it a newline.
  • Arrays can be defined on a single line, or one element per line, depending on how long the array is. Line length limits should be maintained where possible.
  • Key-Value arrays should always be defined using multiple lines
  • Values in key-value pairs should not be intended
  • Its preferred to have a comma on the last element in an array

Examples of arrays

// Multiline array
$names = [
    'John',
    'Jane',
];

// Single line array
$names = ['John', 'Jane'];

Eloquent Models

  • Eloquent models should never use the $fillable attribute, and should always define $guarded = []
  • The ::create() and ->update() methods are preferred over property assignment and the ->save() method
  • Table names should be plural (a table stores multiple models), models should be singular (a model represents a single record)
  • The SoftDeletes trait should be avoided unless specifically required
  • Relation IDs are preferred to be at the beginning of the table
  • Date fields are preferred to be at the end of the table

Laravel Specifics

  • Facades are preferred over dependency injection where available
  • Model records should never be passed into jobs or events
  • Migrations in new projects should throw an exception in the down method to avoid rollbacks

Misc

  • Imports should be alphabetical, not line length.
  • The return construct should have one empty line above them except in switch statements
  • case blocks should be separated from subsequent cases with an empty line
  • strict_types declarations are required
  • All imports in namespaced files should be declared using a use import and not imported at runtime using the \ symbol
  • Class properties in php 7.3 or lower should have one empty line between them. Class properties in 7.4 or higher do not require a space.
  • Interface methods should be separated by an empty line
  • All parameters, arguments and methods should specify typehints and return types where possible
  • Ternary statements requiring newlines should have the ? and : symbols at the start of the line
  • Comparison operators on multiple lines should be at the start of the line
  • The period symbol in multi-line concatenation statements should be at the start of the line