PHP 8.0: Nové funkce, třídy a JIT (4/4)
Vyšlo PHP verze 8.0. Je tak nadupané novinkami, jako nebyla žádná verze předtím. Jejich představení si vyžádalo rovnou čtyři samostatné články. V tomto posledním se podíváme na nové funkce a třídy a představíme si Just in Time Compiler.
Nové funkce
Standardní knihovna PHP disponuje stovkami funkcí a ve verzi 8.0 se objevilo šest nových. Vypadá to jako málo, ale většina z nich zaceluje slabá místa jazyka. Což hezky koresponduje z vyzněním celé verze 8.0, která dotahuje a konsoliduje PHP jako žádná verze předtím. Přehled všech nových funkcí i metod najdete v migration guide.
str_contains()
str_starts_with()
str_ends_with()
Funkce pro zjištění, zda řetězec začíná, končí nebo v sobě obsahuje podřetězec.
if (str_contains('Nette', 'te')) {
...
}
Společně s příchodem této trojice PHP definuje, jak zacházet s prázdným řetězcem při vyhledávání, podle čehož se řídí i všechny ostatní související funkce, a to tak, že prázdný řetězec se nachází všude:
str_contains('Nette', '') // true
str_starts_with('Nette', '') // true
strpos('Nette', '') // 0 (dříve false)
Díky tomu je chování trojice zcela identické k obdobám v Nette:
str_contains() # Nette\Utils\String::contains()
str_starts_with() # Nette\Utils\String::startsWith()
str_ends_with() # Nette\Utils\String::endsWith()
Proč jsou tyhle funkce tak důležité? Standardní knihovny všech jazyků
jsou vždy zatěžkány historickým vývojem a nelze se ubránit vzniku
nekonzistencí a přešlapů. Ale zároveň jde o vizitku toho kterého jazyka.
Je s podivem, když u 25 let starého PHP chybí funkce pro tak základní
operace, jako je vracení prvního či posledního prvku z pole, escapování
HTML bez podrazů (htmlspecialchars
neescapuje apostrof), nebo
právě vyhledávání řetězce v řetězci. Že to jde nějak obejít
neobstojí, protože výsledkem pak není čitelný a srozumitelný kód. Je to
ponaučení pro všechny autory API. Když vidíte, že značnou část
dokumentace funkce zabírá vysvětlení záludností (jako třeba návratové
hodnoty strpos
), je to jasný signál knihovnu upravit a doplnit
právě str_contains
.
get_debug_type()
Nahrazuje již zastaralé get_type()
. Místo dlouhých typů
jako integer
vrací dnes užívané int
, v případě
objektů vrací rovnou 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) |
Objektivizace zdrojů
Hodnoty typu resource pocházejí z dob, kdy ještě PHP nemělo objekty, ale vlastně je potřebovalo. Tak přišly na svět resources. Dnes objekty máme a oproti resources fungují daleko lépe s garbage kolektorem, takže v plánu je postupně všechny nahradit za objekty.
Od PHP 8.0 se mění na objekty resources obrázků, curl spojení, openssl, xml, apod.. V PHP 8.1 přijdou na řadu FTP spojení atd.
$res = imagecreatefromjpeg('image.jpg');
$res instanceof GdImage // true
is_resource($res) // false - BC break
Tyto objekty nemají zatím žádné metody, ani nemůžete přímo
vytvářet jejich instance. Jde zatím skutečně jen o to dostat z PHP
zastaralé resources beze změny API. A to je dobře, protože vytvoření
dobrého API je samostatný a náročný úkol. Nikdo si nepřeje, aby v PHP
vznikly další třídy jako SplFileObject s metodami pojmenovanými
fgetc()
nebo fgets()
.
PhpToken
Do objektů se přesouvá i tokenizér a tedy funkce kolem
token_get_all
. Tentokrát nejde o zbavování se resources, ale
dostáváme plnohodnotný objekt představující jeden PHP token.
<?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
Metoda isIgnorable()
vrací true pro tokeny
T_WHITESPACE
, T_COMMENT
, T_DOC_COMMENT
a
T_OPEN_TAG
.
Weak maps
Weak mapy souvisí s gargabe kolektorem, který z paměti uvolňuje všechny objekty a hodnoty, které se už nepoužívají (tj. není žádná používaná proměnná, či property, která by je obsahovala). Jelikož život PHP vlákna je jepičí a paměti máme dnes na serverech k dispozici dost, otázky kolem efektivního uvolňování paměti zpravidla vůbec neřešíme. Ale u dlouhodoběji běžících skriptů jsou zásadní.
Objekt WeakMap
je podobný SplObjectStorage
V obou
se jako klíče používají objekty a umožňují pod nimi ukládání
libovolných hodnot. Rozdíl je v tom, že WeakMap
nezabrání
tomu, aby byl objekt uvolněn garbage kolektorem. Tj. pokud jediným místem,
kde se ještě objekt vyskytuje, je klíč ve weak mapě, bude z mapy
i paměti odstraněn.
$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 'data for $obj';
dump(count($map)); // 1
unset($obj);
dump(count($map)); // 0
K čemu je to dobré? Třeba pro kešování. Mějme metodu
loadComments()
, které předáme článek na blogu a ona vrátí
všechny jeho komentáře. Protože se metoda volá se stejným článkem
opakovaně, vytvoříme si ještě getComments()
, která bude
výsledek první metody kešovat:
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]
}
...
}
Vtip je v tom, že ve chvíli, kdy se uvolní objekt $article
(třeba aplikace začne pracovat s jiným článkem), uvolní se i jeho
položka z cache.
PHP JIT (Just in Time Compiler)
Možná víte, že PHP se kompiluje do tzv. opcode, což jsou low-level instrukce, které si můžete prohlédnout třeba tady a které vykonává virtuální stroj PHP. A co je JIT? JIT dokáže transparentně kompilovat PHP přímo do strojového kódu, který vykonává přímo procesor, takže se obejde pomalejší vykonávání virtuálním strojem.
JIT má tedy zrychlit PHP.
Snaha implementovat JIT do PHP sahá až do roku 2011 a stojí za ní Dmitry Stogov. Od té doby vyzkoušel 3 různé implementace, ale žádná z nich se nedostala do ostrého PHP a to z těchto důvodů: výsledkem nikdy nebyl žádný podstatný nárůst výkonu pro typické webové aplikace; komplikuje údržbu PHP (tj. nikdo kromě Dmitryho tomu nerozumí 😉); existovaly jiné cesty, jak zlepšit výkon, aniž by se muselo používat JIT.
Skokové zvýšení výkonu PHP ve verzi 7 bylo vedlejším produktem práce na JIT, byť k jeho nasazení paradoxně nedošlo. K tomu dochází až v PHP 8. Ale rovnou budu brzdit přehnaná očekávání: pravděpodobně žádné zrychlení nezaznamenáte.
Proč teda vstupuje JIT do PHP? Jednak jiné cesty pro zlepšení výkonu pomalu docházejí a JIT je prostě na řadě. V běžných webových aplikacích sice zrychlení nepřináší, ale zásadně zrychluje třeba matematické výpočty. Otevírá se tak možnost začít tyhle věci v PHP psát. A vlastně by tak bylo možné přímo v PHP implementovat funkce, které dosud vyžadovaly implementaci přímo v C kvůli rychlosti.
JIT je součástí rozšíření opcache
a zapíná se
společně s ním v php.ini (přečtěte si dokumentaci
k oné čtveřici číslic):
zend_extension=php_opcache.dll
opcache.jit=1205 ; konfigurace pomocí čtveřice OTRC
opcache.enable_cli=1 ; aby fungovalo i v CLI
opcache.jit_buffer_size=128M ; vyhrazená paměť pro zkompilovaný kód
Že JIT běží se dozvíte například v informačním panelu v Tracy Baru.
JIT velmi dobře funguje tehdy, pokud všechny proměnné mají jasně dané
typy a nemohou se měnit při opakovaném volání stejného kódu. Jsem proto
zvědav, jestli jednou budeme v PHP deklarovat typy také
u proměnných: string $s = 'Ahoj, tohle je závěr seriálu';
Komentáře
Díky
Akorát bych ještě doplnil, že pro získání prvního/posledního prvku pole se v PHP používají funkce end()/reset() a pokud jsi myslel klíče tak array_key_first()/array_key_last()
#2 Polki To je právě jen vedlejší efekt těch funkcí, proto mají i tak nevhodné pojmenování a fungují jen s proměnnou, protože parametr je referencí. V PHP chybí
array_first()/array_last()
.To strpos se mi zdá nějaké divné..
PHP 7.4.13
var_dump(strpos(‚Nette je super‘, ‚Nette‘));
int(0)
var_dump(strpos(‚Allahváč Nabhar‘, ‚Nette‘));
bool(false)
Takže PHP 8 mi v druhém případě vrátí nulu? Takže mi vlastně řekne, že ten hledaný string, který se v textu nenachází začíná na prvním znaku? Toto by byl tedy brutální BC break, nějak mi uniká nakýkoli důvot to, udělat. Částečně bych pochopil null, ale i tak vzhledem k častéu využití jako náhrady za str_contains stylem if(strpos(‚x‘, ‚y‘) !== false)… to musí rozbít obrovskou spoustu věcí..
Aha, tak jsem to zkusil s php 8.0.0 a strpos vrátí false.
To mi nedošlo, že ten int to tam dá jen při hledání prázdného stringu..
Tohle taky stojí za zmínku, to mi mnohokrát chybělo:
Sorting functions are now stable, which means that equal-comparing elements will retain their original order.
Bylo by možné přidat odkazy na předchozí články a zároveň doplnit v předcházejících odkaz na tento 4. díl?
Chcete-li odeslat komentář, přihlaste se