PHP 8.0: Új funkciók, osztályok és JIT (4/4)
Megjelent a PHP 8.0 verziója. Tele van olyan új funkciókkal, mint egyetlen korábbi verzió sem. Bemutatásuk négy külön cikket érdemelt ki. Az utolsó részben az új függvényeket és osztályokat vesszük szemügyre, valamint bemutatjuk a Just in Time Compilert.
Új funkciók
A szabványos PHP könyvtár több száz függvényt tartalmaz, és a 8.0-ás verzióban hat új függvény jelent meg. Ez nem tűnik soknak, de a legtöbbjük a nyelv gyenge pontjait orvosolja. Ami szépen illeszkedik az egész 8.0-s verzió koncepciójához, amely úgy szigorítja és egységesíti a PHP-t, mint egyetlen korábbi verzió sem. Az összes új függvény és metódus áttekintése megtalálható a migrációs útmutatóban.
str_contains()
str_starts_with()
str_ends_with()
Funkciók annak meghatározására, hogy egy karakterlánc kezdődik-e, végződik-e, vagy tartalmaz-e részláncot.
if (str_contains('Nette', 'te')) {
...
}
Ennek a hármasságnak a megjelenésével a PHP meghatározza, hogyan kezelje az üres karakterláncot keresés közben, amihez az összes többi kapcsolódó függvény is ragaszkodik, vagyis az üres karakterlánc mindenhol megtalálható:
str_contains('Nette', '') // true
str_starts_with('Nette', '') // true
strpos('Nette', '') // 0 (previously false)
Ennek köszönhetően a hármasság viselkedése teljesen megegyezik a Nette analógokkal:
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 funkciók? Minden nyelv szabványkönyvtárát
mindig terheli a történelmi fejlődés; a következetlenségeket és a
tévedéseket nem lehet elkerülni. De ugyanakkor ez az adott nyelv
tanúbizonysága is. Meglepő módon a 25 éves PHP-ből hiányoznak az olyan
alapvető műveletekhez szükséges függvények, mint például egy tömb első
vagy utolsó elemének visszaadása, a HTML kódok csúnya meglepetések
nélküli eszkábálása (htmlspecialchars
nem menekül az
aposztróf elől), vagy egyszerűen csak egy karakterlánc keresése egy
karakterláncban. Nem állja meg a helyét, hogy ezt valahogyan meg lehet
kerülni, mert az eredmény nem olvasható és érthető kód. Ez egy lecke az
összes API szerzőnek. Ha azt látod, hogy a függvény dokumentációjának
nagy részét a buktatók magyarázatai foglalják el (mint például a
strpos
visszatérési értékei), az egyértelmű jel arra, hogy
módosítsuk a könyvtárat, és adjuk hozzá a
str_contains
címet.
get_debug_type()
A mára elavult get_type()
helyébe lép. A hosszú típusok
helyett, mint a integer
, a ma használt int
,
objektumok esetén közvetlenül a típust adja vissza:
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) |
Erőforrás-objektum migráció
Az erőforrás-típusú értékek még abból az időből származnak, amikor a PHP még nem rendelkezett objektumokkal, de valójában szükség volt rájuk. Így születtek meg az erőforrások. Ma már vannak objektumok, és az erőforrásokhoz képest sokkal jobban működnek a szemétgyűjtővel, ezért a terv az, hogy fokozatosan az összeset objektumokra cseréljük.
A PHP 8.0-tól a képek, curl joins, openssl, xml, stb. erőforrások objektumokra változnak. A PHP 8.1-ben az FTP-kapcsolatok stb. is követni fogják.
$res = imagecreatefromjpeg('image.jpg');
$res instanceof GdImage // true
is_resource($res) // false - BC break
Ezeknek az objektumoknak még nincsenek metódusaik, és nem is lehet őket
közvetlenül példányosítani. Egyelőre tényleg csak arról van szó, hogy
megszabadulunk a PHP elavult erőforrásaitól anélkül, hogy az API-t
megváltoztatnánk. És ez jó, mert egy jó API létrehozása egy különálló
és nagy kihívást jelentő feladat. Senki sem kívánja új PHP-osztályok
létrehozását, mint például a SplFileObject a fgetc()
vagy a
fgets()
nevű metódusokkal.
PhpToken
A tokenizáló és a token_get_all
körüli függvények
szintén objektumokba kerültek át. Ezúttal nem arról van szó, hogy
megszabadulunk az erőforrásoktól, hanem egy teljes értékű objektumot
kapunk, amely egy PHP tokent reprezentál.
<?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
A isIgnorable()
metódus igazat ad vissza a
T_WHITESPACE
, T_COMMENT
, T_DOC_COMMENT
és T_OPEN_TAG
tokenekre.
Gyenge térképek
A gyenge leképezések a szemétgyűjtőhöz kapcsolódnak, amely minden olyan objektumot és értéket felszabadít a memóriából, amelyet már nem használnak (azaz nincs olyan változó vagy tulajdonság, amely tartalmazza őket). Mivel a PHP szálak rövid életűek, és a szervereinken rengeteg memória áll rendelkezésre, általában egyáltalán nem foglalkozunk a hatékony memóriafelszabadítással kapcsolatos kérdésekkel. A hosszabb futású szkriptek esetében azonban elengedhetetlenek.
A WeakMap
objektum hasonló a SplObjectStorage
objektumhoz Mindkettő objektumokat használ kulcsként, és lehetővé teszi
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 szemétgyűjtő
felszabadítsa. Vagyis ha az egyetlen hely, ahol az objektum jelenleg létezik,
egy kulcs a gyenge térképben, akkor eltávolítja a térképből és a
memóriából.
$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 'data for $obj';
dump(count($map)); // 1
unset($obj);
dump(count($map)); // 0
Mire jó ez? Például gyorsítótárazásra. Legyen egy
loadComments()
metódusunk, aminek átadunk egy blogcikket, és az
visszaadja az összes kommentjét. Mivel a metódust többször is meghívjuk
ugyanarra a cikkre, létrehozunk egy másik getComments()
, amely az
első metódus eredményét gyorsítótárazza:
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 lényeg az, hogy amikor a $article
objektumot felszabadítjuk
(például az alkalmazás egy másik cikkel kezd el dolgozni), annak bejegyzése
is kikerül a gyorsítótárból.
PHP JIT (Just in Time Compiler)
Talán tudod, hogy a PHP-t úgynevezett opcode-okba fordítják, amelyek olyan alacsony szintű utasítások, amelyeket például itt láthatsz, és amelyeket a PHP virtuális gép hajt végre. És mi az a JIT? A JIT transzparens módon képes a PHP-t közvetlenül gépi kóddá fordítani, amelyet közvetlenül a processzor hajt végre, így a virtuális gép általi lassabb végrehajtás megkerülhető.
A JIT célja tehát a PHP felgyorsítása.
A JIT PHP-be való implementálására irányuló törekvés 2011-re 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 a PHP végleges kiadásába, három okból: az eredmény soha nem jelentett jelentős teljesítménynövekedést tipikus webes alkalmazások esetében; bonyolítja a PHP karbantartását (azaz Dmitryn kívül senki sem érti 😉 ); voltak más módszerek is a teljesítmény növelésére JIT használata nélkül.
A 7-es PHP-verzióban megfigyelt ugrásszerű teljesítménynövekedés a JIT-tel kapcsolatos munka mellékterméke volt, bár paradox módon nem került bevetésre. Ez csak most történik meg a PHP 8-ban. De visszafogom a túlzott várakozásokat: valószínűleg nem fogsz sebességnövekedést látni.
Miért kerül be a JIT a PHP-ba? Először is, a teljesítmény javításának más módjai lassan kifogynak, és a JIT egyszerűen a következő lépés. A hétköznapi webes alkalmazásokban nem hoz sebességjavulást, de jelentősen felgyorsítja például a matematikai számításokat. Ez megnyitja a lehetőséget, hogy ezeket a dolgokat PHP-ben kezdjük el írni. Valójában lehetséges lenne néhány olyan függvényt közvetlenül PHP-ben megvalósítani, amelyekhez korábban a sebesség miatt közvetlen C implementációra volt szükség.
A JIT a opcache
bővítmény része, és azzal együtt
engedélyezhető a php.ini-ben (olvassuk el a dokumentációt
arról a négy számjegyről):
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
A JIT futását például a Tracy Bar információs panelen ellenőrizheted.
A JIT nagyon jól működik, ha minden változónak egyértelműen
meghatározott típusa van, és nem változhat még akkor sem, ha ugyanazt a
kódot ismételten meghívja. Ezért elgondolkodom azon, hogy vajon egyszer majd
a PHP-ben is típusokat fogunk-e deklarálni a
változókhoz: string $s = 'Bye, this is the end of the series';
A hozzászólás elküldéséhez kérjük, jelentkezzen be