PHP 8.0: Nove funkcije, razredi in JIT (4/4)
Izšla je različica PHP 8.0. Tako je polna novosti, kot še nobena različica pred njo. Njihova predstavitev je zahtevala kar štiri ločene članke. V tem zadnjem si bomo pogledali nove funkcije in razrede ter predstavili Just in Time Compiler.

Nove funkcije
Standardna knjižnica PHP ima na stotine funkcij in v različici 8.0 se je pojavilo šest novih. Videti je kot malo, vendar večina od njih zapolnjuje šibke točke jezika. Kar se lepo ujema z vzdušjem celotne različice 8.0, ki dokončuje in konsolidira PHP kot nobena različica pred njo. Pregled vseh novih funkcij in metod najdete v migration guide.
str_contains()
str_starts_with()
str_ends_with()
Funkcije za ugotavljanje, ali se niz začne, konča ali vsebuje podniz.
if (str_contains('Nette', 'te')) {
...
}
Skupaj s prihodom te trojice PHP definira, kako ravnati s praznim nizom pri iskanju, po čemer se ravnajo tudi vse ostale povezane funkcije, in sicer tako, da se prazen niz nahaja povsod:
str_contains('Nette', '') // true
str_starts_with('Nette', '') // true
strpos('Nette', '') // 0 (prej false)
Zahvaljujoč temu je obnašanje trojice popolnoma identično analogijam v Nette:
str_contains() # Nette\Utils\String::contains()
str_starts_with() # Nette\Utils\String::startsWith()
str_ends_with() # Nette\Utils\String::endsWith()
Zakaj so te funkcije tako pomembne? Standardne knjižnice vseh jezikov so
vedno obremenjene z zgodovinskim razvojem in ni se mogoče izogniti nastanku
nekonsistentnosti in napak. Ampak hkrati gre za vizitko dotičnega jezika.
Presenetljivo je, ko pri 25 let starem PHP manjkajo funkcije za tako osnovne
operacije, kot je vračanje prvega ali zadnjega elementa iz polja, ubežanje
znakov v HTML brez prevar (htmlspecialchars
ne ubeži apostrofa),
ali prav iskanje niza v nizu. Da se to da nekako zaobiti, ne vzdrži,
ker rezultat potem ni čitljiva in razumljiva koda. To je nauk za vse avtorje
API-jev. Ko vidite, da znaten del dokumentacije funkcije zavzema razlaga zank
(kot na primer povratne vrednosti strpos
), je to jasen signal, da
knjižnico prilagodite in dopolnite prav z str_contains
.
get_debug_type()
Nadomešča že zastarelo get_type()
. Namesto dolgih tipov kot
integer
vrača danes uporabljane int
, v primeru
objektov vrača kar tip:
Vrednost | 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) |
Objektivizacija virov
Vrednosti tipa resource izvirajo iz časov, ko PHP še ni imel objektov, ampak jih je pravzaprav potreboval. Tako so prišli na svet resources. Danes imamo objekte in v primerjavi z resources delujejo veliko bolje z garbage collectorjem, zato je v načrtu postopoma vse nadomestiti z objekti.
Od PHP 8.0 se spreminjajo v objekte resources slik, povezav curl, openssl, xml itd.. V PHP 8.1 pridejo na vrsto FTP povezave itd.
$res = imagecreatefromjpeg('image.jpg');
$res instanceof GdImage // true
is_resource($res) // false - BC break
Ti objekti zaenkrat nimajo nobenih metod, niti ne morete neposredno
ustvarjati njihovih instanc. Gre zaenkrat resnično le za to, da se iz PHP
odstranijo zastareli resources brez spremembe API-ja. In to je dobro, ker je
ustvarjanje dobrega API-ja samostojna in zahtevna naloga. Nihče si ne želi, da
bi v PHP nastali dodatni razredi kot SplFileObject z metodami, poimenovanimi
fgetc()
ali fgets()
.
PhpToken
V objekte se seli tudi tokenizer in torej funkcije okoli
token_get_all
. Tokrat ne gre za znebljanje resources, ampak
dobivamo polnovreden objekt, ki predstavlja en PHP token.
<?php
$tokens = PhpToken::tokenize('<?php $a = 10;');
$token = $tokens[0]; // instanca 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()
vrne true za tokene
T_WHITESPACE
, T_COMMENT
, T_DOC_COMMENT
in
T_OPEN_TAG
.
Weak maps
Weak mape so povezane z gargabe kolektorjem, ki iz pomnilnika sprošča vse objekte in vrednosti, ki se ne uporabljajo več (tj. ni nobene uporabljene spremenljivke, či lastnosti, ki bi jih vsebovala). Ker je življenje PHP niti je kratkotrajno in pomnilnika imamo danes na strežnikih na voljo dovolj, vprašanja okoli učinkovitega sproščanja pomnilnika praviloma sploh ne rešujemo. Ampak pri dolgotrajnejših skriptih so bistvena.
Objekt WeakMap
je podoben SplObjectStorage
. V obeh
se kot ključi uporabljajo objekti in omogočajo pod njimi shranjevanje
poljubnih vrednosti. Razlika je v tem, da WeakMap
ne prepreči
temu, da bi bil objekt sproščen garbage kolektorjem. Tj. če edino mesto, kjer
se še objekt pojavlja, je ključ v weak mapi, bo iz mape in pomnilnika
odstranjen.
$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 'podatki za $obj';
dump(count($map)); // 1
unset($obj);
dump(count($map)); // 0
Za kaj je to dobro? Na primer za predpomnjenje. Imejmo metodo
loadComments()
, kateri posredujemo članek na blogu in ona vrne vse
njegove komentarje. Ker se metoda kliče z istim člankom ponavljajoče,
ustvarimo si še getComments()
, ki bo rezultat prve metode
predpomnila:
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]
}
...
}
Šala je v tem, da v trenutku, ko se sprosti objekt $article
(na primer aplikacija začne delati z drugim člankom), se sprosti tudi njegov
element iz predpomnilnika.
PHP JIT (Just in Time Compiler)
Morda veste, da se PHP prevaja v t.i. opcode, kar so nizkonivojska navodila, ki si jih lahko ogledate na primer tukaj in ki jih izvaja virtualni stroj PHP. In kaj je JIT? JIT lahko transparentno prevaja PHP neposredno v strojno kodo, ki jo izvaja neposredno procesor, tako da se obejde počasnejše izvajanje z virtualnim strojem.
JIT naj bi torej pospešil PHP.
Prizadevanja za implementacijo JIT v PHP segajo vse do leta 2011 in za njimi stoji Dmitry Stogov. Od takrat je preizkusil 3 različne implementacije, vendar nobena od njih ni prišla v ostro PHP in to iz teh razlogov: rezultatom nikoli ni bil noben bistven porast zmogljivosti za tipične spletne aplikacije; otežuje vzdrževanje PHP (tj. nihče razen Dmitryja tega ne razume 😉); obstajale so druge poti, kako izboljšati zmogljivost, ne da bi se moralo uporabljati JIT.
Skokovito povečanje zmogljivosti PHP v različici 7 je bilo stranskim produktom dela na JIT, čeprav do njegovega nasajenja paradoksalno ni prišlo. K temu prihaja šele v PHP 8. Ampak takoj bom zaviral pretirana pričakovanja: verjetno nobenega pospeška ne boste zaznali.
Zakaj torej JIT vstopa v PHP? Prvič, druge poti za izboljšanje zmogljivosti počasi zmanjkujejo in JIT je preprosto na vrsti. V običajnih spletnih aplikacijah sicer pospeška ne prinaša, ampak bistveno pospešuje na primer matematične izračune. Odpira se tako možnost, da se te stvari začnejo pisati v PHP. In pravzaprav bi bilo tako mogoče neposredno v PHP implementirati funkcije, ki so doslej zahtevale implementacijo neposredno v C zaradi hitrosti.
JIT je del razširitve opcache
in se vklopi skupaj z njo
v php.ini (preberite si dokumentacijo
k oné čtveřici číslic):
zend_extension=php_opcache.dll
opcache.jit=1205 ; konfiguracija s pomočjo četverice OTRC
opcache.enable_cli=1 ; da deluje tudi v CLI
opcache.jit_buffer_size=128M ; rezerviran pomnilnik za prevedeno kodo
Da JIT teče se boste izvedeli na primer v informacijski plošči v Tracy Baru.
JIT zelo dobro deluje takrat, ko vse spremenljivke imajo jasno dane tipe in
se ne morejo spreminjati pri ponovnem klicu iste kode. Sem zato radoveden, ali
bomo nekoč v PHP deklarirali tipe tudi pri
spremenljivkah: string $s = 'Zdravo, tole je zaključek serije';
Če želite oddati komentar, se prijavite