# PHP
All general PHP guidelines MUST be followed as long as not overridden by a more precise guideline scope.
# PSRs
# Namespaces and Autoloading
Namespaces, directories and autoloading MUST follow PSR-4.
# Coding Style
The PHP code style MUST follow PSR-1, PSR-2 & PSR-12 - the higher PSR can override the lower ones.
# OOP
Every PHP code SHOULD be written in classes, traits, interfaces to be OOP compatible.
# SOLID code
Every PHP code SHOULD follow the SOLID principle. You can read more about from STUPID to SOLID code.
- Single Responsibility Principle
A class should only have a single responsibility, that is, only changes to one part of the software's specification should be able to affect the specification of the class.
- Open/Closed Principle
Software entities should be open for extension, but closed for modification.
- Liskov Substitution Principle
Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.
- Interface Segregation Principle
Many client-specific interfaces are better than one general-purpose interface.
- Dependency Inversion Principle
One should depend upon abstractions, not concretions.
# Types
Every method and function SHOULD be type-hinted as much as possible (arguments & return type). This way you MUST think twice about mixed types.
# Docblocks
- You MUST NOT use docblocks for methods that can be fully type hinted (unless you need a description).
- You SHOULD only add a description when it provides more context than the method signature itself.
- You MUST use full sentences for descriptions, including a period at the end.
# Imports
- You MUST import
use
all classes referenced in this document/class at the top of the file. - You MUST import
use
global classes - likeClosure
andException
. - You SHOULD alias classes with common naming conflicts by default
use GuzzleHttp\Client as GuzzleClient;
.
# Helpers
Helpers are files with global functions not wrapped inside a class.
- You MUST reduce the amount of helper files to an absolute minimum - in best case a single one or none.
- Helper functions MUST wrap an already available method and MUST NOT introduce new logic.
# Naming
- You MUST NOT use a prefix to indicate it's visibility.
- You MUST NOT use a prefix to indicate it's type.
# Namespaces
- You MUST use
UpperCamelCase
for namespaces. - You MUST use namespaces for multiple classes instead of a prefix.
\AdminUsersController
=>\Admin\UsersController
- You MUST use the namespace as directory path.
# Classes
- You MUST use
UpperCamelCase
for class names. - You MUST use the class name as file name.
- You SHOULD use a suffix even if it's already part of the namespace.
\Contracts\User
=>\Contracts\UserContract
# Properties
- You MUST use
$lowerCamelCase
for property names.
# Methods
- You MUST use
lowerCamelCase()
for method names. - You MUST prefix methods with
bool
return byis
orhas
. - You MUST prefix getter/accessor methods by
get
. - You MUST prefix setter/mutator methods by
set
.
# Functions
- You MUST use
lower_snake_case()
for function names. - You MUST prefix methods with
bool
return byis_
orhas_
.
# Variables
- You MUST use
$lowerCamelCase
for variable names.
# Array Keys
- You MUST use
lower_snake_case
for array keys as long as not REQUIRED different by a foreign package or API.
# Macro Methods
Macros are const
like methods - for example if you construct a "constant" string from config values.
- You MUST use the default method naming for macros -
lowerCamelCase()
.
# Test Cases
- You SHOULD use following pattern to name your test case methods:
[entity:it]_can_[do_something]{_when_[some_condition]}
# Enums
We use spatie/enum or the spatie/laravel-enum wrapper for Enums in PHP code.
- You MUST use
UPPER_SNAKE_CASE
for enum naming.
# Conditions
# IF statements
You MUST use curly-brackets for if
statements and MUST NOT use single line if
statements.
// good
if ($condition) {
// conditional code
}
// bad
if ($condition) // conditional code
# early return
You SHOULD use early returns to prevent complex condition blocks and indentation.
# happy path
Following the early return rule the happy path SHOULD be the last return and unindented.
// good
if (is_null($value)) {
throw new Exception;
}
return $value;
// bad
if (! is_null($value)) {
return $value;
}
throw new Exception;
# avoid else
Following the early return rule you SHOULD NOT use/need an else
block.
// good
if (is_null($value)) {
throw new Exception;
}
return $value;
// bad
if (is_null($value)) {
throw new Exception;
} else {
return $value;
}
# complex conditions
You SHOULD use separated if
statements instead of complex &&
or ||
joined ones. This increases readability and separated error handling.
// good
if (is_null($value)) {
return;
}
if (! is_string($value)) {
return;
}
if (strlen($value) >= 50) {
return;
}
return $value;
// bad
if (! is_null($value) && is_string($value) && strlen($value) < 50) {
return $value;
}
# condition negation
You SHOULD NOT use negation if possible but the early return, happy path and complex conditions rules MUST be respected before.
// good
if (is_null($index)) {
return;
}
// bad
if (! is_null($index)) {
return;
}
# negated instance checking
You SHOULD use ! is_a()
instead of ! $class instanceof Class
if you have to negate an instance of condition.
// good
if (! is_a($object, MyClass::class)) {
//
}
// bad
if (! $object instanceof MyClass) {
//
}
# indirect boolean casting
You SHOULD NOT use indirect boolean casting. Instead you SHOULD use comparison operators, is_xyz()
functions or bool_val()
.
PHP has some strange type-weak comparisons which we want to avoid: https://www.php.net/manual/de/types.comparisons.php
// good
function hasColor() {
return is_string($this->color);
}
// bad
function hasColor() {
return !!$this->color;
}
# type safe comparison
You SHOULD use type safe comparison whenever possible instead of weak comparison.
// good
return $isAccepted === true;
return $foo === 'bar';
return $value === null;
return $value === null;
return $value === '';
// bad
return (bool) $isAccepted;
return $foo == 'bar';
return is_null($value);
return empty($value);
return empty($value);
# ternary operators
You MUST place every portion of a ternary expression on its own line and start with the operator unless it's a simple and short expression.
// good
$result = $object instanceof Model
? $object->name
: 'A default value';
$name = $isFoo ? 'foo' : 'bar';
// bad
$result = $object instanceof Model ? $object->name : 'A default value';
$result = $object instanceof Model ?
$object->name :
'A default value';
# Strings
# early casting/transforming
You MUST cast a string
as early as possible to another type if it's only a "wrapper" used in DB, form or wherever.
// good
$agbAccepted = $request['agb_accepted'] === 'yes';
if (! $agbAccepted) {
throw new AgbNotAcceptedException();
}
$model->agb_accepted = $agbAccepted;
// bad
if ($request['agb_accepted'] !== 'yes') {
throw new AgbNotAcceptedException();
}
$model->agb_accepted = $request['agb_accepted'] === 'yes';
# Usage
# compact()
- You MUST use explicit array definitions in business logic.
- You SHOULD use
compact()
in test classes - for example to generate routes with model params.
Laravel →