This project is an opinionated guidelines for PHP and Laravel.
You can find PHP guideline and more specific Laravel guidelines.
These guidelines contain my PHP best practices and also my Laravel best practices.
PHP Version >= 7.4
Created by gh-md-toc
Use PSR-1 and PSR-2 for code style.
namespace My\Class\Namespace;
use ClassInterface;
class MyClass extends ParentClass implements ClassInterface, Serializable
{
public const PUBLIC_CONSTANTS = 'publicConstants';
protected const PROTECTED_CONSTANTS = 'protectedConstants';
private const PRIVATE_CONSTANTS = 'privateConstants';
use SomeTrait;
use AnotherTrait;
public $publicProperties;
protected $protectedProperties;
private $privateProperties;
public function __constructor() {}
public function __destruct() {}
public function publicMethods() {}
protected function protectedMethods() {}
private function privateMethods() {}
}
Classes and methods SHOULD be as small as possible.
Methods MUST have only one responsibility, 20 lines is probably a good soft limit.
Example for single responsibility:
// User.php class
// Good β
public function getSummaryDescription(): string
{
return sprintf(
'%s, %d years old, %s',
$this->getFullName(),
$this->getAge(),
$this->getFullAddress()
);
}
public function getFullName(): string
{
//return ...;
}
public function getAge(): int
{
//return ...;
}
public function getFullAddress(): string
{
//return ...;
}
// Bad β
public function getSummaryDescription(): string
{
return sprintf(
'%s %s %s, %d years old, %s %s %s',
$this->civility,
ucfirst($this->firstName),
strtoupper($this->lastName),
intval($this->age),
$this->address,
$this->postalCode,
$this->city,
);
}
Variables name NEED to explain your code, code is auto-documented with naming. You HAVE TO avoid generic names.
// This code is just an illustration for variables naming.
// do not judge the usefulness and effectiveness of this code π
// Good β
public function getPrettyUsersList(array $users): string
{
$prettyUsersList = '';
foreach ($users as $user) {
$prettyUsersList .= ', ' . $user->getFullName();
}
return $prettyUsersList;
}
// Bad β
// getUsersList name is not description the method
public function getUsersList(array $users): string
{
// Variable name is not understandable
$list = '';
// $item is too generic
foreach ($users as $item) {
$list .= ', ' . $item->getFullName();
}
return $list;
}
You SHOULD use type hinting for parameters and return.
// Good β
class MyClass
{
public function myMethod(string $param): void
{
//
}
}
// Bad β
class MyClass
{
public function myMethod($param)
{
//
}
}
Use void
as return type whenever possible to avoid mistakes.
// Good β
class MyClass
{
public function myMethod(): void
{
//
}
}
// Bad β
class MyClass
{
public function myMethod()
{
//
}
}
PhpDoc stands for documentation purposes. Consider to use type hinting insteadof @param
or @return
// Good β
class MyClass
{
/**
* Method description if method name can't be understandable
*/
public function myMethod(string $param): void
{
//
}
}
// Bad β
class MyClass
{
/**
* @param string $param
* @return void
*/
public function myMethod(string $param): void
{
//
}
}
If you use PHP traits (and you have to use it!), for better readability and more understandable diffs, each trait SHOULD be on its own line.
// Good β
class MyClass
{
use FirstTrait;
use SecondTrait;
use SecondTrait {
SecondTrait::traitMethod as duplicateTraitMethod;
}
}
// Bad β
class MyClass
{
use FirstTrait, SecondTrait {
SecondTrait::traitMethod as duplicateTraitMethod;
}
}
You MUST use curly braces for if statements even if there is only one line of code inside.
// Good β
if ($someCondition === true) {
$this->work();
}
// Very bad β
if ($someCondition === true) {$this->work();}
For concatenate strings prefer In-string variables.
You can also use sprintf()
for more complex use cases.
// Very good β
$finalString = "My name is {$name}!";
// Good β
$finalString = sprintf(
'My name is %s!',
$name
);
// Bad β
$finalString = 'My name is ' .$name . '!';
// If you want to update spring
$finalString = "My name is {$name}! I'm {$age} years old";
// Good β
$finalString = sprintf(
'My name is %s!I\'m %d years old',
$name,
$age
);
// Bad β
$finalString = 'My name is ' .$name . '! I\'m ' . $age . ' years old';
In function, you SHOULD put error check (return, exception) first, this last instruction must be a happy end!
// Good β
private function good(): ?int
{
if (!$someCondition) {
return null;
}
$this->work();
if (!$anotherCondition) {
return null;
}
return $this->secondWork();
}
// Bad β
private function bad(): ?int
{
if ($someCondition) {
$this->work();
if ($anotherCondition) {
return $this->secondWork();
}
}
return null;
}
This rule keep code readable and remove a lot of nested methods.
You SHOULD use lookup array as some as possible insteadof switch
.
If using PHP8, you SHOULD prefer match
// Good β
(lookup array)
function getColorFor(string $role): string
{
$colorsByRoles = [
'admin' => 'red',
'customer' => 'green',
'guest' => 'grey',
];
return $colorsByRoles[$role] ?? 'yellow';
}
// Good β
(match PHP8)
function getColorFor(string $role): string
{
return match ($role) {
'admin' => 'red',
'customer' => 'green',
'guest' => 'grey',
default => 'yellow',
};
}
// Bad β
function getColorFor(string $role): string
{
switch ($role) {
case 'admin':
return 'red';
case 'customer':
return 'green';
case 'guest':
return 'grey';
default:
return 'yellow';
}
}
// Very bad β
function getColorFor(string $role): string
{
if ($role === 'admin') {
return 'red';
}
if ($role === 'customer') {
return 'green';
}
if ($role === 'guest') {
return 'grey';
}
return 'yellow';
}
// WIP