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.

Een minimale set PHP-praktijken voor leesbare code

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 falsy

Na:

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 false

Na:

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.

Door te verzenden ga je ermee akkoord dat we je gegevens verwerken om op je aanvraag te reageren en, indien van toepassing, precontractuele stappen te nemen op jouw verzoek (AVG art. 6(1)(b)) of op basis van ons gerechtvaardigd belang (art. 6(1)(f)). Deel geen bijzondere persoonsgegevens. Zie ons Privacybeleid.
Reactie binnen 1 werkdag.