PHP Tutorial.nl

Word een PHP expert!!!

Home » Classes

Classes: maak zelf objecten met een eigen verantwoordelijkheid

Na functies is het een kleine stap naar een class (klasse). Een class is namelijk een verzameling van functies. Dit klinkt misschien vrij abstract en vrijblijvend. En nee, een class hoort niet 30 willekeurige functies te bevatten. Om het minder abstract te maken: een class heeft samenhang.

Wat is een class?

Een class in PHP is een blauwdruk voor het creëren van objecten. Het definieert de eigenschappen (variabelen) en methoden (functies) die de objecten zullen hebben. Met classes kun je objectgeoriënteerd programmeren (OOP) toepassen, wat helpt bij het structureren en organiseren van je code. Een class kan ook de toegang tot zijn eigenschappen en methoden beperken met toegangsmodificatoren (public, protected, private). Daarnaast kunnen classes gebruikmaken van inheritance, abstracte klassen, interfaces, traits, en readonly eigenschappen voor extra functionaliteit en herbruikbaarheid.

Stel nu dat we een ‘Medewerker’ hebben. Dit is iets met bepaalde eigenschappen zoals: uurloon en aantal uren. Voor deze medewerker kunnen we bepalen hoeveel hij kost voor x aantal uren. Dit laatste is een ‘functie’. In de context van een class noemen we dit een methode.

<?php
class Medewerker {
    public $uurloon = 10;
    public $aantal_uren = 40;
   
    public function weekloon() {
       return $this->uurloon * $this->aantal_uren;
    }
}

Dit lijkt vrij veel op de functie die eerder beschreven is. We hebben een class, met daarachter de naam van de class: Medewerker. De inhoud van deze class staat tussen de { en }.

We zien vervolgens twee dingen: ‘variabelen’ en ‘functies’. De variabelen zijn in dit geval attributen (properties) en de functies worden methodes (methods) genoemd.

Iets anders wat opvalt is de variabele $this. Deze variabele is een verwijzing naar de class zelf. Door middel van $this is het mogelijk om attributen en methodes binnen de class zelf aan te roepen.

Daarnaast hebben we ‘public‘. Dit geeft de zichtbaarheid van een attribuut of methode aan. Andere opties zijn protected en private. Hierover verderop meer uitleg.

Class instantiëren

Het hebben van een class is leuk, maar we kunnen er niet nog iets mee. We moeten het eerst instantiëren en dan kunnen we de publieke functionaliteit ervan aanroepen. Als een class eenmaal is geïnstantieerd dan noemen we deze instantie ook wel het object.

<?php
$medewerker = new Medewerker();
echo $medewerker->weekloon();
?>

Constructors en Destructors

Constructors zijn speciale methoden die automatisch worden aangeroepen wanneer een object van een klasse wordt gemaakt. Ze worden gebruikt om het object te initialiseren.

<?php
class MijnKlasse {
    public function __construct() {
        echo "Het object is aangemaakt.";
    }
}

$object = new MijnKlasse(); // Output: Het object is aangemaakt.
?>

Destructors worden aangeroepen wanneer een object wordt vernietigd of de scriptuitvoering eindigt. Ze worden gebruikt om op te ruimen.

<?php
class MijnKlasse {
    public function __destruct() {
        echo "Het object is vernietigd.";
    }
}

$object = new MijnKlasse(); 
// Output: Het object is vernietigd wanneer het script eindigt.
?>

Constructor promotion in PHP

Constructor promotion is een handige syntaxis in PHP 8 waarmee je eigenschappen van een klasse kunt definiëren en initialiseren in de constructor zelf. Dit vermindert de boilerplate code en maakt je klassen leesbaarder en onderhoudbaarder.

<?php
class Persoon {
    public function __construct(
        public string $naam,
        public int $leeftijd,
        private string $geslacht
    ) {}
}

$persoon = new Persoon("Alice", 28, "Vrouw");
echo $persoon->naam; // Output: Alice
?>

In dit voorbeeld worden de eigenschappen $naam, $leeftijd en $geslacht rechtstreeks in de constructor gedefinieerd en geïnitialiseerd. De zichtbaarheid van de eigenschappen wordt ook in de constructor bepaald (public, private). Dit maakt de klasse korter en overzichtelijker, omdat je niet langer afzonderlijke eigenschapsdefinities en initialisaties in de constructor hoeft te schrijven.

Uitbreiden (extenden) van een class

Soms wil je functionaliteit van een class uitbreiden, zonder daarvoor een groot deel van de definitie te dupliceren. Dit heet Inheritance. 

Het is uitbreiden oftewel extenden heeft wel een limiet. Elke class mag slechts één andere class (of abstracte class) uitbreiden. Wel mag de class die uitgebreid wordt zelf ook weer een class extenden. Let wel op: te diepgaande inheritance kan onoverzichtelijk worden; waar komt nu methode x precies vandaan?

Stel we hebben een variant op de Medewerker: Senior Medewerker. Deze werkt ook 40 uur en heeft hetzelfde uurloon, maar hij krijgt wel een bonus van 50 euro per week.

<?php
class SeniorMedewerker extends Medewerker {

    public function weekloon() {
       $weekloon = parent::weekloon();
       return $weekloon + 50;
    }
}

In het voorbeeld hierboven maken we een nieuwe class aan: SeniorMedewerker. Deze extends de Medewerker. Hij pakt dus alles wat in de class Medewerker zit en breid vervolgens de class uit.

We roepen in dit geval de parent::weekloon() methode aan. De parent is hier dus Medewerker. Het resultaat daarvan tellen we op bij de 50 euro die een Senior Medewerker extra krijgt. We maken hierbij gebruik van Overerving.

Bij overerving pakken we het resultaat van de parent en die gebruiken we om iets extra’s mee te doen. We kunnen ook een eigen implementatie van weekloon schrijven, zonder te kijken naar de parent. We overschrijven in dat geval dus de parent. Dat principe heet Overriding.

Methoden in Classes

Methoden zijn functies die binnen een klasse worden gedefinieerd en gebruikt om het gedrag van objecten te definiëren. Methoden kunnen dezelfde acties uitvoeren als gewone functies, maar hebben toegang tot de eigenschappen van het object.

<?php
class Auto {
    public $merk;
    public $model;

    public function __construct($merk, $model) {
        $this->merk = $merk;
        $this->model = $model;
    }

    public function beschrijf() {
        return "Deze auto is een $this->merk $this->model.";
    }
}

$auto = new Auto("Toyota", "Corolla");
echo $auto->beschrijf(); // Output: Deze auto is een Toyota Corolla.
?>

In dit voorbeeld definiëren we de methode beschrijf binnen de klasse Auto. Deze methode heeft toegang tot de eigenschappen $merk en $model van het object.

Late Static Binding

Om je kennis van het object georiënteerd programmeren in PHP verder te verdiepen, is het essentieel om te begrijpen hoe Late Static Binding (LSB) werkt. LSB zorgt ervoor dat methoden de juiste context gebruiken, zelfs wanneer ze in een afgeleide klasse worden aangeroepen. Dit is bijzonder nuttig in complexe hiërarchieën van classes.

Properties in Classes

Eigenschappen (properties) zijn variabelen die binnen een klasse worden gedefinieerd en die de staat van objecten beschrijven. Properties worden meestal geïnitialiseerd wanneer een object wordt gemaakt en kunnen publiek, beschermd of privé zijn.

<?php
class Persoon {
    public $naam;
    protected $leeftijd;
    private $geslacht;

    public function __construct($naam, $leeftijd, $geslacht) {
        $this->naam = $naam;
        $this->leeftijd = $leeftijd;
        $this->geslacht = $geslacht;
    }

    public function toonNaam() {
        return $this->naam;
    }
}

$persoon = new Persoon("John", 30, "Mannelijk");
echo $persoon->toonNaam(); // Output: John
?>

In dit voorbeeld definiëren we drie eigenschappen ($naam, $leeftijd, $geslacht) binnen de klasse Persoon. De eigenschappen kunnen verschillende toegangsmodificatoren hebben: public, protected en private. Public eigenschappen zijn toegankelijk buiten de klasse, protected eigenschappen zijn alleen toegankelijk binnen de klasse en afgeleide klassen, en private eigenschappen zijn alleen toegankelijk binnen de klasse zelf.

Access Modifiers (Toegangsmodificatoren)

Het is mogelijk om invloed uit te oefenen op de zichtbaarheid van attributen en methodes. Dit doen we met zogeheten toegangsmodificatoren. Over het algemeen zal er meestal gekozen worden voor public functionaliteit. Deze functionaliteit is benaderbaar binnen en buiten de class instantie.

Alternatieven zijn protected en private. Door een attribuut of methode protected te maken zorg je ervoor dat deze alleen bij de class zelf en bij de class die erft (inherit) beschikbaar is.  Een private attribuut of methode is alleen toegankelijk voor de class zelf en dus niet daarbuiten.

Public

Een public eigenschap of methode is toegankelijk vanuit elke context.

<?php
class MijnKlasse {
    public $publiekeEigenschap = "Ik ben publiek!";
}

$object = new MijnKlasse();
echo $object->publiekeEigenschap; // Output: Ik ben publiek!
?>

Protected

Een protected eigenschap of methode is alleen toegankelijk binnen de klasse zelf en door klassen die ervan overerven.

<?php
class MijnKlasse {
    protected $beschermdeEigenschap = "Ik ben beschermd!";
}

class KindKlasse extends MijnKlasse {
    public function toonEigenschap() {
        return $this->beschermdeEigenschap;
    }
}

$object = new KindKlasse();
echo $object->toonEigenschap(); // Output: Ik ben beschermd!
?>

Private

Een private eigenschap of methode is alleen toegankelijk binnen de klasse zelf.

<?php
class MijnKlasse {
    private $privéEigenschap = "Ik ben privé!";
    
    public function toonEigenschap() {
        return $this->privéEigenschap;
    }
}

$object = new MijnKlasse();
echo $object->toonEigenschap(); // Output: Ik ben privé!
?>

Het return type: wat geeft een methode terug

In de recentere PHP-versies is het mogelijk om aan te geven wat voor type een methode precies teruggeeft. Zo weet je als ontwikkelaar wat je kan verwachten bij de implementatie van classes en hoef je geen conversies meer te doen. Het return type kan een van de datatypes zijn uit PHP zelf of bijvoorbeeld een class die je zelf hebt geschreven.

<?php
class SeniorMedewerker extends Medewerker {

    public function weekloon(): int {
       $weekloon = parent::weekloon();
       return $weekloon + 50;
    }
}

In het bovenstaande voorbeeld geven we met :int aan, dat er een integer wordt teruggeven. Geeft je methode niets terug, dan is er sprake van een void en ook dat kunnen we in de code aangeven.

<?php
class SeniorMedewerker extends Medewerker {

    public function voorstellen(): void {
       echo 'Ik ben Andy';
    }
}

En je hebt ook de mogelijkheid om een combinatie zowel een null als een specifiek type terug te geven.

<?php
class SeniorMedewerker extends Medewerker {
    private bool $isOntslagen = false;

    public function verantwoordelijkheden(): ?string {
       if ( $this->isOntslagen === true ) {
          return null;
       } 

       return 'Geeft sturing';
    }
}

Readonly in PHP classes

Sinds PHP 8.1 kun je readonly eigenschappen gebruiken. Deze eigenschappen kunnen alleen tijdens de initialisatie worden ingesteld en zijn daarna onveranderlijk. Dit zorgt voor meer veiligheid en integriteit van objecten.

<?php
class MijnKlasse {
    public readonly string $naam;

    public function __construct(string $naam) {
        $this->naam = $naam;
    }
}

$object = new MijnKlasse("John");
echo $object->naam; // Output: John
$object->naam = "Doe"; // Fout: Cannot modify readonly property
?>

Het final keyword in PHP

Het final keyword in PHP wordt gebruikt om klassen en methoden te definiëren die niet kunnen worden uitgebreid of overschreven door subklassen. Dit is nuttig om te voorkomen dat de functionaliteit van een klasse of methode wordt veranderd in afgeleide klassen, wat kan helpen bij het behouden van de integriteit van de code en het waarborgen van een consistente werking.

Final classes

Een klasse die als final is gemarkeerd, kan niet worden uitgebreid.

<?php
final class NietUitbreidbareKlasse {
    public function zegHallo() {
        echo "Hallo!";
    }
}

// Dit zal een fout veroorzaken
class SubKlasse extends NietUitbreidbareKlasse {
}
?>

Final methodes

Een methode die als final is gemarkeerd, kan niet worden overschreven in een subklasse.

<?php
class BasisKlasse {
    final public function definitieveMethode() {
        echo "Dit is een final methode.";
    }
}

class SubKlasse extends BasisKlasse {
    // Dit zal een fout veroorzaken
    public function definitieveMethode() {
        echo "Probeer deze methode te overschrijven.";
    }
}
?>

Het gebruik van het final keyword is een krachtige manier om ervoor te zorgen dat bepaalde delen van je codebase onveranderd blijven en de oorspronkelijke functionaliteit behouden blijft.

Readonly in PHP Classes

Sinds PHP 8.1 kun je readonly eigenschappen gebruiken. Deze eigenschappen kunnen alleen tijdens de initialisatie worden ingesteld en zijn daarna onveranderlijk. Dit zorgt voor meer veiligheid en integriteit van objecten.

<?php
class MijnKlasse {
    public readonly string $naam;

    public function __construct(string $naam) {
        $this->naam = $naam;
    }
}

$object = new MijnKlasse("John");
echo $object->naam; // Output: John
$object->naam = "Doe"; // Fout: Cannot modify readonly property
?>

Readonly eigenschappen zorgen ervoor dat een eigenschap na initialisatie niet meer kan worden aangepast, wat nuttig is voor het beschermen van gevoelige gegevens in objecten.

Static in PHP Classes

Static methodes en properties in PHP classes bieden een manier om functionaliteit te definiëren die niet afhankelijk is van een specifieke instantie van een klasse. Dit maakt het mogelijk om methodes en eigenschappen te gebruiken zonder eerst een object te creëren.

Static methodes zijn handig voor hulpfuncties, terwijl static properties gedeelde waarden tussen alle instanties van een klasse kunnen bevatten. Hoewel ze nuttig kunnen zijn, hebben ze ook beperkingen, zoals verminderde testbaarheid en uitbreidbaarheid.

<?php
class Hulpmiddelen {
    public static function zegHallo() {
        echo "Hallo!";
    }
}

Hulpmiddelen::zegHallo(); // Output: Hallo!
?>

Abstracte Klassen

Abstracte klassen kunnen niet worden geïnstantieerd en worden gebruikt als blauwdrukken voor andere klassen. Methoden in een abstracte klasse kunnen abstract zijn, wat betekent dat ze geen implementatie bevatten en moeten worden geïmplementeerd in de subklassen.

<?php
abstract class AbstracteKlasse {
    abstract protected function getWaarde();
    
    public function printWaarde() {
        echo $this->getWaarde();
    }
}

class ConcreteKlasse extends AbstracteKlasse {
    protected function getWaarde() {
        return "Concrete waarde";
    }
}

$object = new ConcreteKlasse();
$object->printWaarde(); // Output: Concrete waarde
?>

Interfaces

Interfaces definiëren een contract dat klassen moeten volgen. Klassen die een interface implementeren moeten alle methoden van die interface definiëren.

<?php
interface MijnInterface {
    public function method1();
    public function method2($naam);
}

class MijnKlasse implements MijnInterface {
    public function method1() {
        echo "Methode 1 uitvoeren";
    }
    
    public function method2($naam) {
        echo "Hallo, $naam";
    }
}

$object = new MijnKlasse();
$object->method1(); // Output: Methode 1 uitvoeren
$object->method2("John"); // Output: Hallo, John
?>

Traits

Traits worden gebruikt om methoden te hergebruiken in meerdere klassen. Ze zijn een soort mixins.

<?php
trait GroetTrait {
    public function zegHallo() {
        echo "Hallo!";
    }
}

class MijnKlasse {
    use GroetTrait;
}

$object = new MijnKlasse();
$object->zegHallo(); // Output: Hallo!
?>

Namespaces in classes

Namespaces in PHP helpen je om naamconflicten te voorkomen en je code beter te organiseren. Ze stellen je in staat om dezelfde namen voor klassen, functies en constanten te gebruiken in verschillende delen van je applicatie, zonder dat ze elkaar in de weg zitten. Dit is vooral nuttig in grotere projecten of wanneer je externe bibliotheken gebruikt.

<?php
namespace App\Model;

class Gebruiker {
    public function hallo() {
        return "Hallo, gebruiker!";
    }
}

namespace App\Controller;

use App\Model\Gebruiker;

class GebruikerController {
    public function begroetGebruiker() {
        $gebruiker = new Gebruiker();
        return $gebruiker->hallo();
    }
}

$controller = new \App\Controller\GebruikerController();
echo $controller->begroetGebruiker(); // Output: Hallo, gebruiker!
?>

In dit voorbeeld hebben we twee namespaces: App\Model en App\Controller. Hierdoor kunnen we dezelfde klassenamen in verschillende delen van de applicatie gebruiken zonder conflicten.

Callbacks binnen classes

Callbacks kunnen ook binnen classes worden gebruikt om zo dynamisch gedrag toe te voegen. Zo kun je bijvoorbeeld een objectmethode als callback gebruiken voor functies zoals array_map.

In dit volgende voorbeeld wordt de formatUppercase methode van het Formatter object gebruikt als callback voor array_map.

<?php
class Formatter {
    public function formatUppercase($item) {
        return strtoupper($item);
    }
}

$items = ["apple", "banana", "cherry"];

$formatter = new Formatter();
$uppercaseItems = array_map([$formatter, 'formatUppercase'], $items);

print_r($uppercaseItems);
/*
Output:
Array
(
    [0] => APPLE
    [1] => BANANA
    [2] => CHERRY
)
*/
?>