12. Objektorientiertes PHP

Du weißt nun bereits eine ganze Menge über PHP, etwa über Cookies, wie es im letzten Teil behandelt wurde.

Heute beschäftigen wir uns mit etwas ganz anderem, dem objektorientierten PHP, kurz OOP. Dabei geht es um neue Methoden, etwas um Programmierungsphilosophie und die Struktur und Organisation deines PHP-Codes.

Prozedurales Programmieren

Zunächst einmal sollten wir uns die Frage stellen, was Du denn bisher getan hast. Es war jedenfalls kein objektorientierter Code, den Du geschrieben hast.

Zwar kann man bei PHP eigentlich nicht von Programmierung sprechen, weil es sich um eine Skriptsprache handelt, ich tue es der Einfachheit halber trotzdem in diesem Artikel.

Bisher hast du eher prozedural gearbeitet, es gibt also einzelne Codeabschnitte, die nacheinander, wie Prozesse abgearbeitet werden. Zwischendrin gibt es mal kleinere Funktionen, die Du selbst definiert hast.

Doch gibt es hier einige Nachteile. Insbesondere sich wiederholende Code-Abschnitte können zu einem Problem werden. Möchtest Du dort etwas ändern, so musst Du das gleich an all den Stellen ändern, wo der bisherige Code eingesetzt wird.

Natürlich kannst Du genau aus dem Grunde auch einfache Funktionen einsetzen, um einen sich wiederholenden Prozess mit dem gleichen Code mehrmals auszuführen. Doch was ist, wenn die Funktionen untereinander interagieren können sollen? In diesem Fall müsstest Du gleich mehrere Parameter an jede Funktion übergeben. Das wird schnell unübersichtlich.

Objektorientiertes Programmieren

Die Lösung ist das objektorientierte Arbeiten mit PHP (OOP). Jedes Objekt basiert auf einer sogenannten Klasse. Diese Klasse enthält mehrere Funktionen und einen eigenen Namensraum für Variablen.

Der Name einer Klasse darf nur einmal im gesamten Code vorkommen, es können aber mehrere Objekte dieses Klassen-Typs erstellt werden.

Zur Veranschaulichung nehmen wir einmal ein kurzes Beispiel. Du hast eine eigene Session-Klasse erstellt:

<?php

  class session {
    private $sessioncode;
    
    public function __construct($code) {
      $this -> sessioncode = $code;
    }

    public function getCode() {
      return $this -> sessioncode;
    }
  }
?>

Diese enthält die lokale Variable $sessioncode. Nun kannst Du beliebig viele Objekte erstellen, die alle die gleichen Funktionen der Klasse nutzen können, aber über unterschiedliche Eigenschaften (lokale Variablen) verfügen können:

<?php
  $session1 = new session('ABC');
  echo $session1 -> getCode(); // Ergebnis: ABC

  $anderername = new session('BCD');
  echo $anderername -> getCode(); // Ergebnis: BCD
?>

Vielleicht ahnst Du jetzt bereits den Vorteil von OOP. Du kannst beliebig viele Objekte einer Klasse erstellen, die alle auf die gleichen Funktionen zugreifen können. Das können Sessions, Datenbank-Sitzungen, HTML-Elemente oder etwas anderes sein. Doch, die Möglichkeiten wirst Du im Laufe dieses Tutorials sicher noch genauer kennenlernen.

Externer Zugriff und Initialisierung

Oben hast Du bereits gesehen, wie so eine (Mini-)Klasse aussehen kann. Wir werden uns nachher noch anschauen, wie Du innerhalb eine Klasse arbeiten musst. Doch zunächst beschäftigen wir uns mit dem Zugriff von außen.

Um ein neues Objekt erstellen zu können, muss man es zuerst initialisieren. Dabei spielt der Konstruktor eine wichtige Rolle. Generell gilt jedoch, dass der Aufruf von new klassenname ein Klassen-Objekt zurückgibt. In unser Beispiel-Klasse oben haben wir aber noch zusätzlich Parameter übergeben.

Das geschieht mit Hilfe des Konstruktors, eine Funktion innerhalb der Klasse mit dem Namen __construct(). Dieser kann, wie eine normale Funktion, beliebig viele Parameter erfordern, die dann bei Initialisierung übergeben werden müssen.

Hilfreich ist das dann, wenn die Klasse unbedingt gewisse Eigenschaften benötigt, um arbeiten zu können, etwa eine Verbindung zu einer MySQL-Datenbank (siehe Teil 13 des Tutorials).

Sobald wir das Objekt initialisiert und einer Variablen zugewiesen haben, zum Beispiel $session1, können wir über diese Variable auf alle öffentlichen Variablen und Funktionen der Klasse zugreifen:

echo $session1 -> varName; // gibt die Eigenschaft varName des Objekts $session1 aus
echo $session1 -> functionName(parameter1, parameter2); // Ruft die Funktion functionName mit den Parametern 1 und 2 des Objekts $session1 auf

Wohlgemerkt: Auf alle öffentlichen (public) Funktionen und Variablen. Da wir die lokale Variable $sessioncode mit dem Schlagwort private versehen haben, können wir auf diese nicht zugreifen.

Dafür haben wir die öffentliche Funktion getCode() bereitgestellt, die den Zugriff auf die lokale Variable erlaubt. An dieser Stelle bereits einmal der Hinweis: Es gehört zum guten Stil über öffentliche Funktionen auf private Variablen zuzugreifen, und nicht alle Variablen auf public zu setzen.

Insbesondere bei komplexen Klassen wirst Du mir später für diesen Tipp dankbar sein 😉

Mit private & public beschäftigen wir uns in dem Abschnitt danach. Vorher noch ein Wort zum sogenannten Destruktor, der ebenfalls sehr hilfreich sein kann.

Nehmen wir an, du speicherst während der Skript-Laufzeit die Objekt-Eigenschaften ganz normal in einem Objekt. Am Anfang lädst Du sie aus einer Datenbank und am Ende sollen sie wieder dorthin übertragen werden. Du könntest nun jede Änderung in die Datenbank übertragen, oder:

Du lädst sie mit dem Konstruktor aus der Datenbank, speicherst Änderungen in den lokalen Variablen und mit dem Destruktor werden die Änderungen am Ende einmal in die Datenbank übernommen.

Der Destruktor wird dann ausgeführt, wenn das Objekt mit unset() gelöscht wird oder das Skript beendet ist. Die Funktion sieht so aus (natürlich innerhalb einer Klasse und zwar als public):

public function __destruct() {
  // Code der beim Beenden des Skripts/Löschen des Objekts ausgeführt werden soll
}

Parameter sind bei dieser Funktion nicht möglich. Wie sollten die auch übergeben werden?

Private, Public & Protected

Du hast oben schon von den Schlagwörtern private und public gelesen. Diese sind für OOP zentral, denn so können die Zuständigkeiten klar geregelt werden.

Zunächst einmal folgt die Anwort auf die Frage, wann Du diese einsetzen kannst:

  • zur Deklarierung von Variablen (zu Beginn der Klasse), Beispiele:
    class abc {
      private $sessioncode = 'ABC', $var2, $var3 = array();
      protected $var4, $var5 = 2, $var6 = '';
      public $var7, $var8, $var 9;  
    
      // Funktionen ...
    }

    Wichtig ist, dass die Variablen hier in der ganz normalen Form $variable geschrieben werden. Mit dem gleich-Zeichen kann bereits ein Standard-Wert festgelegt werden. Durch Kommata werden beliebig viele Variablen getrennt, die einem Schlagwort zugeordnet werden sollen.

  • vor function … innerhalb einer Klasse, Beispiel:
    public function funktion1($parameter1, $paramter2) {
    
    }
    
    private function funktion2($parameter1, $paramter2) {
    
    }
    
    protected function funktion3($parameter1, $paramter2) {
    
    }

Nun stellt sich natürlich noch die Frage, was diese 3 Wörter für Auswirkungen haben. Da die Klasse einen eigenen Namensraum besitzt, können Variablen, die außerhalb bereits gesetzt wurden, hier nochmals einen anderen Wert beinhalten. Man kann von außerhalb nicht einfach so auf sie zugreifen. Um das zu regeln, sind diese Schlagwörter wichtig.

Setzt Du eine lokale Variable, ohne, dass ihr vorher ein Schlagwort zugeteilt wurde, oder lässt Du es bei einer Funktion, weg, wird automatisch public als Wert genommen.

public bedeutet, dass die Funktion oder die Variable von außerhalb ($objekt -> …), innerhalb der Klasse und von Elternelementen (siehe nächster Abschnitt aufgerufen werden kann.

protected bedeutet, dass die Funktion oder Variable nicht von außerhalb, aber innerhalb Klasse und innerhalb von Elternelementen aufgerufen werden kann.

private bedeutet, dass nur innerhalb dieser Klasse auf die Variable oder die Funktion zugegriffen werden kann.

Eltern und Kinder

Vielleicht mag Dir das in der Programmierung etwas unpassend erscheinen, aber auch hier gibt es „Eltern“ und „Kinder“, zumindest entsprechende Beziehungen.

Mit extends Elternname wird der Name des Eltern-Elements angegeben. Fortan teilen sich beide einen Namensraum. Wird ein Kindeselement erstellt, so entsteht automatisch auch ein Element der Eltern-Klasse. Das Kind kann auf die public oder protected (daher das Schlagwort protected!) definierten Funktionen und Variablen der Eltern-Klasse zugreifen.

Bei protected hat sogar nur das Kindeselement Zugriff, ein Zugriff von außen ist dann nicht möglich.

Mit der Initialisierung des Kindeselements wird nur der Konstruktor des Kindeselements ausgeführt, der Konstruktor des Eltern-Elements muss also explizit aufgerufen werden, etwa so:

<?php

// Eltern-Element
class BaseClass {
   protected function __construct() {
       print "Im BaseClass Konstruktor\n";
   }
}

// Kindeslement
class SubClass extends BaseClass {
   public function __construct() {
       parent::__construct();
       print "Im SubClass Konstruktor\n";
   }
}

$obj = new SubClass();

?>

Mit parent::

parent::funktion($parameter1, $parameter2); // Eltern-Funktion aufrufen

// '::' und ' :: ' wird gleich behandelt //

parent :: varName; // Eltern-Variable abfragen

können wir also auf die Eigenschaften und Funktionen der Eltern-Klasse zugreifen, sofern es die Rechte (protected oder public) erlauben.

Interaktion innerhalb einer Klasse mit $this

Innerhalb  einer Klasse und deren Funktionen kannst Du mit $this -> auf eine lokale Funktion oder eine lokale Variable zugreifen. Beispiel gefällig?

<?php
class test {
  private $var = 'ABC', $lastError;
  
  public function getVar($varName) {
    if (isset($this -> $varName)) {
      return $this -> $varName;
    } else {
      return $this -> fehler($varName);
    }
  }

  private function fehler($varName) {
    $this -> lastError = $varName." konnte nicht gefunden werden.";
    return 'Es ist ein Fehler aufgetreten';
  }
}

$obj = new test;

echo $obj -> getVar('var'); // gibt ABC zurück
echo $obj -> getVar('var2'); // Gibt 'Es ist ein Fehler aufgetreten' aus
echo $obj -> getVar('lastError'); // Gibt 'var2 konnte nicht gefunden werden." aus

?>

Das $this wird nur dann benötigt, wenn es sich um Klassen-Variablen (oder -Funktionen) handelt. Übergeben sich die Klassen-Funktionen also gegenseitig Variablen (häufig sollte man dies eher über eine lokale Klassen-Variable machen, auf die ja beide Funktionen zugreifen können), ist kein $this notwendig, weil es sich um eine funktionsbeschränkte Variable handelt.

Noch kurz ein Satz zu den Funktionen: Diese können all das, was „normale“ Funktionen auch können, soll heißen return, Parameter und der Zugriff auf andere Klassen-Funktionen sind möglich. Theoretisch kannst Du innerhalb einer Klassen-Funktion sogar ein neues Objekt initialisieren und mit diesem arbeiten.

Konstanten in der OOP

Wir haben uns bereits mit globalen Konstanten beschäftigt. Diese sind auch in der OOP hilfreich, um etwa von überall aus auf verschiedene Einstellungen zugreifen zu können.

Eventuell benötigst Du ja auch eine Konstante, die klassenweit funktioniert, das geht so:

<?php
class test {
  const konstante = 'ABC';

  public function getConst() {
    return self :: konstante; // das gleiche wie 'self::konstante'
  }
}
?>

Mit const definieren wir also eine Klassen-Konstante, allerdings außerhalb der Funktionen und dann können wir innerhalb der Klasse mit self :: konstantenname auf die Konstante zugreifen. Der Wert kann natürlich nicht verändert werden.

Statische Methoden und Variablen (static)

Die wichtigsten Grundlagen der OOP kennst Du nun bereits. Ein Schlüsselwort kennst Du allerdings noch nicht. Dieses solltest Du allerdings behutsam einsetzen, denn echtes OOP geht anders (siehe Abschnitte zuvor).

Wann immer du das Schlagwort public (für static muss die Variable oder Funktion öffentlich sein) einsetzt, kannst du ein static einsetzen. Eine statische Variable wird über Klassen-Name :: $Variable von außen abgefragt.

Achtung: Im Gegensatz zu der eigentlichen OOP, initialisierst Du hier kein Objekt, sondern greifst direkt auf die Variable oder eine Funktion ( Klassen-Name :: funktion($parameter1, $parameter2) ) zu.

Mit statischen Funktionen kannst Du also eher mehrere Funktionen in einer Klasse sammeln, als dass diese wirklich interagieren würden. Da der Zugriff ohne Objekt funktioniert, ist es auch nicht mehr wirklich objektorientiert.

Du kannst natürlich auch nur einzelne Funktionen oder Variablen statisch zugängig machen, alle anderen funktionieren dann wie in der OOP üblich über den Zugriff mit einem Objekt.

Beispiel für den Einsatz von static:

<?php
class test {
  public static $test = 100;
  public $var = 60;

  public function normal() {
    echo $this -> var;
  }

  public static function statisch() {
    echo 'Statische Funktion wurde aufgerufen!';
  }
}

test :: statisch(); // Ausgabe 'Statische Funktion wurde aufgerufen!'
echo test :: $test; // Ausgabe 100

$obj = new test;
$obj -> normal(); // Ausgabe 60
?>

Mehr über static kannst Du im PHP Manual nachlesen.

Fazit zur OOP

Gerade bei komplexeren Projekten kann es sich lohnen auf OOP, objektorientiertes PHP, zu setzen. Derartiger Code ist häufig übersichtlicher und leichter zu bearbeiten.

Auch die Klassen untereinander können gut interagieren und mit Schlüsselworten können klare Zuständigkeiten vergeben werden.

Wer PHP kann, sollte sich unbedingt mal an objektorientiertem Code versuchen.

In dem nächsten und letzten Teil dieses PHP-Tutorials geht es dann um Datenbanken, genauer gesagt um den Zugriff auf MySQL-Datenbanken mit PHP.

Ein Gedanke zu „12. Objektorientiertes PHP“