Ausblick auf PHP 8: Teil 5

PHP 8.0-Features im Fokus: Allgemeine Verbesserungen
Keine Kommentare

Das Release von PHP 8 steht kurz bevor und mit ihm eine Reihe von Verbesserungen, neuen Funktionen und die generelle Überarbeitung, um die weit verbreitete serverseitige Websprache noch besser zu machen. In dieser Serie wollen wir darauf eingehen, was Sie über PHP 8 wissen sollten.

Der Countdown zum großen Release von PHP 8 ist bereits angebrochen. Die lang erwartete neue Version enthält eine ganze Reihe von neuen Funktionen und Verbesserungen, die den Entwicklern das Leben leichter machen sollen. In dieser Serie werfen wir einen Blick auf diese Neuerungen und was sie für die Nutzer bedeuten.

PHP hat eine riesige Anzahl von String-Funktionen, von sehr nützlichen bis hin zu eher obskuren. PHP 8.0 bringt nun gleich drei neue Funktionen mit sich.

str_starts_with() und str_ends_with() wurden gemeinsam implementiert und tun genau das, was sie bereits in ihren Namen versprechen. Beide nehmen einen String zum Prüfen sowie einen Partial String zum Testen und geben einen Boolean zurück, wenn der erste String mit dem zweiten beginnt (oder endet).

<?php
$needle = 'hello';
if (str_starts_with('hello world', $needle)) {
    print "This must be your first program.";
}

Beide Funktionen stimmen in ASCII exakt überein und haben keine Groß-/Kleinschreibung, da dies als zu kompliziert angesehen wurde. Wenn eine Prüfung mit Berücksichtigung der Groß-/Kleinschreibung benötigt werden sollte, kann zuerst strtolower() für beide Strings ausgeführt werden. Diese Funktionen wurden uns durch den RFC von Will Hudgins zur Verfügung gestellt. In ähnlicher Weise ist auch str_contains() ziemlich selbsterklärend. Es prüft, ob der erste String den zweite überhaupt enthält.

<?php
$needle = 'on'; if (str_contains('peace on earth', $needle)) { print "Yes, let's do that."; }

Wie bei den früheren Funktionen ist str_contains() ein ASCII-Vergleich unter Berücksichtigung der Groß-/Kleinschreibung. Der RFC dafür stammt von Philipp Tanlak.

Alle drei Funktionen konnten im User Space durch eine clevere Verwendung von strpos() nachgeahmt werden, wobei „clever“ eine höfliche Art ist, „fehleranfällig“ zu sagen. Einer der häufigsten Fehler von PHP-Neulingen (die von vielen sehr erfahrenen Entwicklern gemacht wurden) war es, eine str_contains()-Typüberprüfung wie diese durchzuführen:

<?php

$its_in_there = strpos($haystack, $needle) == false;

Aufgrund der Typenjonglage von PHP ist diese Basic-Equality-Überprüfung (==) jedoch unzureichend, da eine Position von 0 (d.h. die $needle ist der erste Teil von $haystack) == falsch wäre. Die Prüfung muss identisch (===) sein, was leicht vergessen wird. Es ist auch ein perfektes Beispiel dafür, warum “ return a value or false on error“ keine gute Idee ist und nie und nimmer verwendet werden sollte. Glücklicherweise ist mit PHP 8.0 all diese Cleverness nicht mehr nötig, da das Trio der neuen Funktionen typsicher den Boolean zurückgibt, der von Anfang an gewollt war.

Expressions werfen

PHP unterstützt Ausnahmen über das Keyword throw. Aus verschiedenen internen Gründen wurde throw als Statement implementiert, d.h. es gab keinen Wert zurück und war einfach „ein Ding“. In PHP 8 wurde throw in eine Expression umgewandelt, was bedeutet, dass es technisch gesehen einen Wert zurückgeben kann. Das ist nicht sehr nützlich (throw bricht die aktuelle Ausführungszeile absichtlich um), aber es bedeutet, dass throw jetzt an Expression Places im Code verwendet werden kann. Das beste Beispiel dafür, wo das nützlich ist, sind Ternaries, Null Coalesces und andere Symbole für verkürzte Expressions:

<?php

// $value is non-nullable.
$value = $nullableValue ?? throw new InvalidArgumentException();
 
// $value is truthy.
$value = $falsableValue ?: throw new InvalidArgumentException();

In PHP 7 sind dies Syntaxfehler. In PHP 8.0 tun sie das, was man erwarten würde: den Wert zuweisen, wenn möglich, oder werfen, wenn die Bedingung fehlschlägt. Eine noch lustigere Kombination ist das Mischen mit den neuen match() expression:

<?php

$value = match($size) {
  0 => throw new ThatsNotASize(),
  1 => throw new ThatsTooSmall(),
  3, 4 => 'small',
  5, 6 => 'medium',
  default => 'large',
};

throw-Expressions verdanken wir Ilija Tovilo.

Non-Capturing Catch

Apropos Ausnahmen: Es gibt Fälle, in denen eine Exception abgefangen werden soll. Aber was als Antwort darauf getan werden soll, hängt nicht von den Daten der Exception selbst ab – das Abfangen ist nun möglich, ohne sie einer Variablen zuzuweisen, wie dieses Beispiel zeigt:

<?php

try {
    changeImportantData();
} catch (PermissionException) {
    echo "You don't have permission to do this";
}

Dieser catch löst Ausnahmen für PermissionException aus, wobei die Exception selbst keinem Wert zugewiesen wird.
Max Semenik war für den Inhalt dieses RFC federführend.

IT Security Camp im Februar 2021

Mit Christian Schneider

Christian ist als freiberuflicher Whitehat Hacker, Trainer und Security-Coach tätig. Als Softwareentwickler mit mittlerweile 20 Jahren Erfahrung fand er 2005 seinen Themenschwerpunkt im Bereich IT-Security.

Interview mit Christian Schneider zum Thema „DevSecOps“.

DevSecOps ist, bezogen auf Security-Checks, die logische Fortführung der Automatisierung im DevOps-Sinne

Trailing Commas in Funktionsdefinitionen und Closures

Vor langer Zeit gab es auf einer weit entfernten Mailingliste (d.h. im Jahr 2017) den Vorschlag, an allen Stellen, an denen PHP eine Liste von Werten unterstützt, ein Trailing Comma zuzulassen. Das scheiterte leider in den meisten Fällen, aber nachfolgende RFCs, die die Komma-Unterstützung für jedes Listenkonstrukt unabhängig voneinander vorsahen, wurden angenommen. Die neuesten Ergänzungen für PHP 8 sind die Funktionsdefinitionen und Closures. Bei Funktionsdefinitionen ist es jetzt möglich, nach der letzten Parameterdefinition ein Komma zu setzen, was vor allem dann nützlich ist, wenn viele Parameter auf mehrere Zeilen aufgeteilt sind.

<?php
Class Address
{
    public function __construct(
        string $street,
        string $number,
        string $city,
        string $state,
        string $zip,     // This is new.
    ) { 
    // ... 
    }
}

Trailing Commas in Funktionsaufrufen wurden bereits in PHP 7.3 hinzugefügt. Dieses Update ist Nikita Popov zu verdanken.
Aus dem gleichen Grund sind Trailing Commas in useClosures erlaubt:

<?php
$a ='A';
$b = 'B';
$c = 'C';

$dynamic_function = function(
  $first,
  $second,
  $third,       // This is new, as above.
) use (
  $a,
  $b,
  $c,            // This is also now new.
);

Diese Änderung wurde mit freundlicher Genehmigung von Tyson Andre vorgenommen.

Token Objects

Schließlich haben wir noch eine Änderung, die auf den ersten Blick ein wenig merkwürdig klingt. Für Leute, die Code-Analyse oder Code-Generierung durchführen, kann sie aber dennoch sehr wichtig sein kann.

Die PHP-Funktion token_get_all() ermöglicht es PHP, PHP-Code-Dateien zu parsen und ein Array von Arrays zurückzubekommen, das den Stream of Tokens in dieser Datei darstellt. Ein riesiges Array von Arrays ist jedoch nicht immer der nützlichste Weg, um, nun ja, irgendetwas darzustellen. Zum einen sind PHP-Arrays sehr speicherfressend. Zum anderen sind die verschachtelten Arrays positionell, und die Bedeutung jeder Position variiert je nach Art des Tokens. Dieses Architekturmuster ist in der akademischen Literatur als „fugly“ bekannt.
PHP 8.0 führt eine neue Klasse, PhpToken, mit einer entsprechenden statischen getAll()-Methode ein. PhpToken::getAll() wird ein String vom PHP-Code übergeben und gibt ein Array von PhpToken-Objekten zurück. Da sie Objekte verwendet, ist diese Methode schneller, verbraucht halb so viel Speicher und ist einfacher zu benutzen als token_get_all().

PhpToken enthält auch ein paar Hilfsmethoden, um Informationen über das Token zu erhalten, wie getTokenName() oder isIgnorable(). (Der RFC enthält mehr Details.) Es gibt sogar eine __toString()-Implementierung, die das Objekt wieder in seine Quellcode-String-Form zurückbringt. Darüber hinaus ist es auch möglich, die Klasse zu erweitern und eigene, benutzerdefinierten Methoden hinzuzufügen.

<?php

$code = '...'; // Some PHP source code.

class LowerCaseToken extends PhpToken implements Stringable {
    public function getLowerText() {
        return strtolower($this->text);
    }

    public function __toString(): string {
        return $this->getLowerText();
    }
}

$tokens = LowerCaseToken::getAll($code);

print implode($tokens);

In diesem (extrem ausgeklügelten) Beispiel gibt LowerCaseToken::getAll($code); ein Array von LowerCaseToken -Objekten zurück. implode() konvertiert dann jedes in einen String und verkettet sie ohne zusätzliches Trennzeichen. „Konvertiert in einen String“ bedeutet, dass __toString() aufgerufen wird, die eine kleingeschriebene Version der String-Darstellung des Tokens zurückgibt. Das Nettoergebnis besteht darin, ein Stück Code zu nehmen und ihn in alle Kleinbuchstaben zu zwingen, ohne den Code oder seine Struktur anderweitig zu verändern.

Dies ist zugegebenermaßen nicht das praktikabelste Beispiel, aber man könnte sich vorstellen, eine Datei mit PHP-Quellcode einzulesen, das Array in irgendeiner strukturierten Weise zu modifizieren und es dann in modifizierter Form trivial zu serialisieren, wobei die Struktur und Ausrichtung des Textes beibehalten wird. Alternativ könnte man zusätzliche Methoden hinzufügen, die bei fortgeschrittenem Parsing für Code-Formatierung, statische Analyse oder andere Meta-Codierungsaufgaben helfen.

Es sollte keine Überraschung sein, dass uns dieser RFC mit freundlicher Genehmigung von Nikita Popov zur Verfügung gestellt wurde.

In der nächsten Woche widmen wir uns den kleineren Änderungen, die PHP zwar vorhersehbarer und logischer machen, aber durchaus den Code beeinflussen könnten.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Abonnieren
Benachrichtige mich bei
guest
0 Comments
Inline Feedbacks
View all comments
X
- Gib Deinen Standort ein -
- or -