PHP 8.0: nuove funzioni, classi e JIT (4/4)
È stata rilasciata la versione 8.0 di PHP. È ricca di nuove funzionalità come nessun'altra versione prima. La loro introduzione ha meritato quattro articoli separati. Nell'ultima parte daremo un'occhiata alle nuove funzioni e classi e introdurremo il compilatore Just in Time.
Nuove funzioni
La libreria standard di PHP conta centinaia di funzioni e nella versione 8.0 ne sono apparse sei nuove. Non sembra molto, ma la maggior parte di esse rimedia ai punti deboli del linguaggio. Il che è in linea con l'intero concetto della versione 8.0, che rende il PHP più compatto e consolidato come nessuna versione prima d'ora. Una panoramica di tutte le nuove funzioni e metodi si trova nella Guida alla migrazione.
str_contains()
str_starts_with()
str_ends_with()
Funzioni per determinare se una stringa inizia, finisce o contiene una sottostringa.
if (str_contains('Nette', 'te')) {
...
}
Con l'avvento di questa trinità, PHP definisce come gestire una stringa vuota durante la ricerca, che è ciò a cui aderiscono tutte le altre funzioni correlate, e cioè una stringa vuota si trova ovunque:
str_contains('Nette', '') // true
str_starts_with('Nette', '') // true
strpos('Nette', '') // 0 (previously false)
Grazie a questo, il comportamento della trinità è completamente identico a quello degli analoghi di Nette:
str_contains() # Nette\Utils\String::contains()
str_starts_with() # Nette\Utils\String::startsWith()
str_ends_with() # Nette\Utils\String::endsWith()
Perché queste funzioni sono così importanti? Le librerie standard di tutti
i linguaggi sono sempre appesantite dallo sviluppo storico; le incoerenze e
i passi falsi non possono essere evitati. Ma allo stesso tempo sono una
testimonianza del rispettivo linguaggio. Sorprendentemente, il PHP di 25 anni
fa manca di funzioni per operazioni di base come la restituzione del primo
o dell'ultimo elemento di un array, l'escape dell'HTML senza brutte sorprese
(htmlspecialchars
non esegue l'escape di un apostrofo) o la
semplice ricerca di una stringa in una stringa. Non è detto che possa essere
in qualche modo aggirato, perché il risultato non è un codice
leggibile e comprensibile. Questa è una lezione per tutti gli autori di API.
Quando si vede che gran parte della documentazione della funzione è occupata da
spiegazioni di insidie (come i valori di ritorno di strpos
), è un
chiaro segnale per modificare la libreria e aggiungere
str_contains
.
get_debug_type()
Sostituisce l'ormai obsoleto get_type()
. Al posto dei tipi
lunghi, come integer
, restituisce l'attuale int
,
mentre nel caso degli oggetti restituisce direttamente il tipo:
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) |
Migrazione da risorsa a oggetto
I valori del tipo di risorsa derivano da un'epoca in cui PHP non aveva ancora gli oggetti, ma ne aveva effettivamente bisogno. È così che sono nate le risorse. Oggi abbiamo gli oggetti e, rispetto alle risorse, funzionano molto meglio con il garbage collector, quindi il piano è di sostituirli gradualmente tutti con gli oggetti.
A partire da PHP 8.0, le risorse immagini, curl join, openssl, xml, ecc. sono state trasformate in oggetti. In PHP 8.1, seguiranno le connessioni FTP e così via.
$res = imagecreatefromjpeg('image.jpg');
$res instanceof GdImage // true
is_resource($res) // false - BC break
Questi oggetti non hanno ancora metodi, né si possono istanziare
direttamente. Finora si tratta solo di eliminare le risorse obsolete da PHP
senza modificare l'API. E questo è un bene, perché creare una buona API è un
compito separato e impegnativo. Nessuno desidera la creazione di nuove classi
PHP come SplFileObject con metodi chiamati fgetc()
o
fgets()
.
PhpToken
Anche il tokenizer e le funzioni intorno a token_get_all
sono
migrate a oggetti. Questa volta non si tratta di sbarazzarsi delle risorse, ma
di ottenere un oggetto completo che rappresenta 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
Il metodo isIgnorable()
restituisce true per i token
T_WHITESPACE
, T_COMMENT
, T_DOC_COMMENT
e
T_OPEN_TAG
.
Mappe deboli
Le mappe deboli sono legate al garbage collector, che rilascia dalla memoria tutti gli oggetti e i valori che non sono più utilizzati (cioè non ci sono variabili o proprietà che li contengono). Poiché i thread PHP hanno vita breve e la memoria disponibile sui nostri server è abbondante, di solito non ci occupiamo affatto di questioni relative alla liberazione efficace della memoria. Ma per gli script di lunga durata, sono essenziali.
L'oggetto WeakMap
è simile a SplObjectStorage
Entrambi usano oggetti come chiavi e permettono di memorizzare valori arbitrari
sotto di essi. La differenza è che WeakMap
non impedisce
all'oggetto di essere rilasciato dal garbage collector. Cioè, se l'unico posto
in cui l'oggetto esiste attualmente è una chiave nella mappa debole, verrà
rimosso dalla mappa e dalla memoria.
$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 'data for $obj';
dump(count($map)); // 1
unset($obj);
dump(count($map)); // 0
A cosa serve? Per esempio, per la cache. Abbiamo un metodo
loadComments()
a cui passiamo un articolo del blog e che
restituisce tutti i suoi commenti. Poiché il metodo viene chiamato
ripetutamente per lo stesso articolo, creeremo un altro
getComments()
, che metterà in cache il risultato del primo
metodo:
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]
}
...
}
Il punto è che quando l'oggetto $article
viene rilasciato (per
esempio, l'applicazione inizia a lavorare con un altro articolo), anche la sua
voce viene rilasciata dalla cache.
PHP JIT (compilatore Just in Time)
Forse sapete che PHP viene compilato in cosiddetti opcode, ovvero istruzioni di basso livello che potete vedere qui, per esempio e che vengono eseguite da una macchina virtuale PHP. E cos'è un JIT? Il JIT può compilare in modo trasparente PHP direttamente in codice macchina, che viene eseguito direttamente dal processore, in modo da evitare l'esecuzione più lenta da parte della macchina virtuale.
Il JIT ha quindi lo scopo di velocizzare PHP.
Lo sforzo di implementare JIT in PHP risale al 2011 ed è sostenuto da Dmitry Stogov. Da allora, ha provato 3 diverse implementazioni, ma nessuna di queste è stata inserita nella versione finale di PHP per tre motivi: il risultato non è mai stato un aumento significativo delle prestazioni per le applicazioni web tipiche; complica la manutenzione di PHP (cioè nessuno, a parte Dmitry, lo capisce 😉 ); c'erano altri modi per migliorare le prestazioni senza dover usare un JIT.
L'aumento delle prestazioni osservato nella versione 7 di PHP è stato un sottoprodotto del lavoro sul JIT, anche se paradossalmente non è stato implementato. Questo sta accadendo solo ora in PHP 8. Ma non nutro aspettative esagerate: probabilmente non vedrete alcun aumento di velocità.
Perché il JIT è entrato in PHP? Innanzitutto, gli altri modi per migliorare le prestazioni si stanno lentamente esaurendo e il JIT è semplicemente il passo successivo. Nelle comuni applicazioni web, non porta alcun miglioramento della velocità, ma accelera in modo significativo, ad esempio, i calcoli matematici. Questo apre la possibilità di iniziare a scrivere queste cose in PHP. In effetti, sarebbe possibile implementare direttamente in PHP alcune funzioni che in precedenza richiedevano un'implementazione diretta in C a causa della velocità.
Il JIT fa parte dell'estensione opcache
ed è abilitato insieme
ad essa in php.ini (leggere la documentazione
su quelle quattro 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
È possibile verificare che JIT sia in esecuzione, per esempio, nel pannello informativo della barra Tracy.
Il JIT funziona molto bene se tutte le variabili hanno tipi chiaramente
definiti e non possono cambiare anche quando si richiama ripetutamente lo stesso
codice. Mi chiedo quindi se un giorno anche in PHP dichiareremo i tipi delle
variabili: string $s = 'Bye, this is the end of the series';
Per inviare un commento, effettuare il login