PHP 8.0: Új függvények, osztályok és JIT (4/4)
Megjelent a PHP 8.0-s verziója. Annyira tele van újdonságokkal, mint még egyetlen verzió sem korábban. Bemutatásukhoz egyenesen négy különálló cikkre volt szükség. Ebben az utolsóban az új függvényeket és osztályokat nézzük meg, és bemutatjuk a Just in Time Compilert.

Új függvények
A PHP standard könyvtára több száz függvényt tartalmaz, és a 8.0-s verzióban hat új jelent meg. Ez kevésnek tűnik, de többségük a nyelv gyenge pontjait pótolja. Ami szépen illeszkedik az egész 8.0-s verzió hangulatához, amely úgy tökéletesíti és konszolidálja a PHP-t, mint még egyetlen verzió sem korábban. Az összes új függvény és metódus áttekintését a migration guide-ban találja.
str_contains()
str_starts_with()
str_ends_with()
Függvények annak megállapítására, hogy egy string kezdődik-e, végződik-e, vagy tartalmaz-e egy részstringet.
if (str_contains('Nette', 'te')) {
...
}
Ezzel a hármassal együtt a PHP definiálja, hogyan kell kezelni az üres stringet a keresés során, amihez az összes többi kapcsolódó függvény is igazodik, mégpedig úgy, hogy az üres string mindenhol megtalálható:
str_contains('Nette', '') // true
str_starts_with('Nette', '') // true
strpos('Nette', '') // 0 (korábban false)
Ennek köszönhetően a hármas viselkedése teljesen azonos a Nette-beli megfelelőivel:
str_contains() # Nette\Utils\String::contains()
str_starts_with() # Nette\Utils\String::startsWith()
str_ends_with() # Nette\Utils\String::endsWith()
Miért olyan fontosak ezek a függvények? Minden nyelv standard könyvtára
mindig terhelt a történelmi fejlődéssel, és nem lehet elkerülni a
következetlenségek és melléfogások kialakulását. De ugyanakkor ez az
adott nyelv névjegye is. Meglepő, ha egy 25 éves PHP-ból hiányoznak a
függvények olyan alapvető műveletekhez, mint a tömb első vagy utolsó
elemének visszaadása, a HTML escapelése trükkök nélkül
(htmlspecialchars
nem escapeli az aposztrófot), vagy éppen a
string keresése stringben. Az, hogy valahogy meg lehet kerülni, nem
állja meg a helyét, mert az eredmény nem olvasható és érthető kód lesz.
Ez tanulság minden API szerző számára. Ha látja, hogy a függvény
dokumentációjának jelentős részét a buktatók magyarázata foglalja el
(mint például a strpos
visszatérési értékei), az egyértelmű
jelzés a könyvtár módosítására és éppen a str_contains
kiegészítésére.
get_debug_type()
Helyettesíti a már elavult get_type()
-ot. Hosszú típusok
helyett, mint az integer
, a ma használt int
-et adja
vissza, objektumok esetén pedig egyenesen a típust:
Érték | 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) |
Erőforrások objektumosítása
A resource típusú értékek abból az időből származnak, amikor a PHP-nak még nem voltak objektumai, de valójában szüksége volt rájuk. Így jöttek létre az erőforrások. Ma már vannak objektumaink, és az erőforrásokkal szemben sokkal jobban működnek a garbage collectorral, így a terv az, hogy fokozatosan mindet objektumokra cseréljük.
A PHP 8.0-tól kezdve objektumokká válnak a képek, curl kapcsolatok, openssl, xml stb. erőforrásai. A PHP 8.1-ben az FTP kapcsolatok stb. következnek.
$res = imagecreatefromjpeg('image.jpg');
$res instanceof GdImage // true
is_resource($res) // false - BC break
Ezeknek az objektumoknak még nincsenek metódusaik, és közvetlenül sem
hozhat létre példányokat belőlük. Egyelőre valóban csak arról van szó,
hogy megszabaduljunk a PHP elavult erőforrásaitól az API megváltoztatása
nélkül. És ez jó, mert egy jó API létrehozása önálló és nehéz
feladat. Senki sem szeretné, ha a PHP-ban további olyan osztályok jönnének
létre, mint az SplFileObject, fgetc()
vagy fgets()
nevű metódusokkal.
PhpToken
Objektumokba kerül át a tokenizáló is, tehát a
token_get_all
körüli függvények. Ezúttal nem az
erőforrásoktól való megszabadulásról van szó, hanem egy teljes értékű
objektumot kapunk, amely egy PHP tokent reprezentál.
<?php
$tokens = PhpToken::tokenize('<?php $a = 10;');
$token = $tokens[0]; // PhpToken példány
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
Az isIgnorable()
metódus true-t ad vissza a
T_WHITESPACE
, T_COMMENT
, T_DOC_COMMENT
és T_OPEN_TAG
tokenekre.
Weak map-ek
A weak map-ek a garbage collectorhoz kapcsolódnak, amely felszabadítja a memóriából az összes olyan objektumot és értéket, amelyeket már nem használnak (azaz nincs olyan használt változó vagy property, amely tartalmazná őket). Mivel a PHP szál élete kérészéletű, és ma már elegendő memória áll rendelkezésünkre a szervereken, a hatékony memóriafelszabadítás kérdéseivel általában egyáltalán nem foglalkozunk. De a hosszabb ideig futó szkripteknél alapvetőek.
Az WeakMap
objektum hasonló az
SplObjectStorage
-hoz. Mindkettőben objektumokat használnak
kulcsként, és lehetővé teszik tetszőleges értékek tárolását alattuk.
A különbség az, hogy a WeakMap
nem akadályozza meg, hogy az
objektumot a garbage collector felszabadítsa. Azaz, ha az egyetlen hely, ahol
az objektum még előfordul, a weak map kulcsa, akkor eltávolításra kerül a
map-ből és a memóriából is.
$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 'adatok $obj-hoz';
dump(count($map)); // 1
unset($obj);
dump(count($map)); // 0
Mire jó ez? Például cache-elésre. Legyen egy loadComments()
metódusunk, amelynek átadunk egy blogcikket, és visszaadja annak összes
kommentjét. Mivel a metódus ugyanazzal a cikkel ismételten meghívódik,
létrehozunk még egy getComments()
-t, amely az első metódus
eredményét cache-eli:
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]
}
...
}
A trükk az, hogy abban a pillanatban, amikor az $article
objektum felszabadul (például az alkalmazás elkezd egy másik cikkel
dolgozni), annak bejegyzése is felszabadul a cache-ből.
PHP JIT (Just in Time Compiler)
Talán tudja, hogy a PHP ún. opcode-ra fordítódik, ami low-level utasítások, amelyeket megnézhet például itt, és amelyeket a PHP virtuális gépe hajt végre. És mi a JIT? A JIT képes a PHP-t transzparensen közvetlenül gépi kódra fordítani, amelyet közvetlenül a processzor hajt végre, így megkerüli a virtuális gép lassabb végrehajtását.
A JIT tehát felgyorsítja a PHP-t.
A JIT PHP-ba való implementálására irányuló törekvés egészen 2011-ig nyúlik vissza, és Dmitry Stogov áll mögötte. Azóta 3 különböző implementációt próbált ki, de egyik sem került be az éles PHP-ba, és ennek okai a következők: az eredmény soha nem volt jelentős teljesítménynövekedés a tipikus webalkalmazásoknál; bonyolítja a PHP karbantartását (azaz senki sem érti Dmitry-n kívül 😉); voltak más utak a teljesítmény javítására anélkül, hogy JIT-et kellett volna használni.
A PHP teljesítményének ugrásszerű növekedése a 7-es verzióban a JIT-en végzett munka mellékterméke volt, bár paradox módon nem került bevezetésre. Ez csak a PHP 8-ban történik meg. De rögtön visszafogom a túlzott elvárásokat: valószínűleg semmilyen gyorsulást nem fog észrevenni.
Miért lép tehát be a JIT a PHP-ba? Egyrészt a teljesítmény javításának más útjai lassan elfogynak, és a JIT egyszerűen sorra kerül. A szokásos webalkalmazásokban ugyan nem hoz gyorsulást, de alapvetően felgyorsítja például a matematikai számításokat. Így lehetőség nyílik arra, hogy ezeket a dolgokat PHP-ban kezdjük el írni. És valójában így közvetlenül PHP-ban lehetne implementálni olyan függvényeket, amelyek eddig a sebesség miatt közvetlenül C-ben történő implementációt igényeltek.
A JIT az opcache
kiterjesztés része, és vele együtt
kapcsolható be a php.ini-ben (olvassa el a dokumentációt
ahhoz a négy számjegyhez):
zend_extension=php_opcache.dll
opcache.jit=1205 ; konfiguráció a négy OTRC számjeggyel
opcache.enable_cli=1 ; hogy CLI-ben is működjön
opcache.jit_buffer_size=128M ; lefoglalt memória a lefordított kódhoz
Hogy a JIT fut, azt például a Tracy Bar információs paneljén tudhatja meg.
A JIT nagyon jól működik akkor, ha minden változónak világosan
meghatározott típusa van, és nem változhatnak meg ugyanazon kód ismételt
hívásakor. Kíváncsi vagyok tehát, hogy egyszer a PHP-ban a változóknál
is deklarálunk-e majd
típusokat: string $s = 'Sziasztok, ez a sorozat vége';
A hozzászólás elküldéséhez kérjük, jelentkezzen be