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