Objektorientierung in PHP

Objektorientierung mit PHP

PHP kennt viele OO-Prinzipien

  • Klassen und Objekte
    • statische und nicht-statische Attribute und Methoden
    • abstrakte Klassen und Methoden
    • Konstruktoren und Destruktoren
    • Einfachvererbung
    • Überschreiben von Methoden
  • Interfaces
  • Einschränkung der Sichtbarkeit mit public, protected, private
  • Namensräume (ähnlich Java Packages)
  • Ausnahmen/Exceptions

Grenzen der Objektorientierung

  • kein Überladen von Methoden
  • Großteil der Bibliotheken ist immer noch prozedural, insbesondere Arrays
  • In der Praxis immer Mischbetrieb von prozedural und objektorientiert

Definition einer Klasse

  • PHP-Klassen sind immer public
  • Üblich ist eine Quellcode-Datei pro Klasse
  • Syntax: class CLASSNAME { }
  • Ableiten mit dem Schlüsselwort extends
class Mitarbeiter {
    public $name;
    public $geboren;
}

class Manager extends Mitarbeiter {
    public $abteilung;
}

Klasse mit Typangaben

Neuere PHP-Versionen erlauben es, die Typen anzugeben von

  • Attributen
  • Parametern
  • Rückgabewerten
class Mitarbeiter {
    public string $name;
    public DateTime $geboren;
}

class Manager extends Mitarbeiter {
    public string $abteilung;
}

Instanz- und Klassenvariablen

  • Instanzvariablen haben den Modifier public, protected, private
  • Klassenvariablen haben den Modifier static
  • Variablen können bei der Definition initialisiert werden
class Tier {
    protected string $name;  // nicht initialisiert
    public int $alter = 1;   // Variable vorbelegt

    // statische Variable
    public static int $instanzen = 0;
}

Zugriff auf Instanzvariablen

  • Instanzvariablen anderer Objekte
    $referenz->attributname
  • Klassenvariable der eigenen Klasse
    self::$variablenname
  • Klassenvariablen fremder Klassen
    Klassenname::$variablenname
function setName(string $name) {
    $this->name = $name;
}

$t = new Tier();
$t->alter = 14;

echo Tier::$instanzen;

Zugriffsbeschränkung

Attribute und Methoden können einen Modifier haben

  • public: jeder darf zugreifen
  • protected: eigene und abgeleitete Klassen darf zugreifen
  • private: nur die eigene Klasse darf zugreifen

Klassenkonstanten

Klassenkonstanten

  • innerhalb einer Klasse mit Schlüsselwort const definiert
  • grundsätzlich public
  • kein $ im Namen
  • kein Datentyp, wird automatisch abgeleitet
  • Zugriff mit Klassenname::Konstantenname
class Math {
    const PI = 3.14159265;

    public static function kreisflaeche(float $radius) {
        return Math::PI*$radius*$radius;
    }
}

(Nichtstatische) Methoden

Innerhalb einer Klasse definierte Funktionen

  • mit Schlüsselwort function gekennzeichnet
  • nur über ein Objekt der Klasse aufrufbar
  • kein Überladen, lediglich Überschreiben möglich
  • optionale Datentypen für Parameter und Rückgabewert
  • überflüssige Parameter werden kommentarlos ignoriert

Statische Methoden

Statische Methoden

  • gekennzeichnet durch das Schlüsselwort static
  • dürfen $this-Referenz nicht verwenden
  • dürfen nur statische Attribute der Klasse nutzen
  • dürfen nur statische Methoden aufrufen

Konstruktor

Konstruktor heißt in PHP __construct()

  • kann beliebige Parameter (auch mit Default) haben
  • kann nicht überladen werden (nur ein Konstruktor pro Klasse)
  • wird (im Gegensatz zu Java) vererbt, es sei denn, es gibt in der Kindklasse einen Konstruktor
  • wird implizit bei new aufgerufen
  • kann auch explizit aufgerufen werden
  • Konstruktor der Elternklasse wird nicht automatisch gerufen, kann aber explizit mit parent::__construct() gerufen werden

Beispiel: Konstruktor

class Mitarbeiter {
    public string $name;
    public DateTime $geboren;

    function __construct(string $name = "",
          DateTime $geboren = new DateTime("1.1.1970")) {
        $this->name = $name;
        $this->geboren = $geboren;
    }
}

$m = new Mitarbeiter("Peter");
echo $m->name;                     // -> Peter
echo $m->geboren->format('d.m.Y'); // -> 1.1.1970

Kopieren von Objekten

Objekte können mit clone kopiert werden

  • ruft implizit die __clone()-Methode auf
  • __clone()-Methode kann überschrieben werden
$a = new Mitarbeiter("Petra Mustermann");
$b = $a;
$a->name ="Petra Suhrbier";

echo $a->name; // -> Petra Suhrbier
echo $b->name; // -> Petra Suhrbier

$c = new Mitarbeiter("Franz Meier");
$d = clone $c;
$c->name ="Franz Suhrbier";

echo $c->name; // -> Franz Suhrbier
echo $d->name; // -> Franz Meier

toString()-Methode

Methode __toString()

  • wird bei Ausgabe eines Objektes mit echo aufgerufen
  • bestimmt, welche Attribute einer Klasse ausgegeben werden
  • wenn Methode nicht definiert ist, gibt es eine Fehlermeldung
class Mitarbeiter {
    public string $name;
    public DateTime $geboren;
    function __construct(string $name = "", DateTime $geboren) { ... }
    function __toString() {
        return "Name: $this->name, geboren: " . $this->geboren->format('d.m.Y');
    }
}

$ma = new Mitarbeiter("Markus Peter", new DateTime("13.7.1984"));
echo $ma; // -> Name: Markus Peter, geboren: 13.7.1984
class Mitarbeiter {
    public string $name;
    public DateTime $geboren;

    function __construct(string $name = "", DateTime $geboren = new DateTime("1.1.1970")) {
        $this->name = $name;
	$this->geboren = $geboren;
    }

    function __toString() {
        return "Name: $this->name, geboren: " . $this->geboren->format('d.m.Y');
    }
}

$ma = new Mitarbeiter("Markus Peter", new DateTime("13.7.1984"));
echo $ma; // -> Name: Markus Peter, geboren: 13.7.1984

Destruktor

Klassen können einen Destruktor angeben

  • __destruct()-Methode enthält den Code des Destruktors
  • parameterlos
  • wird aufgerufen, wenn letzte Referenz auf das Objekt wegfällt
  • nach dem Destruktor-Aufruf wird das Objekt gelöscht
  • PHP hat keinen Garbage-Collector, sondern betreibt Reference-Counting
  • Destruktor der Elternklasse wird nicht automatisch gerufen, kann aber explizit mit parent::__destruct() gerufen werden

Beispiel: Destruktor

class KillMe {
    function __destruct() {
        echo "Wurde vernichtet";
    }
}

$k = new KillMe();
$l = $k;

echo "Noch lebt es";
$k = null;
echo "Noch lebt es";
$l = null;
Noch lebt es
Noch lebt es
Wurde vernichtet

Überschreiben von Methoden

  • Gleichnamige Methode in abgeleiteter Klasse überschreibt (d. h. ersetzt) Methode der Basisklasse
  • Überschriebene Methode der Elternklasse kann mit parent::methode() aufgerufen werden
  • Polymorphie braucht man in PHP nicht, da Referenzvariablen keinen Typ haben
  • Methoden werden immer virtuell aufgerufen

Beispiel: Überschreiben

class A {
    function methode() {
        echo "A";
    }
}

class B extends A {
    function methode() {
        parent::methode();
        echo "B";
    }
}

$a = new A(); $a->methode(); // -> A
$b = new B(); $b->methode(); // -> AB

Abstrakte Methoden und Klassen

Abstrakte Methoden

  • haben keine Rumpf
  • legen nur die Signatur fest
  • werden mit abstract gekennzeichnet
  • müssen von der abgeleiteten Klasse implementiert werden

Abstrakte Klassen

  • haben mindestens eine abstrakte Methode
  • können nicht instanziert werden
  • werden mit abstract gekennzeichnet

Beispiel: Abstrakte Methoden

abstract class A {
    public function konkret() {
        echo "Ich bin konkret";
    }

    public abstract function abstrakt();
}

class B extends A {
    public function abstrakt() {
        echo "Jetzt bin ich auch konkret";
    }
}

$b = new B();
$b->abstrakt(); // -> Jetzt bin ich auch konkret

$a = new A(); // Fehler!

instanceof-Operator

Mit instanceof kann man prüfen ob ein Objekt

  • von der angegebenen Klassen oder
  • von einer Subklasse dieser Klasse ist
class A {}
class B extends A {}

$a = new A();
$b = new B();

echo $a instanceof A; // -> true (1)
echo $a instanceof B; // -> false ()
echo $b instanceof A; // -> true (1)
echo $b instanceof B; // -> true (1)

Interfaces

Interfaces

  • werden mit dem Schlüsselwort interface eingeleitet
  • definieren Methoden-Signaturen ohne Implementierung
  • können nicht instanziert werden

Methoden in Interfaces

  • sind immer implizit public
  • dürfen nicht protected oder private sein
  • dürfen keine Implementierungen enthalten

Implementierung von Interfaces

Eine PHP-Klasse

  • darf beliebig viele Interfaces implementieren (implements)
  • darf von genau einer anderen Klasse erben
  • implementiert alle Methoden des Interfaces und wird konkret
  • implementiert nicht alle Methoden und wird dadurch abstrakt

Beispiel: Interfaces

interface Flieger {
    function fliegen();
}

class Ente implements Flieger {
    function fliegen() { /* fliegen */ }
}

abstract class Superheld implements Flieger {
    function weltRetten() { }
}

class Superman extends Superheld {
    function fliegen() { /* fliegen */ }
}

$s = new Superman();
echo $s instanceof Flieger;   // -> true (1)
echo $s instanceof Superheld; // -> true (1)

Namensräume

Namensräume (namespaces)

  • stellen einen Prefix vor
    • Klassen- und Interfacenamen
    • Globale Funktions- und Konstantennamen
  • verhindern Namenskollisionen (name clashes)
  • sind ähnlich zu Java-Paketen (packages)
  • sind hierarchisch aufgebaut mit Backslash (\) als Trennzeichen

Namensraum definieren

  • Namensraum wird mit dem Schlüsselwort namespace definiert
  • Trennung von Ebenen erfolgt duch \
  • Namespace-Statement muss als erstes in der Datei stehen
    • keine anderen Statements vor der Deklaration
    • kein HTML vor der Deklaration
<?php
namespace de\smits\web;

class A {
    function f() {
        echo "Hallo";
    }
}
?>

Mehrere Namensräume in einer Datei

Es können auch mehrere Namensräume in einer Datei deklariert werden

namespace { // globaler Namespace
    const PI = 3.14159265;
}

namespace de\smits\web {

    class A {
        function f() {
            echo PI;
        }
    }

    $a = new A();
    $a->f();
}

Namensraum nutzen

Drei Möglichkeiten Namespaces zu nutzen

  • Verwendung des Prefix vor allen Namen
  • Importieren eines ganzen Namensraums mit use
    • Klassen und Interfaces können dann ohne Prefix genutzt werden
    • Funktionen behalten letzten Teil des Namensraums als Prefix
  • Importieren einzelner Klassen oder Interfaces mit use
    • Klassen und Interfaces können ohne Prefix genutzt werden
    • Es kann beim Import ein Alias vergeben werden

Beispiel: Namensraum

namespace de\smits\web {
    class A { }
    function f() { return "web"; }
}

namespace de\smits\pr3 {
    class A { }
    function f() { return "pr3"; }
}

namespace de\smits {
    // Zugriff über Unter-Namespace
    echo pr3\f(); // -> pr3
    echo web\f(); // -> web

    // Zugriff über den vollen Namen
    echo \de\smits\pr3\f(); // -> pr3
    echo \de\smits\web\f(); // -> web
}
namespace {
    // Zugriff über den vollen Namen
    echo de\smits\pr3\f(); // -> pr3
    echo de\smits\web\f(); // -> web

    use de\smits\pr3;  // namespace importieren
    use de\smits\web;  // namespace importieren

    echo pr3\f(); // -> pr3
    echo web\f(); // -> web
    $a = new pr3\A();

    use de\smits\web\A; // Klasse importieren
    $a = new A();

    use de\smits\web\A as X; // Klasse mit Alias importieren
    $a = new X();
}

Copyright © 2025 Thomas Smits