PHP 8.0: Neue Funktionen, Klassen und JIT (4/4)
PHP Version 8.0 ist erschienen. Sie ist so vollgepackt mit Neuerungen wie keine Version zuvor. Ihre Vorstellung erforderte gleich vier separate Artikel. In diesem letzten Teil werfen wir einen Blick auf neue Funktionen und Klassen und stellen den Just-in-Time-Compiler vor.

Neue Funktionen
Die Standardbibliothek von PHP verfügt über Hunderte von Funktionen, und in Version 8.0 sind sechs neue hinzugekommen. Das mag nach wenig klingen, aber die meisten von ihnen schließen Schwachstellen der Sprache. Das passt gut zum Tenor der gesamten Version 8.0, die PHP wie keine Version zuvor vervollständigt und konsolidiert. Eine Übersicht über alle neuen Funktionen und Methoden finden Sie im Migrationsleitfaden.
str_contains()
str_starts_with()
str_ends_with()
Funktionen zur Feststellung, ob eine Zeichenkette mit einer Teilzeichenkette beginnt, endet oder diese enthält.
if (str_contains('Nette', 'te')) {
...
}
Mit der Einführung dieses Trios definiert PHP, wie mit einer leeren Zeichenkette bei der Suche umzugehen ist, woran sich auch alle anderen verwandten Funktionen orientieren, und zwar so, dass die leere Zeichenkette überall vorkommt:
str_contains('Nette', '') // true
str_starts_with('Nette', '') // true
strpos('Nette', '') // 0 (früher false)
Dadurch ist das Verhalten des Trios völlig identisch mit den Pendants in Nette:
str_contains() # Nette\Utils\String::contains()
str_starts_with() # Nette\Utils\String::startsWith()
str_ends_with() # Nette\Utils\String::endsWith()
Warum sind diese Funktionen so wichtig? Die Standardbibliotheken aller
Sprachen sind immer durch die historische Entwicklung belastet, und
Inkonsistenzen und Fehltritte lassen sich nicht vermeiden. Aber gleichzeitig
sind sie die Visitenkarte der jeweiligen Sprache. Es ist verwunderlich, wenn bei
einem 25 Jahre alten PHP Funktionen für so grundlegende Operationen fehlen wie
das Zurückgeben des ersten oder letzten Elements eines Arrays, das Escapen von
HTML ohne Fallstricke (htmlspecialchars
escapet kein Apostroph)
oder eben das Suchen einer Zeichenkette in einer Zeichenkette. Dass es
irgendwie umgangen werden kann, hält nicht stand, denn das Ergebnis ist
dann kein lesbarer und verständlicher Code. Das ist eine Lehre für alle
API-Autoren. Wenn Sie sehen, dass ein erheblicher Teil der Dokumentation einer
Funktion die Erklärung von Tücken einnimmt (wie z.B. die Rückgabewerte von
strpos
), ist das ein klares Signal, die Bibliothek anzupassen und
eben str_contains
zu ergänzen.
get_debug_type()
Ersetzt das bereits veraltete get_type()
. Anstelle langer Typen
wie integer
gibt es die heute gebräuchlichen int
zurück, im Falle von Objekten gibt es direkt den Typ zurück:
Wert | gettype() | get_debug_type() |
---|---|---|
'abc' |
string |
string |
[1, 2] |
array |
array |
231 |
integer |
int |
3.14 |
double |
float |
true |
boolean |
bool |
null |
NULL |
null |
new stdClass |
object |
stdClass |
new Foo\Bar |
object |
Foo\Bar |
function() {} |
object |
Closure |
new class {} |
object |
class@anonymous |
new class extends Foo {} |
object |
Foo@anonymous |
curl_init() |
resource |
resource (curl) |
curl_close($ch) |
resource (closed) |
resource (closed) |
Objektivierung von Ressourcen
Werte vom Typ Ressource stammen aus Zeiten, als PHP noch keine Objekte hatte, sie aber eigentlich brauchte. So kamen die Ressourcen zur Welt. Heute haben wir Objekte, und im Vergleich zu Ressourcen funktionieren sie viel besser mit dem Garbage Collector, daher ist geplant, sie nach und nach alle durch Objekte zu ersetzen.
Ab PHP 8.0 werden Ressourcen für Bilder, Curl-Verbindungen, OpenSSL, XML usw. in Objekte umgewandelt. In PHP 8.1 werden FTP-Verbindungen usw. folgen.
$res = imagecreatefromjpeg('image.jpg');
$res instanceof GdImage // true
is_resource($res) // false - BC break
Diese Objekte haben bisher keine Methoden, und Sie können auch nicht direkt
ihre Instanzen erstellen. Es geht vorerst wirklich nur darum, die veralteten
Ressourcen aus PHP ohne API-Änderung zu entfernen. Und das ist gut so, denn die
Erstellung einer guten API ist eine separate und anspruchsvolle Aufgabe. Niemand
wünscht sich, dass in PHP weitere Klassen wie SplFileObject mit Methoden namens
fgetc()
oder fgets()
entstehen.
PhpToken
Auch der Tokenizer und damit die Funktionen um token_get_all
werden in Objekte verschoben. Diesmal geht es nicht darum, Ressourcen
loszuwerden, sondern wir erhalten ein vollwertiges Objekt, das ein einzelnes
PHP-Token darstellt.
<?php
$tokens = PhpToken::tokenize('<?php $a = 10;');
$token = $tokens[0]; // Instanz von PhpToken
echo $token->id; // T_OPEN_TAG
echo $token->text; // '<?php'
echo $token->line; // 1
echo $token->getTokenName(); // 'T_OPEN_TAG'
echo $token->is(T_STRING); // false
echo $token->isIgnorable(); // true
Die Methode isIgnorable()
gibt true für die Tokens
T_WHITESPACE
, T_COMMENT
, T_DOC_COMMENT
und T_OPEN_TAG
zurück.
Weak Maps
Weak Maps hängen mit dem Garbage Collector zusammen, der alle Objekte und Werte aus dem Speicher freigibt, die nicht mehr verwendet werden (d.h. es gibt keine verwendete Variable oder Property mehr, die sie enthält). Da die Lebensdauer eines PHP-Threads kurzlebig ist und wir heute auf Servern ausreichend Speicher zur Verfügung haben, beschäftigen wir uns in der Regel überhaupt nicht mit Fragen der effizienten Speicherfreigabe. Aber bei länger laufenden Skripten sind sie entscheidend.
Das Objekt WeakMap
ähnelt SplObjectStorage
. In
beiden werden Objekte als Schlüssel verwendet und ermöglichen die Speicherung
beliebiger Werte unter ihnen. Der Unterschied besteht darin, dass
WeakMap
nicht verhindert, dass das Objekt vom Garbage Collector
freigegeben wird. D.h., wenn der einzige Ort, an dem das Objekt noch vorkommt,
der Schlüssel in der Weak Map ist, wird es aus der Map und dem Speicher
entfernt.
$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 'Daten für $obj';
dump(count($map)); // 1
unset($obj);
dump(count($map)); // 0
Wozu ist das gut? Zum Beispiel zum Caching. Nehmen wir an, wir haben eine
Methode loadComments()
, der wir einen Blogartikel übergeben und
die alle seine Kommentare zurückgibt. Da die Methode wiederholt mit demselben
Artikel aufgerufen wird, erstellen wir uns noch getComments()
, die
das Ergebnis der ersten Methode zwischenspeichert:
class Comments
{
private WeakMap $cache;
public function __construct()
{
$this->cache = new WeakMap;
}
public function getComments(Article $article): ?array
{
$this->cache[$article] ??= $this->loadComments($article);
return $this->cache[$article]
}
...
}
Der Clou ist, dass in dem Moment, in dem das Objekt $article
freigegeben wird (z.B. weil die Anwendung beginnt, mit einem anderen Artikel zu
arbeiten), auch sein Eintrag aus dem Cache freigegeben wird.
PHP JIT (Just-in-Time-Compiler)
Vielleicht wissen Sie, dass PHP in sogenannten Opcode kompiliert wird, das sind Low-Level-Instruktionen, die Sie zum Beispiel hier ansehen können und die von der virtuellen Maschine von PHP ausgeführt werden. Und was ist JIT? JIT kann PHP transparent direkt in Maschinencode kompilieren, der direkt vom Prozessor ausgeführt wird, sodass die langsamere Ausführung durch die virtuelle Maschine umgangen wird.
JIT soll also PHP beschleunigen.
Die Bemühungen, JIT in PHP zu implementieren, reichen bis ins Jahr 2011 zurück und gehen auf Dmitry Stogov zurück. Seitdem hat er 3 verschiedene Implementierungen ausprobiert, aber keine davon schaffte es in das produktive PHP, und zwar aus folgenden Gründen: Das Ergebnis war nie eine wesentliche Leistungssteigerung für typische Webanwendungen; es erschwert die Wartung von PHP (d.h. niemand außer Dmitry versteht es 😉); es gab andere Wege, die Leistung zu verbessern, ohne JIT verwenden zu müssen.
Die sprunghafte Leistungssteigerung von PHP in Version 7 war ein Nebenprodukt der Arbeit an JIT, obwohl es paradoxerweise nicht eingesetzt wurde. Dies geschieht erst in PHP 8. Aber ich werde gleich überzogene Erwartungen bremsen: Wahrscheinlich werden Sie keine Beschleunigung feststellen.
Warum kommt JIT also in PHP? Einerseits gehen andere Wege zur Leistungsverbesserung langsam zur Neige, und JIT ist einfach an der Reihe. In gängigen Webanwendungen bringt es zwar keine Beschleunigung, aber es beschleunigt beispielsweise mathematische Berechnungen erheblich. Damit eröffnet sich die Möglichkeit, diese Dinge in PHP zu schreiben. Und tatsächlich wäre es so möglich, Funktionen direkt in PHP zu implementieren, die bisher aus Geschwindigkeitsgründen eine Implementierung direkt in C erforderten.
JIT ist Teil der Erweiterung opcache
und wird zusammen mit ihr
in der php.ini aktiviert (lesen Sie die Dokumentation
zu diesem vierstelligen Code):
zend_extension=php_opcache.dll
opcache.jit=1205 ; Konfiguration mittels OTRC-Vierer
opcache.enable_cli=1 ; damit es auch in der CLI funktioniert
opcache.jit_buffer_size=128M ; reservierter Speicher für kompilierten Code
Dass JIT läuft, erfahren Sie beispielsweise im Informationspanel in der Tracy Bar.
JIT funktioniert sehr gut, wenn alle Variablen klar definierte Typen haben
und sich bei wiederholtem Aufruf desselben Codes nicht ändern können. Ich bin
daher gespannt, ob wir eines Tages in PHP auch Typen für Variablen deklarieren
werden: string $s = 'Hallo, das ist der Abschluss der Serie';
Um einen Kommentar abzugeben, loggen Sie sich bitte ein