PHP 8.0: Neue Funktionen, Klassen und JIT (4/4)
PHP Version 8.0 ist veröffentlicht worden. Sie ist voll von neuen Funktionen wie keine andere Version zuvor. Ihre Einführung hat vier separate Artikel verdient. Im letzten Teil werfen wir einen Blick auf neue Funktionen und Klassen und stellen den Just in Time Compiler vor.
Neue Funktionen
Die PHP-Standardbibliothek hat Hunderte von Funktionen und in Version 8.0 sind sechs neue hinzugekommen. Das scheint nicht viel zu sein, aber die meisten von ihnen beheben Schwachstellen der Sprache. Das passt gut zum Gesamtkonzept der Version 8.0, die PHP wie keine Version zuvor strafft und konsolidiert. Eine Übersicht über alle neuen Funktionen und Methoden finden Sie im Migration Guide.
str_contains()
str_starts_with()
str_ends_with()
Funktionen zur Bestimmung, ob eine Zeichenkette beginnt, endet oder eine Teilzeichenkette enthält.
if (str_contains('Nette', 'te')) {
...
}
Mit der Einführung dieser Dreifaltigkeit definiert PHP, wie eine leere Zeichenkette bei der Suche zu behandeln ist, woran sich alle anderen verwandten Funktionen halten, und zwar eine leere Zeichenkette wird überall gefunden:
str_contains('Nette', '') // true
str_starts_with('Nette', '') // true
strpos('Nette', '') // 0 (previously false)
Dadurch ist das Verhalten der Trinität völlig identisch mit dem der Nette-Analoga:
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? Standardbibliotheken aller Sprachen
sind immer durch die historische Entwicklung belastet; Ungereimtheiten und
Fehltritte lassen sich nicht vermeiden. Gleichzeitig sind sie aber auch ein
Zeugnis der jeweiligen Sprache. Überraschenderweise fehlen dem 25 Jahre alten
PHP Funktionen für so grundlegende Operationen wie die Rückgabe des ersten
oder letzten Elements eines Arrays, das Escapen von HTML ohne böse
Überraschungen (htmlspecialchars
escaped kein Apostroph) oder
einfach nur die Suche nach einem String in einem String. Es gilt nicht, dass es
irgendwie umgangen werden kann, weil das Ergebnis kein lesbarer und
verständlicher Code ist. Dies ist eine Lektion für alle API-Autoren. Wenn Sie
sehen, dass ein großer Teil der Funktionsdokumentation von Erklärungen zu
Fallstricken (wie den Rückgabewerten von strpos
) eingenommen wird,
ist das ein klares Zeichen, die Bibliothek zu ändern und
str_contains
hinzuzufügen.
get_debug_type()
Ersetzt das inzwischen veraltete get_type()
. Anstelle von
Langtypen wie integer
gibt er den heute verwendeten
int
zurück, bei Objekten direkt den Typ:
Value | 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) |
Migration von Ressourcen zu Objekten
Die Werte des Ressourcentyps stammen aus einer Zeit, in der PHP noch keine Objekte hatte, diese aber tatsächlich benötigte. So wurden die Ressourcen geboren. Heute haben wir Objekte, und im Vergleich zu Ressourcen arbeiten sie viel besser mit dem Garbage Collector zusammen, daher ist der Plan, sie nach und nach alle durch Objekte zu ersetzen.
Ab PHP 8.0 werden die Ressourcen images, curl joins, openssl, xml, etc. durch Objekte ersetzt. In PHP 8.1 werden FTP-Verbindungen, etc. folgen.
$res = imagecreatefromjpeg('image.jpg');
$res instanceof GdImage // true
is_resource($res) // false - BC break
Diese Objekte haben noch keine Methoden, und man kann sie auch nicht direkt
instanziieren. Bis jetzt geht es wirklich nur darum, veraltete Ressourcen aus
PHP zu entfernen, ohne die API zu ändern. Und das ist gut so, denn die
Entwicklung einer guten API ist eine eigene und anspruchsvolle Aufgabe. Niemand
wünscht sich die Schaffung neuer PHP-Klassen wie SplFileObject mit Methoden
namens fgetc()
oder fgets()
.
PhpToken
Der Tokenizer und die Funktionen rund um token_get_all
werden
ebenfalls in Objekte umgewandelt. Diesmal geht es nicht darum, Ressourcen
loszuwerden, sondern wir erhalten ein vollwertiges Objekt, das ein PHP-Token
repräsentiert.
<?php
$tokens = PhpToken::tokenize('<?php $a = 10;');
$token = $tokens[0]; // instance 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()
liefert true für die Token
T_WHITESPACE
, T_COMMENT
, T_DOC_COMMENT
und T_OPEN_TAG
.
Schwache Karten
Weak Maps hängen mit dem Garbage Collector zusammen, der alle Objekte und Werte, die nicht mehr verwendet werden, aus dem Speicher freigibt (d.h. es gibt keine Variable oder Eigenschaft, die sie enthält). Da PHP-Threads nur kurzlebig sind und wir auf unseren Servern reichlich Speicher zur Verfügung haben, befassen wir uns in der Regel gar nicht mit Fragen der effektiven Speicherfreigabe. Aber für länger laufende Skripte sind sie unerlässlich.
Das Objekt WeakMap
ist ähnlich wie
SplObjectStorage
. Beide verwenden Objekte als Schlüssel und
erlauben es, beliebige Werte unter ihnen zu speichern. Der Unterschied ist, dass
WeakMap
nicht verhindert, dass das Objekt vom Garbage Collector
freigegeben wird. D.h. wenn der einzige Ort, an dem das Objekt derzeit
existiert, ein Schlüssel in der weak map ist, wird es aus der map und dem
Speicher entfernt.
$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 'data for $obj';
dump(count($map)); // 1
unset($obj);
dump(count($map)); // 0
Wozu ist das gut? Zum Beispiel für das Caching. Nehmen wir an, wir haben
eine Methode loadComments()
, der wir einen Blog-Artikel übergeben
und die alle seine Kommentare zurückgibt. Da die Methode wiederholt für
denselben Artikel aufgerufen wird, erstellen wir eine weitere
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]
}
...
}
Wenn das Objekt $article
freigegeben wird (z. B. wenn die
Anwendung mit einem anderen Artikel zu arbeiten beginnt), wird auch sein Eintrag
aus dem Cache freigegeben.
PHP JIT (Just in Time Compiler)
Sie wissen vielleicht, dass PHP in so genannten Opcode kompiliert wird, d.h. in Low-Level-Anweisungen, die Sie z. B. hier sehen können und die von einer virtuellen PHP-Maschine ausgeführt werden. Und was ist ein JIT? JIT kann PHP auf transparente Weise direkt in Maschinencode kompilieren, der direkt vom Prozessor ausgeführt wird, so dass die langsamere Ausführung durch die virtuelle Maschine umgangen wird.
JIT ist also dazu gedacht, PHP zu beschleunigen.
Die Bemühungen, JIT in PHP zu implementieren, gehen auf das Jahr 2011 zurück und werden von Dmitry Stogov unterstützt. Seitdem hat er drei verschiedene Implementierungen ausprobiert, aber keine davon hat es in eine endgültige PHP-Version geschafft, und zwar aus drei Gründen: Das Ergebnis war nie eine signifikante Leistungssteigerung für typische Webanwendungen; es erschwert die PHP-Wartung (d. h. niemand außer Dmitry versteht es 😉 ); es gab andere Möglichkeiten, die Leistung zu verbessern, ohne ein JIT verwenden zu müssen.
Der sprunghafte Anstieg der Leistung in PHP Version 7 war ein Nebenprodukt der Arbeit an JIT, obwohl es paradoxerweise nicht eingesetzt wurde. Dies geschieht erst jetzt in PHP 8. Aber ich werde mich mit übertriebenen Erwartungen zurückhalten: Sie werden wahrscheinlich keinen Geschwindigkeitszuwachs sehen.
Warum also hält JIT Einzug in PHP? Erstens gehen andere Möglichkeiten zur Leistungssteigerung langsam aus, und JIT ist einfach der nächste Schritt. Bei gewöhnlichen Webanwendungen bringt es keine Geschwindigkeitsverbesserungen, aber es beschleunigt z. B. mathematische Berechnungen erheblich. Damit eröffnet sich die Möglichkeit, diese Dinge in PHP zu schreiben. In der Tat wäre es möglich, einige Funktionen direkt in PHP zu implementieren, die bisher aus Geschwindigkeitsgründen eine direkte C-Implementierung erforderten.
JIT ist Teil der opcache
Erweiterung und wird zusammen mit
dieser in der php.ini aktiviert (lesen Sie die Dokumentation
über diese vier Ziffern):
zend_extension=php_opcache.dll
opcache.jit=1205 ; configuration using four digits OTRC
opcache.enable_cli=1 ; in order to work in the CLI as well
opcache.jit_buffer_size=128M ; dedicated memory for compiled code
Sie können überprüfen, ob JIT läuft, z. B. im Informationsfenster der Tracy Bar.
JIT funktioniert sehr gut, wenn alle Variablen klar definierte Typen haben
und sich auch beim wiederholten Aufruf desselben Codes nicht ändern können.
Ich frage mich daher, ob wir eines Tages auch in PHP Typen für Variablen
deklarieren
werden: string $s = 'Bye, this is the end of the series';
Um einen Kommentar abzugeben, loggen Sie sich bitte ein