Modul 295


Backend für Applikationen realisieren

Web Icon

06 - Klassen


Objektorientierte Grundlagen in PHP

PHP Logo

Lernziele dieser Lektion

  • Die Begriffe Klasse, Objekt und Instanz erklären können
  • Eigene Klassen mit Eigenschaften und Methoden in PHP definieren können
  • Objekte erzeugen und mit $this, Konstruktoren und Sichtbarkeiten arbeiten können
  • Statische Eigenschaften, Methoden und Klassenkonstanten einsetzen können
  • Vererbung, abstrakte Klassen und Interfaces anwenden können
  • readonly-Eigenschaften und den instanceof-Operator verwenden können

Rückblick: Funktionen

  • Mit Funktionen haben wir Code gebündelt und wiederverwendbar gemacht
  • Bisher waren Daten (Variablen, Arrays) und Code (Funktionen) getrennte Dinge
  • Heute verbinden wir beides: Klassen bündeln Daten und die dazugehörigen Funktionen gemeinsam

Wozu Klassen?

  • Ein Array kann Daten zu einem Produkt speichern - aber die Logik dazu (z.B. Preis berechnen) liegt woanders im Code
  • Eine Klasse beschreibt einen eigenen Datentyp, der Eigenschaften (Daten) und Methoden (Verhalten) an einem Ort zusammenfasst
  • Das Konzept kennen Sie bereits aus anderen Sprachen - in PHP gilt dasselbe Prinzip
                    
                        <?php
                        // Bisher: Daten und Logik getrennt
                        $product = ["name" => "Apfel", "price" => 0.50];

                        function printInfo($product) {
                            echo $product["name"] . ": " . $product["price"] . " CHF";
                        }
                    
                

Klasse, Objekt und Instanz

  • Eine Klasse ist ein Bauplan - sie legt fest, welche Eigenschaften und Methoden ein Ding dieses Typs hat
  • Ein Objekt ist ein konkretes Ding, das nach diesem Bauplan erzeugt wurde - man sagt auch, es ist eine Instanz der Klasse
  • Aus einer Klasse lassen sich beliebig viele, unabhängige Objekte erzeugen

Eine Klasse definieren

  • Mit dem Schlüsselwort class, gefolgt vom Namen (meist PascalCase) und einem Codeblock in geschweiften Klammern
  • Eigenschaften (properties) sind Variablen innerhalb der Klasse - sie speichern die Daten eines Objekts
                    
                        <?php
                        class Product {
                            public string $name;
                            public float $price;
                        }
                        // Bis hierhin passiert noch nichts -
                        // die Klasse wurde nur definiert
                    
                

Ein Objekt erzeugen

  • Mit dem Schlüsselwort new gefolgt vom Klassennamen wird ein neues Objekt (eine Instanz) erzeugt
  • Auf Eigenschaften greift man mit dem Pfeil-Operator -> zu
                    
                        <?php
                        class Product {
                            public string $name;
                            public float $price;
                        }

                        $apple = new Product();
                        $apple->name = "Apfel";
                        $apple->price = 0.50;

                        echo $apple->name; // "Apfel"
                    
                

Mehrere, unabhängige Objekte

  • Jedes Objekt hat seine eigenen Werte für die Eigenschaften - eine Änderung an einem Objekt wirkt sich nicht auf ein anderes aus
                    
                        <?php
                        $apple = new Product();
                        $apple->name = "Apfel";
                        $apple->price = 0.50;

                        $bread = new Product();
                        $bread->name = "Brot";
                        $bread->price = 3.20;

                        echo $apple->name; // "Apfel"
                        echo $bread->name; // "Brot"
                    
                

Methoden

  • Eine Methode ist eine Funktion innerhalb einer Klasse - sie beschreibt das Verhalten eines Objekts
  • Aufgerufen wird sie ebenfalls mit dem Pfeil-Operator ->
                    
                        <?php
                        class Product {
                            public string $name;
                            public float $price;

                            public function printInfo() {
                                echo $this->name . ": " . $this->price . " CHF";
                            }
                        }

                        $apple = new Product();
                        $apple->name = "Apfel";
                        $apple->price = 0.50;
                        $apple->printInfo(); // "Apfel: 0.5 CHF"
                    
                

$this - Verweis auf das aktuelle Objekt

  • Innerhalb einer Methode zeigt $this immer auf das Objekt, über welches die Methode gerade aufgerufen wurde
  • Damit greift eine Methode auf die Eigenschaften ihres eigenen Objekts zu
                    
                        <?php
                        $apple->printInfo(); // $this zeigt hier auf $apple
                        $bread->printInfo(); // $this zeigt hier auf $bread
                    
                

Der Konstruktor __construct()

  • Die Methode __construct() wird automatisch aufgerufen, sobald ein Objekt mit new erzeugt wird
  • Sie dient dazu, die Eigenschaften direkt bei der Erzeugung zu setzen - statt sie danach einzeln zuzuweisen
                    
                        <?php
                        class Product {
                            public string $name;
                            public float $price;

                            public function __construct($name, $price) {
                                $this->name = $name;
                                $this->price = $price;
                            }
                        }

                        $apple = new Product("Apfel", 0.50);
                        echo $apple->name; // "Apfel"
                    
                

Constructor Property Promotion

  • Seit PHP 8 können Eigenschaften direkt in der Parameterliste des Konstruktors deklariert werden - Deklaration und Zuweisung passieren dann in einem Schritt
  • Spart Schreibarbeit, das Verhalten ist identisch zur vorherigen Folie
                    
                        <?php
                        class Product {
                            public function __construct(
                                public string $name,
                                public float $price
                            ) {}
                        }

                        $apple = new Product("Apfel", 0.50);
                        echo $apple->name; // "Apfel"
                    
                

Sichtbarkeit: public und private

  • public - von überall zugreifbar, auch von aussen über ->
  • private - nur innerhalb derselben Klasse zugreifbar, schützt Daten vor unerlaubtem Zugriff von aussen
                    
                        <?php
                        class Product {
                            public function __construct(
                                public string $name,
                                private float $price
                            ) {}
                        }

                        $apple = new Product("Apfel", 0.50);
                        echo $apple->name;  // "Apfel" - erlaubt
                        echo $apple->price; // Fehler: $price ist private
                    
                

Sichtbarkeit: protected

  • protected - zugreifbar innerhalb der eigenen Klasse und in davon abgeleiteten Klassen (Vererbung, siehe später)
  • Von aussen (über ->) ist der Zugriff, wie bei private, nicht erlaubt

Getter und Setter

  • Da private-Eigenschaften von aussen nicht direkt zugreifbar sind, definiert man öffentliche Methoden dafür - Getter zum Lesen, Setter zum Schreiben
  • Ein Setter kann zusätzlich Werte prüfen, bevor er sie übernimmt
                    
                        <?php
                        class Product {
                            public function __construct(
                                public string $name,
                                private float $price
                            ) {}

                            public function getPrice(): float {
                                return $this->price;
                            }

                            public function setPrice(float $price): void {
                                if ($price >= 0) {
                                    $this->price = $price;
                                }
                            }
                        }

                        $apple = new Product("Apfel", 0.50);
                        echo $apple->getPrice(); // 0.5
                        $apple->setPrice(0.80);
                        echo $apple->getPrice(); // 0.8
                    
                

Statische Eigenschaften

  • Eine static-Eigenschaft gehört der Klasse selbst, nicht einem einzelnen Objekt - alle Objekte teilen sich denselben Wert
  • Zugriff innerhalb der Klasse mit self::$name
                    
                        <?php
                        class Product {
                            public static int $count = 0;

                            public function __construct(public string $name) {
                                self::$count++;
                            }
                        }

                        $apple = new Product("Apfel");
                        $bread = new Product("Brot");
                        echo Product::$count; // 2
                    
                

Statische Methoden

  • Eine static-Methode wird über den Klassennamen aufgerufen, nicht über ein Objekt - Klasse::methode()
  • Innerhalb einer statischen Methode gibt es kein $this, da kein konkretes Objekt vorliegt
                    
                        <?php
                        class Product {
                            private static int $count = 0;

                            public function __construct(public string $name) {
                                self::$count++;
                            }

                            public static function getCount(): int {
                                return self::$count;
                            }
                        }

                        new Product("Apfel");
                        new Product("Brot");
                        echo Product::getCount(); // 2
                    
                

Klassenkonstanten

  • Mit const definiert man einen Wert, der sich nie ändert - z.B. einen Steuersatz
  • Zugriff innerhalb der Klasse mit self::NAME, von aussen mit Klasse::NAME
                    
                        <?php
                        class Product {
                            public const TAX_RATE = 0.077;

                            public function __construct(
                                public string $name,
                                public float $price
                            ) {}

                            public function priceWithTax(): float {
                                return $this->price * (1 + self::TAX_RATE);
                            }
                        }

                        $apple = new Product("Apfel", 0.50);
                        echo $apple->priceWithTax(); // 0.5385
                        echo Product::TAX_RATE;      // 0.077
                    
                

Vererbung mit extends

  • Eine Klasse kann alle Eigenschaften und Methoden einer anderen Klasse erben - die neue Klasse muss diese nicht selbst nochmals definieren
  • Die ursprüngliche Klasse heisst Basisklasse (Eltern- klasse), die neue abgeleitete Klasse (Kindklasse)
                    
                        <?php
                        class SaleProduct extends Product {
                            // erbt $name, $price, priceWithTax(), ...
                            public function __construct(
                                string $name,
                                float $price,
                                public float $discount
                            ) {
                                parent::__construct($name, $price);
                            }
                        }

                        $sale = new SaleProduct("Apfel", 0.50, 0.2);
                        echo $sale->priceWithTax(); // geerbt von Product
                    
                

parent::

  • Mit parent:: ruft man gezielt eine Methode der Basisklasse auf - z.B. deren Konstruktor, wie auf der vorherigen Folie
  • So lässt sich Code der Basisklasse wiederverwenden, statt ihn zu duplizieren

Methoden überschreiben (Overriding)

  • Eine abgeleitete Klasse kann eine geerbte Methode mit demselben Namen neu definieren - die eigene Version gilt dann
  • Mit parent::methode() lässt sich innerhalb der neuen Version trotzdem die ursprüngliche Methode mit aufrufen
                    
                        <?php
                        class SaleProduct extends Product {
                            public function __construct(
                                string $name, float $price, public float $discount
                            ) {
                                parent::__construct($name, $price);
                            }

                            public function priceWithTax(): float {
                                $reducedPrice = $this->price * (1 - $this->discount);
                                return $reducedPrice * (1 + self::TAX_RATE);
                            }
                        }

                        $sale = new SaleProduct("Apfel", 0.50, 0.2);
                        echo $sale->priceWithTax(); // eigene, überschriebene Version
                    
                

Abstrakte Klassen

  • Eine abstract-Klasse kann nicht direkt mit new erzeugt werden - sie dient nur als Basis für andere Klassen
  • Sie kann abstrakte Methoden enthalten - diese haben keinen Codeblock, jede abgeleitete Klasse muss sie implementieren
                    
                        <?php
                        abstract class Shape {
                            abstract public function calculateArea(): float;

                            public function printArea(): void {
                                echo "Fläche: " . $this->calculateArea();
                            }
                        }

                        $shape = new Shape(); // Fehler: abstrakte Klasse
                    
                

Abstrakte Klasse implementieren

  • Konkrete Klassen erben von der abstrakten Klasse und liefern die Umsetzung der abstrakten Methode
  • Nicht-abstrakte Methoden (wie printArea()) werden ganz normal geerbt
                    
                        <?php
                        class Circle extends Shape {
                            public function __construct(private float $radius) {}

                            public function calculateArea(): float {
                                return M_PI * $this->radius ** 2;
                            }
                        }

                        $circle = new Circle(3);
                        $circle->printArea(); // "Fläche: 28.27..."
                    
                

Interfaces

  • Ein interface schreibt vor, welche Methoden eine Klasse haben muss - ohne selbst eine Umsetzung vorzugeben
  • Eine Klasse setzt ein Interface mit implements um und kann dabei auch gleichzeitig von einer Klasse erben
                    
                        <?php
                        interface Describable {
                            public function describe(): string;
                        }

                        class Circle extends Shape implements Describable {
                            public function __construct(private float $radius) {}

                            public function calculateArea(): float {
                                return M_PI * $this->radius ** 2;
                            }

                            public function describe(): string {
                                return "Kreis mit Radius " . $this->radius;
                            }
                        }
                    
                

Unveränderliche Eigenschaften mit readonly

  • Seit PHP 8.1 kann eine Eigenschaft als readonly markiert werden - sie darf nur einmal, direkt im Konstruktor, gesetzt werden
  • Ein späterer Schreibversuch löst einen Fehler aus - nützlich für Werte, die sich nach der Erzeugung nicht mehr ändern sollen
                    
                        <?php
                        class Point {
                            public function __construct(
                                public readonly float $x,
                                public readonly float $y
                            ) {}
                        }

                        $p = new Point(3, 4);
                        echo $p->x;  // 3
                        $p->x = 10;  // Fehler: readonly-Eigenschaft
                    
                

Der instanceof-Operator

  • Mit instanceof prüft man, ob ein Objekt von einer bestimmten Klasse (oder einem Interface) ist
  • Praktisch, wenn ein Array Objekte verschiedener, verwandter Klassen enthält
                    
                        <?php
                        $shapes = [new Circle(3), new Product("Apfel", 0.5)];

                        foreach ($shapes as $item) {
                            if ($item instanceof Shape) {
                                $item->printArea();
                            }
                        }
                        // nur der Kreis wird ausgegeben - Product ist kein Shape
                    
                

TL;DR

  • Eine Klasse ist ein Bauplan für Objekte - mit new wird daraus eine konkrete Instanz erzeugt
  • Eigenschaften und Methoden gehören zusammen; $this zeigt innerhalb einer Methode auf das eigene Objekt
  • public, private und protected steuern die Sichtbarkeit - Getter/Setter kapseln private Daten
  • static gehört der Klasse, nicht dem Objekt; const definiert unveränderliche Klassenwerte
  • extends vererbt, abstract-Klassen und interfaces legen fest, was abgeleitete Klassen können müssen