Skip to content

Coding Standards

We follow PSR-12 as a baseline for PHP code style, with team-specific additions described here.


PHP version

All projects and packages currently run PHP 7.4. Avoid features introduced in PHP 8.0+ (named arguments, enums, readonly properties, match expressions, union types, etc.) unless explicitly agreed upon for a specific project.


Code style (PSR-12 + PHP-CS-Fixer)

Code formatting is handled automatically by PHP-CS-Fixer. No manual whitespace discussions in PR reviews.

Run locally:

vendor/bin/php-cs-fixer fix src/

Standard .php-cs-fixer.dist.php:

<?php

$finder = PhpCsFixer\Finder::create()
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/tests');

return (new PhpCsFixer\Config())
    ->setRules([
        '@PSR12' => true,
        '@PHP74Migration' => true,
        'declare_strict_types' => true,
        'ordered_imports' => ['sort_algorithm' => 'alpha'],
        'no_unused_imports' => true,
        'trailing_comma_in_multiline' => true,
        'array_syntax' => ['syntax' => 'short'],
    ])
    ->setFinder($finder);

Static analysis (PHPStan)

All projects run PHPStan level 8 as a minimum. New projects start at level 9.

vendor/bin/phpstan analyse src --level=8

Naming conventions

Element Convention Example
Classes PascalCase UserInvitationService
Interfaces PascalCase + Interface suffix PaymentGatewayInterface
Traits PascalCase + Trait suffix HasTimestampsTrait
Enums PascalCase InvoiceStatus
Methods camelCase sendVerificationEmail()
Properties camelCase $billingAddress
Constants UPPER_SNAKE_CASE MAX_RETRY_ATTEMPTS
Files PascalCase (one class per file) UserInvitationService.php

General principles

Strict types: All PHP files start with declare(strict_types=1);

Type declarations: All methods have full type annotations — parameters, return types, and property types. Use mixed only as a last resort.

Dependency injection: Inject dependencies via constructor. Avoid service locators and static methods for dependencies.

Single responsibility: A class has one responsibility. If a class is hard to name precisely, it's probably doing too much.

Immutability: Prefer readonly properties and value objects over mutable state.


What we don't do

  • We don't use var_dump() or dd() in committed code
  • We don't commit composer.lock conflicts unresolved
  • We don't add @suppress to PHPStan errors without a comment explaining why
  • We don't use array as a type hint where a specific type is available