PHP 8.0: Vollständiger Überblick über die Neuigkeiten (1/4)
PHP Version 8.0 wird in diesem Moment veröffentlicht. Sie ist voll von neuen Dingen, wie keine andere Version zuvor. Ihre Einführung hat vier separate Artikel verdient. Im ersten werfen wir einen Blick darauf, was sie auf der Sprachebene bringt.
Bevor wir uns mit PHP befassen, sollten Sie wissen, dass die aktuelle Version von Nette vollständig auf die achte Version vorbereitet ist. Außerdem wurde als Geschenk ein vollständig kompatibles Nette 2.4 veröffentlicht, so dass aus Sicht des Frameworks nichts dagegen spricht, es zu verwenden.
Benannte Argumente
Fangen wir gleich mit einer Bombe an, die man getrost als Game Changer bezeichnen könnte. Argumente können an Funktionen und Methoden nun nicht mehr nur an der Position, sondern anhand ihres Namens übergeben werden. Was absolut cool ist, falls eine Methode wirklich zu viele Parameter hat:
class Response implements IResponse
{
public function setCookie(
string $name,
string $value,
string|DateInterface|null $time,
string $path = null,
string $domain = null,
bool $secure = null,
bool $httpOnly = null,
string $sameSite = null
) {
...
}
}
Die ersten beiden Argumente werden positionell übergeben, die anderen mit ihrem Namen: (named muss nach den positionellen folgen)
$response->setCookie('lang', $lang, sameSite: 'None');
// instead of the horrible
$response->setCookie('lang', $lang, null, null, null, null, null, 'None');
Vor der Einführung dieser Funktion gab es Pläne, eine neue API für das
Senden von Cookies in Nette zu erstellen, da die Anzahl der Argumente für
setCookie()
stark anstieg und die positionale Notation verwirrend
war. Das ist jetzt nicht mehr nötig, weil benannte Argumente in diesem Fall die
bequemste API sind. Die IDE zeigt sie an und es gibt Typsicherheit.
Sie sind sogar ideal geeignet, um logische Argumente zu erklären, wo ihre
Verwendung nicht notwendig ist, aber ein einfaches true
oder
false
reicht nicht aus:
// before
$db = $container->getService(Database::class, true);
// now
$db = $container->getService(Database::class, need: true);
Die Namen der Argumente sind jetzt Teil der öffentlichen API. Es ist nicht mehr möglich, sie nach Belieben zu ändern. Aus diesem Grund wird auch bei Nette geprüft, ob alle Argumente einen passenden Namen haben.
Benannte Argumente können auch in Kombination mit Variadics verwendet werden:
function variadics($a, ...$args) {
dump($args);
}
variadics(a: 1, b: 2, c: 3);
// $args will contain ['b' => 2, 'c' => 3]
Das Array $args
kann nun auch nichtnumerische Schlüssel
enthalten, was eine Art BC-Break darstellt. Das Gleiche gilt für das Verhalten
von call_user_func_array($func, $args)
, wo die Schlüssel im Array
$args
jetzt eine viel wichtigere Rolle spielen. Im Gegensatz dazu
sind die Funktionen der func_*()
Familie von benannten Argumenten
abgeschirmt.
Benannte Argumente stehen in engem Zusammenhang mit der Tatsache, dass der
Splat-Operator ...
nun assoziative Arrays expandieren kann:
variadics(...['b' => 2, 'c' => 3]);
Überraschenderweise funktioniert er derzeit nicht innerhalb von Arrays:
$arr = [ ...['a' => 1, 'b' => 2] ];
// Fatal error: Cannot unpack array with string keys
Die Kombination von benannten Argumenten und Variadics gibt uns die
Möglichkeit, endlich eine feste Syntax zu haben, z.B. für die Methode
link()
, an die wir sowohl benannte als auch positionale Argumente
übergeben können:
// before
$presenter->link('Product:detail', $id, 1, 2);
$presenter->link('Product:detail', [$id, 'page' => 1]); // had to be an array
// now
$presenter->link('Product:detail', $id, page: 1);
Die Syntax für benannte Argumente ist viel attraktiver als das Schreiben von
Arrays, so dass Latte
sie sofort übernommen hat, wo sie zum Beispiel in den Tags
{include}
und {link}
verwendet werden kann:
{include 'file.latte' arg1: 1, arg2: 2}
{link default page: 1}
Wir werden auf benannte Argumente im dritten Teil der Serie im Zusammenhang mit Attributen zurückkommen.
Ein Ausdruck kann eine Ausnahme auslösen
Das Auslösen einer Ausnahme ist jetzt ein Ausdruck. Sie können ihn in
Klammern einschließen und zu einer if
Bedingung hinzufügen. Hmmm,
das klingt nicht sehr praktisch. Aber das hier ist viel interessanter:
// before
if (!isset($arr['value'])) {
throw new \InvalidArgumentException('value not set');
}
$value = $arr['value'];
// now, when throw is an expression
$value = $arr['value'] ?? throw new \InvalidArgumentException('value not set');
Da Pfeilfunktionen bisher nur einen Ausdruck enthalten konnten, können sie nun dank dieser Funktion Ausnahmen auslösen:
// only single expression
$fn = fn() => throw new \Exception('oops');
Match-Ausdruck
Die Anweisung switch-case
hat zwei große Nachteile:
- sie verwendet einen nicht-strikten Vergleich
==
anstelle von===
- man muss aufpassen, dass man nicht versehentlich
break
vergisst
Aus diesem Grund bietet PHP eine Alternative in Form des neuen Ausdrucks
match
an, der einen strengen Vergleich verwendet und umgekehrt
break
nicht benutzt.
switch
Code-Beispiel:
switch ($statusCode) {
case 200:
case 300:
$message = $this->formatMessage('ok');
break;
case 400:
$message = $this->formatMessage('not found');
break;
case 500:
$message = $this->formatMessage('server error');
break;
default:
$message = 'unknown status code';
break;
}
Und dasselbe (nur mit strengem Vergleich), geschrieben mit
match
:
$message = match ($statusCode) {
200, 300 => $this->formatMessage('ok'),
400 => $this->formatMessage('not found'),
500 => $this->formatMessage('server error'),
default => 'unknown status code',
};
Beachten Sie, dass match
keine Kontrollstruktur wie
switch
ist, sondern ein Ausdruck. In diesem Beispiel weisen wir den
resultierenden Wert einer Variablen zu. Gleichzeitig sind die einzelnen
“Optionen” auch Ausdrücke, so dass es nicht möglich ist, mehrere Schritte
zu schreiben, wie im Fall von switch
.
Wenn es keine Übereinstimmung gibt (und es gibt keine Standardklausel), wird
die Ausnahme UnhandledMatchError
ausgelöst.
Übrigens gibt es in Latte auch die Tags {switch}
,
{case}
und {default}
. Ihre Funktion entspricht genau
der des neuen match
. Sie verwenden einen strikten Vergleich,
benötigen keine break
und es ist möglich, mehrere durch Kommas
getrennte Werte in case
anzugeben.
Nullsafe-Operator
Die optionale Verkettung ermöglicht es Ihnen, einen Ausdruck zu schreiben,
dessen Auswertung stoppt, wenn er auf null trifft. Das ist dank des neuen
?->
Operators möglich. Er ersetzt eine Menge Code, der sonst
immer wieder auf null geprüft werden müsste:
$user?->getAddress()?->street
// approximately translates to
$user !== null && $user->getAddress() !== null
? $user->getAddress()->street
: null
Warum “ungefähr”? Weil der Ausdruck in Wirklichkeit raffinierter
ausgewertet wird, so dass kein Schritt wiederholt wird. Zum Beispiel wird
$user->getAddress()
nur einmal aufgerufen, so dass das Problem,
dass die Methode beim ersten und zweiten Mal etwas anderes zurückgibt, nicht
auftritt.
Diese Funktion wurde von Latte vor einem Jahr eingeführt. Jetzt wird sie von PHP selbst übernommen. Großartig.
Förderung von Konstruktoreigenschaften
Syntaktischer Zucker, der es erspart, den Typ zweimal und die Variable viermal zu schreiben. Schade, dass es nicht zu einer Zeit kam, als wir noch keine so cleveren IDEs hatten, die das heute für uns schreiben 🙂
class Facade
{
private Nette\Database\Connection $db;
private Nette\Mail\Mailer $mailer;
public function __construct(Nette\Database\Connection $db, Nette\Mail\Mailer $mailer)
{
$this->db = $db;
$this->mailer = $mailer;
}
}
class Facade
{
public function __construct(
private Nette\Database\Connection $db,
private Nette\Mail\Mailer $mailer,
) {}
}
Es funktioniert mit Nette DI, Sie können es benutzen.
Strengere Typprüfungen für arithmetische und bitweise Operatoren
Was einst den dynamischen Skriptsprachen zum Durchbruch verhalf, ist heute ihr schwächster Punkt. Einst wurde PHP die “magischen Anführungszeichen” und die Registrierung globaler Variablen los, und jetzt wird das lockere Verhalten durch Strenge ersetzt. Die Zeiten, in denen man in PHP fast jeden Datentyp addieren, multiplizieren etc. konnte, für den es keinen Sinn machte, sind längst vorbei. Seit Version 7.0 wird PHP immer strenger und seit Version 8.0 endet der Versuch, irgendwelche arithmetischen/bitweisen Operatoren auf Arrays, Objekte oder Ressourcen anzuwenden, mit einem TypeError. Die Ausnahme ist das Hinzufügen von Arrays.
// arithmetic and bitwise operators
+, -, *, /, **, %, <<, >>, &, |, ^, ~, ++, --:
Sanftere String-Zahlen-Vergleiche
Oder machen Sie lose Operatoren wieder großartig.
Es scheint, dass es keinen Platz mehr für den losen Operator ==
gibt, dass er nur ein Tippfehler beim Schreiben von ===
ist, aber
diese Änderung bringt ihn wieder zurück auf die Karte. Wenn wir ihn schon
haben, dann soll er sich auch vernünftig verhalten. Infolge des früheren
“unvernünftigen” Vergleichs könnte zum Beispiel in_array()
Sie unangenehm trollen:
$validValues = ['foo', 'bar', 'baz'];
$value = 0;
dump(in_array($value, $validValues));
// surprisingly returned true
// since PHP 8.0 returns false
Die Änderung des Verhaltens von ==
betrifft den Vergleich von
Zahlen und “numerischen” Zeichenketten und ist in der folgenden Tabelle
dargestellt:
Vergleich | Vor | PHP 8.0 |
---|---|---|
0 == "0" |
wahr | wahr |
0 == "0.0" |
wahr | wahr |
0 == "foo" |
wahr | falsch |
0 == "" |
wahr | falsch |
42 == " 42" |
wahr | wahr |
42 == "42 " |
wahr | wahr |
42 == "42foo" |
wahr | falsch |
42 == "abc42" |
falsch | falsch |
"42" == " 42" |
wahr | wahr |
"42" == "42 " |
falsch | wahr |
Überraschenderweise handelt es sich um einen BC-Bruch im Kern der Sprache, der ohne jeden Widerstand angenommen wurde. Und das ist gut so. JavaScript könnte in dieser Hinsicht sehr neidisch sein.
Fehlerberichterstattung
Viele interne Funktionen lösen jetzt TypeError und ValueError anstelle von
Warnungen aus, die leicht zu ignorieren waren. Eine Reihe von Kernel-Warnungen
wurden neu klassifiziert. Der Shutup-Operator @
unterdrückt nun
keine fatalen Fehler mehr. Und PDO wirft standardmäßig Ausnahmen.
Nette hat immer versucht, diese Dinge auf irgendeine Weise zu lösen. Tracy änderte das Verhalten des Shutup-Operators, Database änderte das PDO-Verhalten, Utils enthalten Ersatz für Standardfunktionen, die Ausnahmen anstelle von leicht zu übersehenden Warnungen werfen, usw. Es ist schön zu sehen, dass die strenge Richtung, die Nette in seiner DNA hat, die native Richtung der Sprache wird.
Negative Array-Schlüsselinkremente
$arr[-5] = 'first';
$arr[] = 'second';
Was wird der Schlüssel des zweiten Elements sein? Früher war es
0
, since PHP 8 it’s -4
.
Nachgestelltes Komma
Die letzte Stelle, an der das nachgestellte Komma nicht stehen durfte, war die Definition von Funktionsargumenten. Dies ist eine Sache der Vergangenheit:
public function __construct(
Nette\Database\Connection $db,
Nette\Mail\Mailer $mailer, // trailing comma
) {
....
}
$object::class
Die magische Konstante ::class
funktioniert auch mit Objekten
$object::class
und ersetzt die Funktion get_class()
vollständig.
Fangen Sie Ausnahmen nur nach Typ
Und schließlich: Es ist nicht notwendig, in der catch-Klausel eine Variable für die Ausnahme anzugeben:
try {
$container->getService(Database::class);
} catch (MissingServiceException) { // no $e
$logger->log('....');
}
*In den nächsten Teilen werden wir wichtige Neuerungen bei den Datentypen sehen, wir werden zeigen, was Attribute sind, welche neuen Funktionen und Klassen in PHP aufgetaucht sind und wir werden den Just in Time Compiler vorstellen.
Um einen Kommentar abzugeben, loggen Sie sich bitte ein