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:
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.
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()ordd()in committed code - We don't commit
composer.lockconflicts unresolved - We don't add
@suppressto PHPStan errors without a comment explaining why - We don't use
arrayas a type hint where a specific type is available