๐Ÿงฉ

PHP ยท Les 8 โ€” Laatste les

Klassen en namespaces

OOP-basis: klassen, constructor, visibility, inheritance en namespaces โ†’ brug naar Symfony ยท 30 min

Klassen en objecten

Een klasse is een blauwdruk. Een object is een instantie van die blauwdruk.

<?php
// Klasse definitie
class Gebruiker {
    // Properties (kenmerken)
    public string $naam;
    public string $email;
    public int    $leeftijd;

    // Methoden (gedrag)
    public function begroet(): string {
        return "Hallo, ik ben {$this->naam}!";
    }

    public function isVolwassen(): bool {
        return $this->leeftijd >= 18;
    }
}

// Object aanmaken met new
$jan = new Gebruiker();
$jan->naam      = "Jan de Vries";
$jan->email     = "jan@example.com";
$jan->leeftijd  = 20;

echo $jan->begroet();           // Hallo, ik ben Jan de Vries!
echo $jan->isVolwassen() ? "Volwassen\n" : "Minderjarig\n";

// Meerdere objecten van dezelfde klasse
$marie = new Gebruiker();
$marie->naam = "Marie Jansen";
$marie->leeftijd = 17;
echo $marie->isVolwassen() ? "Volwassen\n" : "Minderjarig\n";

โš ๏ธ Let op verschil met JS

De klasse-syntax lijkt sterk op JS (ES6+). Groot verschil: PHP gebruikt $this->property met een pijl (->), terwijl JS this.property met een punt gebruikt. En PHP properties hebben expliciet type-declaraties.

Constructor en property promotion

<?php
// Traditionele constructor
class Product {
    public string $naam;
    public float  $prijs;
    public int    $voorraad;

    public function __construct(string $naam, float $prijs, int $voorraad = 0) {
        $this->naam     = $naam;
        $this->prijs    = $prijs;
        $this->voorraad = $voorraad;
    }

    public function getTotaalwaarde(): float {
        return $this->prijs * $this->voorraad;
    }
}

// Constructor property promotion (PHP 8) โ€” korter en cleaner
class ProductV2 {
    public function __construct(
        public readonly string $naam,
        public float           $prijs,
        public int             $voorraad = 0,
    ) {}  // Body leeg โ€” alles staat in de parameters!

    public function getTotaalwaarde(): float {
        return $this->prijs * $this->voorraad;
    }
}

$laptop = new ProductV2("Laptop", 899.99, 5);
echo $laptop->naam;                       // Laptop
echo $laptop->getTotaalwaarde();          // 4499.95

// readonly โ€” mag na aanmaken niet gewijzigd worden
// $laptop->naam = "Anders";  // Error!

๐Ÿ”— Komt terug in Symfony

Constructor property promotion gebruik je constant in Symfony-services: public function __construct(private readonly ProductRepository $repo) {}. Symfony injecteert automatisch de ProductRepository via dependency injection op basis van het type.

Visibility: public, private, protected

<?php
class BankRekening {
    public  string $rekeningNummer;   // Overal leesbaar
    private float  $saldo;            // Alleen binnen deze klasse
    protected string $eigenaar;       // Klasse + subklassen

    public function __construct(string $nr, string $eigenaar, float $beginSaldo = 0) {
        $this->rekeningNummer = $nr;
        $this->eigenaar       = $eigenaar;
        $this->saldo          = $beginSaldo;
    }

    // Getter โ€” gecontroleerde toegang tot private property
    public function getSaldo(): float {
        return $this->saldo;
    }

    // Setter โ€” validatie voor schrijven
    public function storten(float $bedrag): void {
        if ($bedrag <= 0) throw new \InvalidArgumentException("Bedrag moet positief zijn");
        $this->saldo += $bedrag;
    }

    public function opnemen(float $bedrag): void {
        if ($bedrag > $this->saldo) throw new \RuntimeException("Onvoldoende saldo");
        $this->saldo -= $bedrag;
    }
}

$rekening = new BankRekening("NL01TEST0001", "Jan", 100.00);
$rekening->storten(50);
echo $rekening->getSaldo();  // 150
// $rekening->saldo = 9999;  // Fatal error โ€” private!

Overzicht visibility

public

Overal

protected

Klasse + children

private

Alleen de klasse zelf

โš ๏ธ Let op verschil met JS

In moderne JS gebruik je #saldo voor private properties (ES2022). In PHP heb je al tientallen jaren private. PHP's systeem is duidelijker en wordt in de hele Symfony-codebase gebruikt.

Inheritance (overerving)

<?php
// Basisklasse (parent)
class Dier {
    public function __construct(
        protected string $naam,
        protected int    $leeftijd,
    ) {}

    public function beschrijving(): string {
        return "{$this->naam} ({$this->leeftijd} jaar)";
    }

    public function geluid(): string {
        return "...";
    }
}

// Subklasse โ€” erft van Dier
class Hond extends Dier {
    public function __construct(string $naam, int $leeftijd, private string $ras) {
        parent::__construct($naam, $leeftijd);  // Roep parent constructor aan
    }

    // Method override
    public function geluid(): string {
        return "Woef!";
    }

    public function beschrijving(): string {
        return parent::beschrijving() . " โ€” {$this->ras}";
    }
}

class Kat extends Dier {
    public function geluid(): string { return "Miauw!"; }
}

$hond = new Hond("Rex", 3, "Labrador");
$kat  = new Kat("Whiskers", 5);

echo $hond->beschrijving() . "\n";  // Rex (3 jaar) โ€” Labrador
echo $hond->geluid() . "\n";        // Woef!
echo $kat->geluid() . "\n";         // Miauw!

// instanceof controle
var_dump($hond instanceof Dier);   // bool(true)

๐Ÿ”— Komt terug in Symfony

Symfony gebruikt inheritance overal: class ProductController extends AbstractController geeft je toegang tot render(), redirectToRoute(), etc. class ProductRepository extends ServiceEntityRepository geeft je Doctrine-query-methoden. Je schrijft alleen wat specifiek is voor jouw klasse.

Namespaces en autoloading

Namespaces voorkomen naamconflicten en organiseren grote projecten. PSR-4 autoloading via Composer zorgt dat klassen automatisch geladen worden.

<?php
// src/Entity/Product.php
namespace App\Entity;

class Product {
    public function __construct(
        private string $naam,
        private float  $prijs,
    ) {}
    public function getNaam(): string { return $this->naam; }
}

// ---

// src/Service/PrijsCalculator.php
namespace App\Service;

use App\Entity\Product;        // Importeer Product-klasse
use App\Repository\ProductRepository;

class PrijsCalculator {
    public function __construct(
        private readonly ProductRepository $repo
    ) {}

    public function berekenKorting(Product $product, float $pct): float {
        return $product->getPrijs() * (1 - $pct / 100);
    }
}

// ---
// composer.json โ€” PSR-4 autoloading
// "autoload": { "psr-4": { "App\\": "src/" } }
// Composer vindt App\Entity\Product in src/Entity/Product.php

Namespace-regels in Symfony

โ†’ namespace App\Controller; โ†’ bestand in src/Controller/

โ†’ namespace App\Entity; โ†’ bestand in src/Entity/

โ†’ namespace App\Service; โ†’ bestand in src/Service/

โ†’ use importeert klassen uit andere namespaces

๐Ÿ”— Komt terug in Symfony

Elke PHP-file in een Symfony-project begint met namespace App\.... Symfony's Dependency Injection Container weet welke services beschikbaar zijn door namespaces te scannen. Jij schrijft klassen in de juiste map en Symfony doet de rest.

Sandbox โ€” OOP in de praktijk

Bouw een kleine productcatalogus met klassen, inheritance en type hints.

// output

Klik op Uitvoeren...

Kennischeck

Wat je nu weet

โœ“PHP-tags, echo, var_dump en comments
โœ“Variabelen, datatypes en type juggling
โœ“if/elseif/else, switch/match, == vs ===
โœ“for, while, foreach, break en continue
โœ“Indexed, associatieve en geneste arrays
โœ“Functies, type hints, scope, closures
โœ“$_GET, $_POST, htmlspecialchars, validatie
โœ“Klassen, inheritance en namespaces

PHP-track afronden

Markeer alle 8 lessen als voltooid en start de Symfony-track.