Mit diesem Beitrag möchte ich euch eine Einführung in die objektorientierte Programmierung geben. Es soll insbesondere um die ersten Strukturen in Form von Klassen und Objekten gehen.
Dieses Tutorial richtet sich an PHP-Neulinge, die ihr erstes „Hallo Welt“-Programm schon geschrieben haben und jetzt in OOP einsteigen wollen.
Begriffsklärung Klasse und Objekt
Unter einer Klasse versteht man in der Programmierung den Bauplan für ein Objekt. Die Klasse definiert welche Daten das Objekt halten kann – in sogenannten Instanzvariablen – und welche Funktionalität es anbietet – in sogenannten Methoden.
Wir schreiben bei der Entwicklung also Klassen (Baupläne) und erzeugen während der Laufzeit Objekte (Instanzen der Klasse).
Aufbau einer Klasse
Zunächst ein Beispiel.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
class EineKlasse { const EINE_KLASSEN_KONSTANTE = 'ein Wert'; public $oeffentlicheVariable; protected $geschuetzeVariable; private $geheimeVariable; public function oeffentlicheMethode() { } protected function geschuetzteMethode() { } private function geheimeMethode() { } } |
Auf die einzelnen Aspekte obigen Codes werden wir im folgenden näher eingehen.
Nomenklatur
Bei der Benennung der Klasse selbst sollte mit einem Großbuchstaben angefangen und in CamelCase fortgefahren werden. Dadurch sind im Programmcode Klassen einfach am ersten Großbuchstaben erkennbar. Die zugehörige PHP-Datei wird exakt so benannt wie die Klasse und lautet somit EineKlasse.php
. Es gilt: Nur eine Klasse pro Datei.
Für die Methoden- und Variablennamen sollte wie sonst auch lowerCamelCase verwendet werden. Also den ersten Buchstaben klein schreiben und danach normal in CamelCase.
Unterstriche im Namen entsprechen nicht mehr den heutigen Best Practices.
Zugriff auf Methoden und Variablen definieren
Die einzelnen Methoden und Variablen der Klasse können für einen von drei Bereichen verfügbar gemacht werden. Diese sind öffentlich (public
), innerhalb der Klassenhierarchie (protected
) und nur in der Klasse selbst (private
).
Der Zugriff wird direkt vor einer Variable angegeben oder bei Methoden vor dem Schlüsselwort function
.
Klassenkonstanten wie EINE_KLASSEN_KONSTANTE
sind immer öffentlich verfügbar.
Instanziierung und Methodenaufruf
Nachdem die Klasse definiert wurde, kann sie im Programm verwendet werden. Dazu erzeugen wir ein neues Objekt mit dem Schlüsselwort new
. Dieses erwartet von uns den Bauplan (Namen der Klasse) für das Objekt.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// PHP muss die Klasse kennen, bevor wir sie verwenden können require_once 'EineKlasse.php'; // Instanz erzeugen $klasse = new EineKlasse(); // Methode aufrufen $klasse->oeffentlicheMethode(); // Variable schreiben $klasse->oeffentlicheVariable = 'Hallo Welt'; // Variable auslesen echo $klasse->oeffentlicheVariable; // Klassenkonstanten können ohne eine Instanz der Klasse abgerufen werden echo EineKlasse::EINE_KLASSEN_KONSTANTE; |
Da wir uns an dieser Stelle im öffentlichen Kontext befinden (also außerhalb der Klasse), können wir nur mit public
gekennzeichnete Methoden und Variablen benutzen.
Versuchen wir auf protected
oder private
zuzugreifen, werden wir einen Fehler erhalten.
1 2 3 4 5 6 7 8 9 10 11 |
echo $klasse->geschuetzeVariable; // PHP Fatal error: Cannot access protected property EineKlasse::$geschuetzeVariable in (...) on line (...) $klasse->geschuetzeMethode(); // PHP Fatal error: Call to undefined method EineKlasse::geschuetzeMethode() in (...) on line (...) echo $klasse->geheimeVariable; // PHP Fatal error: Cannot access private property EineKlasse::$geheimeVariable in (...) on line (...) $klasse->geheimeMethode(); // PHP Fatal error: Call to private method EineKlasse::geheimeMethode() from context '' in (...) on line (...) |
Aufruf innerhalb der Klasse
Innerhalb der Klasse werden Variablen und Methoden durch die spezielle Variable $this
erreicht.
1 2 3 4 5 6 7 8 9 10 11 |
public function oeffentlicheMethode() { // Zugriff auf alle Methoden $this->geschuetzteMethode(); $this->geheimeMethode(); // Zugriff auf alle Variablen $wert = $this->oeffentlicheVariable.' '.$this->geheimeVariable; $this->geschuetzeVariable = 42; } |
Für Dinge, die keine Instanz der Klasse benötigen, wie zum Beispiel die Klassenkonstante verwenden wir self
.
1 2 3 4 5 |
protected function geschuetzteMethode() { // Zugriff auf die Konstante return self::EINE_KLASSEN_KONSTANTE; } |
Getter und Setter
Häufig ist es allerdings erwünscht, dem Anwender Zugang zu einer geschützten Variable zu gewähren und diesen lediglich zu kontrollieren oder nur lesend oder nur schreibend zu erlauben. Dies erreichen wir durch zusätzliche öffentliche Methoden, für die sich die Namen „Getter“ und „Setter“ eingebürgert haben.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class Person { public $name; protected $telefon; /** * @return string */ public function getTelefon() { return $this->telefon; } /** * @param string $telefon */ public function setTelefon($telefon) { $this->telefon = $telefon; } } |
Unsere Person hat also zwei Attribute: Name und Telefonnummer. Die Daten sind alle öffentlich verfügbar, wenn auch auf unterschiedliche Art und Weise.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// Klasse bekannt machen require_once 'Person.php'; // Eine Person definieren $kandidat = new Person(); $kandidat->name = 'Stefan'; $kandidat->setTelefon('012 / 34 5'); // Daten der Person ausgeben echo 'Name: ', $kandidat->name, "\nTelefonnummer: ", $kandidat->getTelefon(), "\n"; /* Ausgabe: Name: Stefan Telefonnummer: 012 / 34 5 */ // vollständiges Objekt print_r($kandidat); /* Ausgabe: Person Object ( [name] => Stefan [telefon:protected] => 012 / 34 5 ) */ |
In diesem einfachen Fall sind die Getter und Setter ziemlich überflüssig, verdeutlichen aber ihre Funktion.
In der Praxis könnten wir hier jetzt dafür sorgen wollen, dass alle Personen einer einheitlichen Struktur für die Telefonnummer folgen. Da der Anwender über unseren Setter gehen muss um den Wert zu ändern, können wir hier sehr einfach eine Formatierung vornehmen.
1 2 3 4 5 6 7 8 |
/** * @param string $telefon */ public function setTelefon($telefon) { // Wir wollen die Nummer als reine Zahlen abspeichern $this->telefon = preg_replace('/\D/', '', $telefon); } |
Egal wie wir jetzt die Telefonnummer eingeben, sie wird in unserem erzwungenen Format gespeichert.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
require_once 'Person.php'; $kandidat1 = new Person(); $kandidat1->name = 'Stefan'; $kandidat1->setTelefon('012 / 34 5'); echo 'Name: ', $kandidat1->name, "\nTelefonnummer: ", $kandidat1->getTelefon(), "\n"; /* Ausgabe: Name: Stefan Telefonnummer: 012345 */ $kandidat2 = new Person(); $kandidat2->name = 'Pö'; $kandidat2->setTelefon('987-234'); echo 'Name: ', $kandidat2->name, "\nTelefonnummer: ", $kandidat2->getTelefon(), "\n"; /* Ausgabe: Name: Pö Telefonnummer: 987234 */ |
Magische Methoden und der Konstruktor
PHP bietet uns innerhalb einer Klasse sogenannte magische Methoden. Magisch deshalb, weil sie normalerweise nicht vom Anwender selbst aufgerufen werden, sondern aufgrund einer bestimmten Bedingungen als „letzte Rettung“ einspringen.
Alle diese Methoden beginnen mit einem doppelten Unterstrich und wir sollten dieses Präfix daher unbedingt bei der Benennung eigener Methoden vermeiden.
Die magischen Methoden sind: __construct()
, __destruct()
, __call()
, __callStatic()
, __get()
, __set()
, __isset()
, __unset()
, __sleep()
, __wakeup()
, __toString()
, __invoke()
, __set_state()
, __clone()
, __debugInfo()
.
Sie lassen sich in Gruppen zusammen fassen, auf die ich im Folgenden kurz eingehe.
Konstruktor
Der Konstruktor übernimmt eine spezielle Aufgabe bei der Instanziierung der Objekte. Klassen können bereits hier Parameter verlangen, die im Konstruktor definiert und bearbeitet werden.
1 2 3 4 5 6 7 8 9 |
/** * @param string $name * @param string $telefon */ public function __construct($name, $telefon) { $this->name = $name; $this->setTelefon($telefon); } |
Die Sichtbarkeit des Konstruktors muss public
sein. Als Argumente definiert er die Parameter für die Instanziierung. Zu beachten ist, dass ein return
im Konstruktor keinen Sinn ergibt – PHP würde dies einfach ignorieren, da die „Rückgabe“ das neue Objekt ist. Die Anwendung sieht dann so aus.
1 2 3 4 5 6 7 8 9 |
require_once 'Person.php'; $kandidat = new Person('Stefan', '012 / 34 5'); echo 'Name: ', $kandidat->name, "\nTelefonnummer: ", $kandidat->getTelefon(), "\n"; /* Ausgabe: Name: Stefan Telefonnummer: 012345 */ |
Achtung: Obwohl der Konstruktor natürlich Zugriff auf die geschützten Variablen der Klasse hat, sollten immer die definierten Setter verwendet werden. Andernfalls würde deren zusätzliche Funktionalität verloren gehen.
Das Gegenteil des Konstruktors ist der Destruktor. Definiert über __destruct()
enthält er Anweisungen, die ausgeführt werden, bevor ein Objekt „zerstört“ (heißt: aus dem Speicher gelöscht) wird. Dies geschieht automatisch für alle Objekte bei Programmende. Wir werden allerdings sehr selten wirklich einen Destruktor benötigen.
Automatische Umwandlung in Text
Eine weitere magische Methode ist __toString()
. Sie wird aufgerufen, wenn das Objekt im Textkontext verwendet wird.
1 2 3 4 5 6 7 |
/** * @return string */ public function __toString() { return 'Name: '.$this->name."\nTelefonnummer: ".$this->getTelefon(); } |
Implementieren wir diese Methode, können wir anschließend im Programm einfach das Objekt selbst in Strings verwenden.
1 2 3 4 5 6 7 8 9 |
require_once 'Person.php'; $kandidat = new Person('Stefan', '012 / 34 5'); echo $kandidat, "\n"; /* Ausgabe: Name: Stefan Telefonnummer: 012345 */ |
Zugriff auf nicht definierte Variablen und Methoden
Eine große Gruppe der magischen Methoden dient dazu den Zugriff auf nicht vorhandene Variablen oder Methoden zu ermöglichen. Diese sind: __call(), __callStatic(), __get(), __set(), __isset(), __unset().
Unsere Klasse könnten wir folgendermaßen definieren.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
class MagischePerson { private $daten = []; /** * Magischer Getter, der einspringt, wenn es eine abgefragte Variable nicht gibt. * * @param string $key Name der Variable. * * @return mixed */ public function __get($key) { return $this->daten[$key]; } /** * Magischer Setter, der einspringt, wenn es eine abgefragte Variable nicht gibt. * * @param string $key Name der Variable. * @param mixed $value Wert, der zugewiesen werden soll. * * @return mixed */ public function __set($key, $value) { $this->daten[$key] = $value; } /** * Magische Methode, die eingreift, wenn eine Methode nicht definiert ist. * * @param string $name Name der Methode. * @param array $arguments Parameter die übergeben wurden. */ public function __call($name, array $arguments) { if (substr($name, 0, 3) == 'get') { return $this->daten[substr(strtolower($name), 3)]; } elseif (substr($name, 0, 3) == 'set') { $this->daten[substr(strtolower($name), 3)] = $arguments[0]; } } } |
Und dann so benutzen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
require_once 'MagischePerson.php'; $mp = new MagischePerson(); // Hier wird __set() aufgerufen $mp->name = 'Stefan'; // Hier wird __call() benutzt $mp->setTelefon('012 / 34 5'); echo 'Name: ', $mp->name, "\nTelefonnummer: ", $mp->getTelefon(), "\n"; /* Ausgabe: Name: Stefan Telefonnummer: 012 / 34 5 */ |
Die Fehlerbehandlung, die nötig wäre um hier einigermaßen anständige Ergebnisse in echten Programmen liefern zu können, würde hier jeden Rahmen sprengen. Obige Vorgehensweise ist absolut fatal und sollte nur in sehr ausgewählten Spezialfällen angewandt werden! Sie beeinträchtigt die Les- und Wartbarkeit des Programmcodes immens.
Sonderfälle
Die hier nicht näher erklärten magischen Methoden beschäftigen sich mit Spezial- und Ausnahmefällen, die ihre eigenen Kapitel verdienen. Daher werde ich es in diesem Beitrag bei einer bloßen Nennung belassen.
Zusammenfassung
In diesem Artikel habt ihr eine wichtige Grundlage der objektorientierten Programmierung – die Klasse – kennen gelernt. Natürlich gibt es noch viele Feinheiten zu entdecken, die ich euch in jeweils eigenen Artikeln erläutern werden.
Das nächste Kapitel im Rahmen objektorientierter Programmierung könnten die Namensräume (namespace) sein. Einen passenden Beitrag dazu findet ihr hier: Namensräume in PHP.
Hier gibt es noch weitere Tutorials.