PHP 8.0: Noi funcții, clase și JIT (4/4)
A fost lansată versiunea 8.0 a PHP. Este plină de funcții noi, cum nu a mai fost nicio altă versiune până acum. Prezentarea lor a meritat patru articole separate. În ultima parte vom arunca o privire asupra noilor funcții și clase și vom prezenta Compilatorul Just in Time.
Funcții noi
Biblioteca standard PHP are sute de funcții, iar în versiunea 8.0 au apărut șase funcții noi. Nu pare mult, dar cele mai multe dintre ele remediază punctele slabe ale limbajului. Ceea ce se aliniază foarte bine cu întregul concept al versiunii 8.0, care strânge și consolidează PHP ca nicio altă versiune anterioară. O prezentare generală a tuturor noilor funcții și metode poate fi găsită în ghidul de migrare.
str_contains()
str_starts_with()
str_ends_with()
Funcții pentru a determina dacă un șir de caractere începe, se termină sau conține o subșir.
if (str_contains('Nette', 'te')) {
...
}
Odată cu apariția acestei trinități, PHP definește modul de tratare a unui șir gol în timpul căutării, la care aderă toate celelalte funcții aferente, și anume un șir gol se găsește peste tot:
str_contains('Nette', '') // true
str_starts_with('Nette', '') // true
strpos('Nette', '') // 0 (previously false)
Datorită acestui fapt, comportamentul trinității este complet identic cu cel al analogilor Nette:
str_contains() # Nette\Utils\String::contains()
str_starts_with() # Nette\Utils\String::startsWith()
str_ends_with() # Nette\Utils\String::endsWith()
De ce sunt aceste funcții atât de importante? Bibliotecile standard ale
tuturor limbajelor sunt întotdeauna împovărate de dezvoltarea istorică;
inconsecvențele și pașii greșiți nu pot fi evitați. Dar, în același
timp, este o mărturie a limbajului respectiv. În mod surprinzător, PHP,
vechi de 25 de ani, nu dispune de funcții pentru operații de bază precum
returnarea primului sau ultimului element al unei matrice, scăparea HTML fără
surprize neplăcute (htmlspecialchars
nu scapă de un apostrof) sau
pur și simplu căutarea unui șir de caractere într-un șir de caractere. Nu
se susține că poate fi cumva ocolit, pentru că rezultatul nu este un
cod lizibil și inteligibil. Aceasta este o lecție pentru toți autorii de
API-uri. Când vedeți că o mare parte din documentația funcției este
ocupată de explicații ale capcanelor (cum ar fi valorile de returnare ale
strpos
), este un semn clar de a modifica biblioteca și de a
adăuga str_contains
.
get_debug_type()
Înlocuiește acum învechitul get_type()
. În locul tipurilor
lungi, cum ar fi integer
, returnează tipul utilizat în prezent
int
, în cazul obiectelor returnează direct tipul:
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) |
Migrarea de la resurse la obiecte
Valorile de tip resursă provin dintr-o perioadă în care PHP nu avea încă obiecte, dar avea nevoie de ele. Așa s-au născut resursele. Astăzi avem obiecte și, în comparație cu resursele, acestea funcționează mult mai bine cu colectorul de gunoi, așa că planul este de a le înlocui treptat pe toate cu obiecte.
Începând cu PHP 8.0, resursele images, curl joins, openssl, xml, etc. sunt schimbate în obiecte. În PHP 8.1, vor urma conexiunile FTP etc.
$res = imagecreatefromjpeg('image.jpg');
$res instanceof GdImage // true
is_resource($res) // false - BC break
Aceste obiecte nu au încă nicio metodă și nici nu le puteți instanția
direct. Până acum, este vorba doar de a scăpa de resursele învechite din PHP
fără a modifica API-ul. Și asta este bine, pentru că crearea unui API bun
este o sarcină separată și dificilă. Nimeni nu dorește crearea unor noi
clase PHP, cum ar fi SplFileObject, cu metode numite fgetc()
sau
fgets()
.
PhpToken
Tokenizatorul și funcțiile din jurul token_get_all
sunt, de
asemenea, migrate în obiecte. De data aceasta nu este vorba de a scăpa de
resurse, ci obținem un obiect cu drepturi depline care reprezintă un
token PHP.
<?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()
returnează true pentru token-urile
T_WHITESPACE
, T_COMMENT
, T_DOC_COMMENT
,
și T_OPEN_TAG
.
Hărți slabe
Hărțile slabe sunt legate de colectorul de gunoi, care eliberează din memorie toate obiectele și valorile care nu mai sunt utilizate (adică nu există nicio variabilă sau proprietate care să le conțină). Deoarece firele de execuție PHP au o durată scurtă de viață și avem multă memorie disponibilă pe serverele noastre, de obicei nu abordăm deloc problemele referitoare la eliberarea efectivă a memoriei. Dar pentru scripturile cu o durată de execuție mai lungă, acestea sunt esențiale.
Obiectul WeakMap
este similar cu SplObjectStorage
Ambele utilizează obiecte ca chei și permit stocarea de valori arbitrare sub
ele. Diferența constă în faptul că WeakMap
nu împiedică
eliberarea obiectului de către garbage collector. De exemplu, dacă singurul
loc în care există în prezent obiectul este o cheie din harta slabă, acesta
va fi eliminat din hartă și din memorie.
$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 'data for $obj';
dump(count($map)); // 1
unset($obj);
dump(count($map)); // 0
La ce este bun? De exemplu, pentru caching. Să avem o metodă
loadComments()
căreia îi transmitem un articol de blog și care
returnează toate comentariile sale. Deoarece metoda este apelată în mod
repetat pentru același articol, vom crea o altă metodă
getComments()
, care va pune în cache rezultatul primei metode:
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]
}
...
}
Ideea este că atunci când obiectul $article
este eliberat (de
exemplu, aplicația începe să lucreze cu un alt articol), intrarea acestuia
este, de asemenea, eliberată din memoria cache.
PHP JIT (Just in Time Compiler)
Poate știți că PHP este compilat în așa-numitele opcode, care sunt instrucțiuni de nivel scăzut pe care le puteți vedea aici, de exemplu și care sunt executate de o mașină virtuală PHP. Și ce este un JIT? JIT poate compila în mod transparent PHP direct în cod mașină, care este executat direct de către procesor, astfel încât execuția mai lentă de către mașina virtuală este ocolită.
Prin urmare, JIT este menit să accelereze PHP.
Efortul de a implementa JIT în PHP datează din 2011 și este susținut de Dmitri Stogov. De atunci, el a încercat 3 implementări diferite, dar niciuna dintre ele nu a ajuns într-o versiune finală a PHP din trei motive: rezultatul nu a fost niciodată o creștere semnificativă a performanței pentru aplicațiile web tipice; complică întreținerea PHP (adică nimeni în afară de Dmitry nu o înțelege 😉 ); existau alte modalități de a îmbunătăți performanța fără a fi nevoie să se folosească un JIT.
Saltul de creștere a performanței observat în versiunea 7 a PHP a fost un produs secundar al activității privind JIT, deși, în mod paradoxal, nu a fost implementat. Acest lucru se întâmplă abia acum, în PHP 8. Dar îmi voi reține așteptările exagerate: probabil că nu veți vedea nicio creștere de viteză.
Așadar, de ce intră JIT în PHP? În primul rând, alte modalități de îmbunătățire a performanței se epuizează încet, iar JIT este pur și simplu următorul pas. În aplicațiile web obișnuite, nu aduce îmbunătățiri ale vitezei, dar accelerează semnificativ, de exemplu, calculele matematice. Acest lucru deschide posibilitatea de a începe să scrie aceste lucruri în PHP. De fapt, ar fi posibil să se implementeze direct în PHP unele funcții care anterior necesitau o implementare directă în C din cauza vitezei.
JIT face parte din extensia opcache
și este activată
împreună cu aceasta în php.ini (citiți documentația
despre aceste patru cifre):
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
Puteți verifica dacă JIT rulează, de exemplu, în panoul de informații Tracy Bar.
JIT funcționează foarte bine dacă toate variabilele au tipuri clar
definite și nu se pot schimba nici măcar atunci când se apelează același
cod în mod repetat. Prin urmare, mă întreb dacă nu cumva într-o bună zi
vom declara tipuri în PHP și pentru
variabile: string $s = 'Bye, this is the end of the series';
Pentru a trimite un comentariu, vă rugăm să vă conectați