PHP 8.4.1 Released!

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 Wertetypen 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.

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 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
add a note

User Contributed Notes 2 notes

up
22
toinenkayt (ta at ta) [iwonderr] gmail d
3 years ago
While waiting for native support for typed arrays, here are a couple of alternative ways to ensure strong typing of arrays by abusing variadic functions. The performance of these methods is a mystery to the writer and so the responsibility of benchmarking them falls unto the reader.

PHP 5.6 added the splat operator (...) which is used to unpack arrays to be used as function arguments. PHP 7.0 added scalar type hints. Latter versions of PHP have further improved the type system. With these additions and improvements, it is possible to have a decent support for typed arrays.

<?php
declare (strict_types=1);

function
typeArrayNullInt(?int ...$arg): void {
}

function
doSomething(array $ints): void {
(function (?
int ...$arg) {})(...$ints);
// Alternatively,
(fn (?int ...$arg) => $arg)(...$ints);
// Or to avoid cluttering memory with too many closures
typeArrayNullInt(...$ints);

/* ... */
}

function
doSomethingElse(?int ...$ints): void {
/* ... */
}

$ints = [1,2,3,4,null];
doSomething ($ints);
doSomethingElse (...$ints);
?>

Both methods work with all type declarations. The key idea here is to have the functions throw a runtime error if they encounter a typing violation. The typing method used in doSomethingElse is cleaner of the two but it disallows having any other parameters after the variadic parameter. It also requires the call site to be aware of this typing implementation and unpack the array. The method used in doSomething is messier but it does not require the call site to be aware of the typing method as the unpacking is performed within the function. It is also less ambiguous as the doSomethingElse would also accept n individual parameters where as doSomething only accepts an array. doSomething's method is also easier to strip away if native typed array support is ever added to PHP. Both of these methods only work for input parameters. An array return value type check would need to take place at the call site.

If strict_types is not enabled, it may be desirable to return the coerced scalar values from the type check function (e.g. floats and strings become integers) to ensure proper typing.
up
13
crash
3 years ago
The documentation lacks the information, that it's possible to change the return type of a method defined in an interface when the interface's methods return type is defined as `mixed`.

From the RFC:

"The mixed return type could be narrowed in a subclass as this is covariant and is allowed in LSP." (https://wiki.php.net/rfc/mixed_type_v2)

This means the following code is valid in PHP 8.0:

<?php

interface ITest
{
public function
apfel(): mixed; // valid as of 8.0
}

class
Test implements ITest
{
public function
apfel(): array // more explicit
{
return [];
}
}

var_dump((new Test())->apfel());
?>

You can see the result here: https://3v4l.org/PXDB6
To Top