Een minimale set PHP-praktijken voor leesbare code
Schone code is geen religie - het is hoe je je toekomstige zelf (en teamgenoten) behoedt voor verwarring. Hieronder vind je een aantal vriendelijke werkwijzen die PHP-code voorspelbaarder en gemakkelijker te onderhouden maken.

⚡ Opmerking: voorbeelden gebruiken Laravel voor de duidelijkheid, maar de ideeën zijn van toepassing op elk framework, CMS of gewoon PHP.
1) PSR-1, PSR-4, PSR-12: gedeelde taal en voorspelbaarheid
Waarom het belangrijk is: consistente structuur en opmaak verwijderen ruis. Reviews richten zich op logica in plaats van spaties en accolades.
Hoe over te nemen:phpcs en een auto-fixer inschakelen, PSR-4 autoloading configureren in composer.json, afstemmen op PSR-12.
Voor:
class usercontroller {function STORE($Req){return User::Create($Req->all());}}Na:
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
class UserController
{
public function store(Request $request): User
{
return User::create($request->all());
}
}2) SOLID zonder ijver: denk eerst aan SRP
Waarom het belangrijk is: een klasse zou één reden moeten hebben om te veranderen. Lagere koppeling, eenvoudiger testen, veiliger refactoren.
Typische valkuil: een "God-object" dat DB, e-mail, logging en validatie doet.
Voor:
class UserManager {
public function register(array $data) {
$user = User::create($data);
Mail::to($user->email)->send(new WelcomeMail($user));
Log::info('User created', ['id' => $user->id]);
return $user;
}
}Na:
class UserService
{
public function __construct(
protected UserRepository $users,
protected Mailer $mailer,
protected Logger $logger,
) {}
public function register(array $data): User
{
$user = $this->users->create($data);
$this->mailer->sendWelcome($user);
$this->logger->userCreated($user);
return $user;
}
}3) Encapsulation by default: wees zuinig met public
Waarom het belangrijk is: een kleiner publiek oppervlak beschermt je API en houdt internals flexibel.
Voor:
class ReportGenerator {
public function connectDb() {/*...*/}
public function fetchData() {/*...*/}
public function renderReport() {/*...*/}
}Na:
class ReportGenerator {
protected function connectDb() {/*...*/}
protected function fetchData() {/*...*/}
public function renderReport() {/*...*/}
}4) Strict typing: typ parameters en returns
Waarom het belangrijk is: minder verrassingen, betere IDE hints, veiliger refactoren. Geef de voorkeur aan DTO's/waardeobjecten boven losse arrays.
Voor:
public function calculate($a, $b) {
return $a + $b;
}Na:
public function calculate(int $a, int $b): int {
return $a + $b;
}5) ≤ 20 regels per methode: controleer abstractieniveaus
Waarom het belangrijk is: lange methoden zijn vaak een mix van validatie, domeinregels, neveneffecten en I/O.
Voor:
public function processOrder(array $data) {
// validate...
// create order...
// charge...
// email...
// log...
}Na:
public function processOrder(array $data) {
$this->validate($data);
$order = $this->createOrder($data);
$this->charge($order);
$this->notify($order);
$this->log($order);
}6) Dubbel op 2+ plaatsen? Extraheer
Waarom het belangrijk is: DRY vermindert drift. De ene kopie repareren maar de andere niet is een klassieke bron van bugs.
Voor:
$user = User::create($data);
Mail::to($user->email)->send(new WelcomeMail($user));
$admin = User::create($adminData);
Mail::to($admin->email)->send(new WelcomeMail($admin));Na:
class UserService {
public function register(array $data): User {
$user = User::create($data);
Mail::to($user->email)->send(new WelcomeMail($user));
return $user;
}
}7) Repository + Service: dunne controllers, duidelijk domein
Waarom het belangrijk is: DB-toegang (repositories) gescheiden houden van zakelijke use-cases (services) maakt code verklaarbaar en testbaar.
Voor:
public function index()
{
return User::where('active', true)->get();
}Na:
class UserRepository {
public function active(): Collection {
return User::where('active', true)->get();
}
}
class UserService {
public function __construct(private UserRepository $users){}
public function getActive(): Collection { return $this->users->active(); }
}8) Strikte vergelijking en expliciete voorwaarden
Waarom het belangrijk is:=== vermijdt de eigenzinnige dwang van PHP. Gebruik if ($var) alleen voor gegarandeerde booleans.
Voor:
if ($user->age == '18') { /* ... */ }
if ($discount) { /* ... */ } // '0' is falsyNa:
if ($user->age === 18) { /* ... */ }
if ($discount !== null && $discount > 0) { /* ... */ }9) Naamgeving: optimaliseren voor aandacht van de lezer
Waarom het belangrijk is: goede namen zijn ingebouwde documentatie. Neem eenheden/format op waar relevant.
Voor:
$amount = 100;Na:
$orderTotalCents = 100;10) PHP 8+ Constructor Promotie
Waarom het belangrijk is: minder boilerplate, duidelijkere bedoeling.
final class CreateInvoice
{
public function __construct(
private CustomerRepository $customers,
private TaxCalculator $taxes,
) {}
public function handle(int $customerId, int $amountCents): Invoice { /*...*/ }
}11) Laravel Query Builder: gebruik voorwaardelijke clausules
Waarom het belangrijk is: elimineer het if-woud rond query's; bouw ze compositorisch op.
Voor:
$query = User::query();
if ($active) { $query->where('active', true); }
if ($role) { $query->where('role', $role); }
if ($fromDate) { $query->whereDate('created_at', '>=', $fromDate); }
$users = $query->get();Na:
$users = User::query()
->when($active, fn($q) => $q->where('active', true))
->when($role, fn($q) => $q->where('role', $role))
->when($fromDate, fn($q) => $q->whereDate('created_at', '>=', $fromDate))
->get();12) Meerdere DB-statusmutaties? Verpak in een transactie
Waarom het belangrijk is: behoud consistentie, zelfs als een stap halverwege mislukt.
Voor:
$order = Order::create($data);
$payment->charge($order->total);
$order->markPaid();Na:
DB::transaction(function () use ($data, $payment) {
$order = Order::create($data);
$payment->charge($order->total);
$order->markPaid();
});13) Vroeg terugkeren met bewakingsclausules
Waarom het belangrijk is: minder nesten, makkelijker scannen, minder gemiste else takken.
Voor:
public function handle(?User $user) {
if ($user) {
if ($user->isActive()) {
// ...
}
}
}Na:
public function handle(?User $user) {
if (!$user || !$user->isActive()) {
return;
}
// main logic
}14) Vermijd waarheidsgetrouwheid als de bedoeling onduidelijk is
Waarom het belangrijk is:if ($variable) verbergt type ambiguïteit; wees expliciet voor strings/nummers/arrays.
Voor:
if ($limit) { /* ... */ } // '0' becomes falseNa:
if ($limit !== null) { /* ... */ }15) Automatiseer zodat gewoonten niet vervallen
Waarom het belangrijk is: mensen zijn inconsistent; tools niet. Laat automatisering stijl en veiligheid in toom houden.
- Pre-commit: fixer + PHPCS + PHPStan/Psalm
- CI: dezelfde controles + tests
- IDE sjablonen/snippets voor veelgebruikte klassen
- Laravel: draai Pint (
./vendor/bin/pint)
16) Richtlijnen, geen geboden
Het echte leven heeft deadlines, erfenissen en beperkingen. Breek regels bewust, laat een notitie achter(waarom) en een achterstallige taak(wat te verbeteren). Het doel is leesbaarheid en voorspelbaarheid, niet rituele zuiverheid.
Neem contact op
Een externe audit van je project nodig?
Vertel ons je context en het resultaat dat je wilt, en wij stellen de eenvoudigste volgende stap voor.