## Typdeklarationen

Typen können für Funktionsargumente, Rückgabewerte, seit PHP 7.4.0 für Klasseneigenschaften und seit PHP 8.3.0 für Klassenkonstanten deklariert werden. Dadurch wird sichergestellt, dass der Wert zum Zeitpunkt des Aufrufs vom angegebenen Typ ist und andernfalls ein TypeError geworfen.

Mit Ausnahme des Typs resource können alle von PHP unterstützten Typen in einer benutzerdefinierten Typdeklaration verwendet werden. Diese Seite enthält eine Changelog über die Verfügbarkeit der verschiedenen Typen und eine Dokumentation über ihre Verwendung in Typdeklarationen.

> Hinweis:

> Wenn eine Klasse eine Methode einer Schnittstelle implementiert oder eine Methode neu implementiert, die bereits von der Elternklasse definiert wurde, muss sie mit der obigen Definition kompatibel sein. Eine Methode ist kompatibel, wenn sie den Regeln der Kovarianz und Kontravarianz folgt.

### Changelog

Version | Beschreibung
--- | ---
8.3.0 | Unterstützung für die Typisierung von Klassen-, Interface-, Trait- und Enum-Konstanten wurde hinzugefügt.
8.2.0 | Unterstützung für DNF-Typen wurde hinzugefügt.
8.2.0 | Unterstützung für den literalen Typ true wurde hinzugefügt.
8.2.0 | Die Typen null und false können nun eigenständig verwendet werden.
8.1.0 | Unterstützung für Schnittmengentypen wurde hinzugefügt.
8.1.0 | Die Rückgabe per Referenz aus einer void-Funktion ist nun veraltet.
8.1.0 | Unterstützung für den Rückgabetyp never wurde hinzugefügt.
8.0.0 | Unterstützung für mixed wurde hinzugefügt.
8.0.0 | Unterstützung für den reinen Rückgabetyp static wurde hinzugefügt.
8.0.0 | Unterstützung für Union-Typen wurde hinzugefügt.
7.4.0 | Unterstützung für die Typisierung von Klasseneigenschaften wurde hinzugefügt.
7.2.0 | Unterstützung für object wurde hinzugefügt.
7.1.0 | Unterstützung für iterable wurde hinzugefügt.
7.1.0 | Unterstützung für void wurde hinzugefügt.
7.1.0 | Unterstützung für nullable Typen wurde hinzugefügt.

### Hinweise zur Verwendung von atomaren (einfachen) Typen

Atomare Typen verhalten sich einfach, wobei es einige kleinere Einschränkungen gibt, auf die in diesem Abschnitt eingegangen wird.

#### Skalare Typen

Warnung

Namens-Aliase für skalare Typen (bool, int, float, string) werden nicht unterstützt. Stattdessen werden sie wie die Namen von Klassen oder Schnittstellen behandelt. Wird zum Beispiel `boolean` als Typdeklaration verwendet, muss der Wert ein `instanceof` der Klasse oder Schnittstelle `boolean` sein, und nicht vom Typ bool:

    <?php
        function test(boolean $param) {}
        test(true);
    ?>

Das oben gezeigte Beispiel erzeugt mit PHP 8 folgende Ausgabe:

    Warning: "boolean" will be interpreted as a class name. Did you mean "bool"? Write "\boolean" to suppress this warning in /in/9YrUX on line 2

    Fatal error: Uncaught TypeError: test(): Argument #1 ($param) must be of type boolean, bool given, called in - on line 3 and defined in -:2
    Stack trace:
    #0 -(3): test(true)
    #1 {main}
      thrown in - on line 2

#### void

> Hinweis:

> Die Rückgabe per Referenz aus einer Funktion, die void zurückgibt, ist seit PHP 8.1.0 veraltet, da eine solche Funktion widersprüchlich ist. Vor PHP 8.1.0 gab sie bereits folgende `E_NOTICE` aus, wenn sie aufgerufen wurde: Only variable references should be returned by reference.

>     <?php
>     function &test(): void {}
>     ?>

#### Callable

Dieser Typ kann nicht als Typdeklaration für die Eigenschaft einer Klasse verwendet werden.

> Hinweis: Es ist nicht möglich, eine Signatur für die Funktion anzugeben.

#### Typdeklarationen für Parameter, die per Referenz übergeben werden

Wenn ein per Referenz übergebener Parameter eine Typdeklaration hat, wird der Typ der Variablen nur beim Eintritt in die Funktion, zu Beginn des Aufrufs, überprüft, nicht aber bei der Rückgabe der Funktion. Das bedeutet, dass eine Funktion den Typ einer Variablenreferenz ändern kann.

Beispiel #1 Typisierte Parameter, die per Referenz übergeben werden

    <?php
    function array_baz(array &$param)
    {
        $param = 1;
    }
    $var = [];
    array_baz($var);
    var_dump($var);
    array_baz($var);
    ?>

Das oben gezeigte Beispiel erzeugt eine ähnliche Ausgabe wie:

    int(1)

    Fatal error: Uncaught TypeError: array_baz(): Argument #1 ($param) must be of type array, int given, called in - on line 9 and defined in -:2
    Stack trace:
    #0 -(9): array_baz(1)
    #1 {main}
      thrown in - on line 2

### Hinweise zur Verwendung von zusammengesetzten Typen

Bei der Deklaration von zusammengesetzten Typen gibt es mehrere Einschränkungen und um einfache Fehler zu vermeiden, wird bei der Kompilierung eine Redundanzprüfung durchgeführt.

Achtung

Vor PHP 8.2.0, d. h. vor der Einführung von DNF-Typen, war es nicht möglich, Schnittmengentypen (Intersection-Typen) mit zusammengefassten Typen (Union-Typen) zu kombinieren.

#### Zusammengefasste Typen (Union-Typen)

Warnung

Es ist nicht möglich, die beiden Singleton-Typen false und true in einem Union-Typ zu kombinieren; stattdessen kann bool verwendet werden.

Achtung

Da false und null vor PHP 8.2.0 nicht als eigenständige Typen verwendet werden konnten, war ein Union-Typ, der nur aus diesen Typen bestand, nicht erlaubt. Hierbei handelt es sich um die folgenden Typen: false, `false|null` und `?false`.

##### Syntaktischer Zucker für den Typ nullable

Wenn ein einzelner Basistyp deklariert wird, kann dieser als nullable gekennzeichnet werden, indem dem Typnamen ein Fragezeichen (`?`) vorangestellt wird. Somit bedeuten `?T` und `T|null` dasselbe.

> Hinweis: Diese Syntax wird seit PHP 7.1.0 unterstützt und geht der Unterstützung von allgemeinen Union-Typen voraus.

> Hinweis:

> Es ist auch möglich, nullable Parameter anzugeben, indem `null` zum Standardwert gemacht wird. Dies ist jedoch nicht empfehlenswert, denn wenn der Standardwert in einer Kindklasse geändert wird, wird eine Verletzung der Typkompatibilität ausgelöst, da der Typ null zur Typdeklaration hinzugefügt werden muss. Darüber hinaus ist dieses Verhalten seit PHP 8.4 veraltet.

> Beispiel #2 Die alte Methode, um Parameter nullable zu machen

>     <?php
>     class C {}

>     function f(C $c = null) {
>         var_dump($c);
>     }

>     f(new C);
>     f(null);
>     ?>

> Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

>     object(C)#1 (0) {
>     }
>     NULL

#### Doppelte und redundante Typen

Um einfache Fehler in Deklarationen von zusammengesetzten Typen abzufangen, führen redundante Typen, die ohne Laden einer Klasse erkannt werden können, zu einem Fehler beim Kompilieren. Dies schließt Folgendes ein:

- Jeder Typ, dessen Name aufgelöst wird, darf nur einmal vorkommen. Typen wie `int|string|INT` oder `Countable&Traversable&COUNTABLE` führen zu einem Fehler.
- Die Verwendung des Typs mixed oder never führt zu einem Fehler.
- Bei zusammengesetzen Typen:
  - Wenn der Typ bool verwendet wird, können nicht zusätzlich die Typen false oder true verwendet werden.
  - Wenn der Typ object verwendet wird, können nicht zusätzlich Klassentypen verwendet werden.
  - Wenn der Typ iterable verwendet wird, können nicht zusätzlich array und Traversable verwendet werden.
- Bei Schnittmengentypen:
  - Die Verwendung eines Typs, der kein Klassentyp ist, führt zu einem Fehler.
  - Die Verwendung von self, parent oder static führt zu einem Fehler.
- Bei DNF-Typen:
  - Wenn ein allgemeinerer Typ verwendet wird, ist der restriktivere Typ überflüssig.
  - Die Verwendung zweier identischer Schnittmengentypen.

> Hinweis: Dies garantiert nicht, dass der Typ "minimal" ist, weil dies bedeuten würde, dass alle verwendeten Klassentypen geladen werden müssten.

Wenn zum Beispiel `A` und `B` Aliase von Klassen sind, dann bleibt `A|B` ein gültiger Union-Typ, auch wenn er entweder auf `A` oder `B` reduziert werden könnte. Bei `B extends A {}` ist `A|B` ebenfalls ein gültiger Union-Typ, auch wenn er auf `A` reduziert werden kann.

    <?php
    function foo(): int|INT {} // Unzulässig
    function foo(): bool|false {} // Unzulässig
    function foo(): int&Traversable {} // Unzulässig
    function foo(): self&Traversable {} // Unzulässig

    use A as B;
    function foo(): A|B {} // Unzulässig ("use" ist Teil der Namensauflösung)
    function foo(): A&B {} // Unzulässig ("use" ist Teil der Namensauflösung)

    class_alias('X', 'Y');
    function foo(): X|Y {} // Zulässig (Redundanz ist nur zur Laufzeit bekannt)
    function foo(): X&Y {} // Zulässig (Redundanz ist nur zur Laufzeit bekannt)
    ?>

### Beispiele

Beispiel #3 Grundlegendes Beispiel für die Deklaration eines Klassentyps

    <?php
    class C {}
    class D extends C {}

    // Das erweitert C nicht.
    class E {}

    function f(C $c) {
        echo get_class($c)."\n";
    }

    f(new C);
    f(new D);
    f(new E);
    ?>

Das oben gezeigte Beispiel erzeugt mit PHP 8 folgende Ausgabe:

    C
    D

    Fatal error: Uncaught TypeError: f(): Argument #1 ($c) must be of type C, E given, called in /in/gLonb on line 14 and defined in /in/gLonb:8
    Stack trace:
    #0 -(14): f(Object(E))
    #1 {main}
      thrown in - on line 8

Beispiel #4 Grundlegendes Beispiel für die Deklaration eines Schnittstellentyps

    <?php
    interface I { public function f(); }
    class C implements I { public function f() {} }

    // Das implementiert I nicht.
    class E {}

    function f(I $i) {
        echo get_class($i)."\n";
    }

    f(new C);
    f(new E);
    ?>

Das oben gezeigte Beispiel erzeugt mit PHP 8 folgende Ausgabe:

    C

    Fatal error: Uncaught TypeError: f(): Argument #1 ($i) must be of type I, E given, called in - on line 13 and defined in -:8
    Stack trace:
    #0 -(13): f(Object(E))
    #1 {main}
      thrown in - on line 8

Beispiel #5 Grundlegendes Beispiel für die Deklaration eines Rückgabetyps

    <?php
    function sum($a, $b): float {
        return $a + $b;
    }

    // Hier wird ein Gleitkommawert zurückgegeben.
    var_dump(sum(1, 2));
    ?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

    float(3)

Beispiel #6 Zurückgeben eines Objekts

    <?php
    class C {}

    function getC(): C {
        return new C;
    }

    var_dump(getC());
    ?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

    object(C)#1 (0) {
    }

Beispiel #7 Deklaration eines nulllable Parameters

    <?php
    class C {}

    function f(?C $c) {
        var_dump($c);
    }

    f(new C);
    f(null);
    ?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

    object(C)#1 (0) {
    }
    NULL

Beispiel #8 Deklaration eines nulllable Rückgabetyps

    <?php
    function get_item(): ?string {
        if (isset($_GET['item'])) {
            return $_GET['item'];
        } else {
            return null;
        }
    }
    ?>

Beispiel #9 Deklaration eines Klasseneigenschafts-Typs

    <?php
    class User {
        public static string $foo = 'foo';

        public int $id;
        public string $username;

        public function __construct(int $id, string $username) {
            $this->id = $id;
            $this->username = $username;
        }
    }
    ?>

### Strikte Typisierung

Standardmäßig wandelt PHP Werte eines falschen Typs automatisch in den erwarteten Typ um, wenn dies möglich ist. Wenn an eine Funktion zum Beispiel ein Wert vom Typ int für einen Parameter übergeben wird, der einen Wert vom Typ string erwartet, dann erhält die Funktion eine Variable vom Typ string.

Der strikte Modus kann auf Dateibasis aktiviert werden. Im strikten Modus wird nur ein Wert akzeptiert, der genau der Typdeklaration entspricht, andernfalls wird ein TypeError geworfen. Die einzige Ausnahme von dieser Regel ist die Übergabe eines int-Wertes, wenn ein float-Wert erwartet wird.

Warnung

Funktionsaufrufe innerhalb interner Funktionen sind von der `strict_types`-Deklaration nicht betroffen.

Um den strikten Modus zu aktivieren, wird die `declare`-Anweisung zusammen mit der `strict_types`-Deklaration verwendet:

> Hinweis:

> Strikte Typisierung gilt für Funktionsaufrufe, die innerhalb einer Datei mit aktivierter strikter Typisierung gemacht werden, nicht für die Funktionen, die innerhalb dieser Datei deklariert sind. Wenn eine Funktion, die in einer Datei mit strikter Typisierung definiert ist, aus einer Datei aufgerufen wird, in der die strikte Typisierung nicht aktiviert ist, wird die Präferenz des Aufrufers (automatische Umwandlung des Typs, coercive typing) berücksichtigt und der Wert wird typkonvertiert.

> Hinweis:

> Die strikte Typisierung ist nur für skalare Typdeklarationen definiert.

Beispiel #10 Strikte Typisierung für die Werte von Argumenten

    <?php
    declare(strict_types=1);

    function sum(int $a, int $b) {
        return $a + $b;
    }

    var_dump(sum(1, 2));
    var_dump(sum(1.5, 2.5));
    ?>

Das oben gezeigte Beispiel erzeugt mit PHP 8 folgende Ausgabe:

    int(3)

    Fatal error: Uncaught TypeError: sum(): Argument #1 ($a) must be of type int, float given, called in - on line 9 and defined in -:4
    Stack trace:
    #0 -(9): sum(1.5, 2.5)
    #1 {main}
      thrown in - on line 4

Beispiel #11 Erzwungene Typisierung für die Werte von Argumenten

    <?php
    function sum(int $a, int $b) {
        return $a + $b;
    }

    var_dump(sum(1, 2));

    // Diese werden in Ganzzahlen umgewandelt: man beachte die folgende Ausgabe!
    var_dump(sum(1.5, 2.5));
    ?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

    int(3)
    int(3)

Beispiel #12 Strikte Typisierung für Rückgabewerte

    <?php
    declare(strict_types=1);

    function sum($a, $b): int {
        return $a + $b;
    }

    var_dump(sum(1, 2));
    var_dump(sum(1, 2.5));
    ?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

    int(3)

    Fatal error: Uncaught TypeError: sum(): Return value must be of type int, float returned in -:5
    Stack trace:
    #0 -(9): sum(1, 2.5)
    #1 {main}
      thrown in - on line 5

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open