Skip to main content

Coding Standards

Guidelines, best practices, programming styles and conventions in use when writing Sirius Backend code.

Style Guide

All PHP code must follow the PSR-12 Coding Style

Linting is performed in CI and will fail any build that does not meet this standard (excluding test files)

make frontend-lint make membrane-lint or make api-lint can be run locally to check changes

Unit tests

Favour unit tests, over behat and UI tests. Use a few of the latter to test happy paths and integration assumptions, and the majority of the variations as unit or Jest tests

When writing unit test methods please prefer descriptive snake case as it allows long descriptive names, but is easier to read. E.g test_myFunction_throws_an_InvalidArgumentException_when_myVariable_is_null

Writing a new endpoint

When writing a new API endpoint for internal consumption either add a controller or controller method in module/Application/Controller/V1/. Then wire your controller and services using the approach documented in the Commands, Queries and Events article.

When writing a new API endpoint for use by code other than the UI (e.g. the Notify Status Poller or Digideps product) either add a controller or controller method in module/Application/Controller/PublicApiV1/, and add to the openapi specification back-end/docs/api/swagger/api.public.v1.yaml

Factories and Dependency Injection

Laminas-DI can automatically wire a lot of services, negating the need to create factories

Specifying specific services for DI can be done in back-end/module/Application/config/autoload/di.global.php

Where DI does not work or the flexibility of a factory is required, implement the Laminas\ServiceManager\Factory\FactoryInterface and follow

class MyClassFactory implements FactoryInterface
{
    /**
     * @param ContainerInterface $container
     * @param string $requestedName
     * @param array<mixed>|null $options
     * @return MyClass
     */
    public function __invoke(
        ContainerInterface $container,
        $requestedName,
        array $options = null
    ): MyClass {
        if (method_exists($container, 'getServiceLocator')) {
            $container = $container->getServiceLocator();
        }

        return new MyClass(
            $container->get(HardToWireClass::class),
        );
    }
}

Writing a new ZF module

Modules must use the PSR-4 autoloading specification and should use a similar layout to the finance module

Directory structure:

module/Finance/
├── config
│   └── module.config.php
├── src
│   ├── Command
│   ├── CommandHandler
│   ├── Controller
│   ├── Model
│   └── Module.php
└── test
    ├── feature
    ├── functional
    └── unit
This page was last reviewed on 16 December 2020. It needs to be reviewed again on 16 February 2021 by the page owner #opg-sirius-develop .
This page was set to be reviewed before 16 February 2021 by the page owner #opg-sirius-develop. This might mean the content is out of date.