PHP 8.0: Nuove funzioni, classi e JIT (4/4)
È stata rilasciata la versione 8.0 di PHP. È così ricca di novità come nessuna versione precedente. La loro presentazione ha richiesto ben quattro articoli separati. In quest'ultima parte, esamineremo le nuove funzioni e classi e presenteremo il Just in Time Compiler.

Nuove funzioni
La libreria standard di PHP dispone di centinaia di funzioni e nella versione 8.0 ne sono apparse sei nuove. Sembra poco, ma la maggior parte di esse colma i punti deboli del linguaggio. Il che si sposa bene con il tono dell'intera versione 8.0, che perfeziona e consolida PHP come nessuna versione precedente. Una panoramica di tutte le nuove funzioni e metodi si trova nella migration guide.
str_contains()
str_starts_with()
str_ends_with()
Funzioni per verificare se una stringa inizia, finisce o contiene una sottostringa.
if (str_contains('Nette', 'te')) {
...
}
Insieme all'arrivo di questa tripletta, PHP definisce come trattare la stringa vuota durante la ricerca, criterio seguito anche da tutte le altre funzioni correlate, ovvero che la stringa vuota si trova ovunque:
str_contains('Nette', '') // true
str_starts_with('Nette', '') // true
strpos('Nette', '') // 0 (prima false)
Grazie a ciò, il comportamento della tripletta è del tutto identico agli equivalenti in 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 e non si può
evitare la nascita di incoerenze e passi falsi. Ma allo stesso tempo, sono il
biglietto da visita di quel linguaggio. È sorprendente che in un PHP vecchio di
25 anni manchino funzioni per operazioni così basilari come restituire il
primo o l'ultimo elemento di un array, eseguire l'escaping HTML senza tranelli
(htmlspecialchars
non esegue l'escaping dell'apostrofo), o appunto
cercare una stringa in una stringa. Che si possa in qualche modo
aggirare non regge, perché il risultato non è un codice leggibile e
comprensibile. È una lezione per tutti gli autori di API. Quando vedete che una
parte considerevole della documentazione di una funzione è dedicata alla
spiegazione delle insidie (come ad esempio i valori di ritorno di
strpos
), è un chiaro segnale per modificare la libreria e
aggiungere proprio str_contains
.
get_debug_type()
Sostituisce il già obsoleto get_type()
. Invece di tipi lunghi
come integer
, restituisce l'ormai usato int
, nel caso
di oggetti restituisce direttamente il tipo:
Valore | 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) |
Oggettivazione delle risorse
I valori di tipo resource provengono dai tempi in cui PHP non aveva ancora oggetti, ma in realtà ne aveva bisogno. Così sono nate le risorse. Oggi abbiamo gli oggetti e rispetto alle risorse funzionano molto meglio con il garbage collector, quindi il piano è di sostituirle gradualmente tutte con oggetti.
Da PHP 8.0, le risorse immagini, connessioni curl, openssl, xml, ecc. vengono trasformate in oggetti. In PHP 8.1 sarà la volta delle connessioni FTP, ecc.
$res = imagecreatefromjpeg('image.jpg');
$res instanceof GdImage // true
is_resource($res) // false - BC break
Questi oggetti per ora non hanno metodi, né potete creare direttamente le
loro istanze. Si tratta per ora davvero solo di eliminare da PHP le risorse
obsolete senza modificare l'API. E questo è un bene, perché creare una buona
API è un compito separato e impegnativo. Nessuno desidera che in PHP nascano
altre classi come SplFileObject con metodi chiamati fgetc()
o
fgets()
.
PhpToken
Anche il tokenizer si sposta verso gli oggetti, e quindi le funzioni intorno
a token_get_all
. Questa volta non si tratta di sbarazzarsi delle
risorse, ma otteniamo un oggetto a pieno titolo che rappresenta un singolo
token PHP.
<?php
$tokens = PhpToken::tokenize('<?php $a = 10;');
$token = $tokens[0]; // istanza 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
.
Weak maps
Le weak map sono correlate al gargabe collector, che rilascia dalla memoria tutti gli oggetti e i valori che non vengono più utilizzati (cioè non esiste alcuna variabile o proprietà utilizzata che li contenga). Poiché la vita di un thread PHP è effimera e oggi abbiamo molta memoria disponibile sui server, di solito non ci preoccupiamo affatto delle questioni relative al rilascio efficiente della memoria. Ma per gli script a lunga esecuzione sono fondamentali.
L'oggetto WeakMap
è simile a SplObjectStorage
. In
entrambi, gli oggetti vengono utilizzati come chiavi e consentono di memorizzare
sotto di essi valori arbitrari. La differenza è che WeakMap
non
impedisce che l'oggetto venga rilasciato dal garbage collector. Cioè, se
l'unico posto in cui l'oggetto esiste ancora è la chiave nella weak map, verrà
rimosso dalla map e dalla memoria.
$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 'dati per $obj';
dump(count($map)); // 1
unset($obj);
dump(count($map)); // 0
A cosa serve? Ad esempio, per il caching. Supponiamo di avere un metodo
loadComments()
, al quale passiamo un articolo del blog e che
restituisce tutti i suoi commenti. Poiché il metodo viene chiamato
ripetutamente con lo stesso articolo, creiamo anche 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 trucco sta nel fatto che nel momento in cui l'oggetto
$article
viene rilasciato (ad esempio, l'applicazione inizia a
lavorare con un altro articolo), viene rilasciata anche la sua voce
dalla cache.
PHP JIT (Just in Time Compiler)
Forse sapete che PHP viene compilato nel cosiddetto opcode, che sono istruzioni di basso livello, che potete vedere ad esempio qui e che vengono eseguite dalla macchina virtuale PHP. E cos'è JIT? JIT può compilare trasparentemente PHP direttamente in codice macchina, che viene eseguito direttamente dal processore, quindi si evita l'esecuzione più lenta tramite la macchina virtuale.
JIT dovrebbe quindi accelerare PHP.
Il tentativo di implementare JIT in PHP risale al 2011 ed è opera di Dmitry Stogov. Da allora ha provato 3 diverse implementazioni, ma nessuna di esse è arrivata in una versione stabile di PHP per i seguenti motivi: il risultato non è mai stato un aumento significativo delle prestazioni per le tipiche applicazioni web; complica la manutenzione di PHP (cioè nessuno tranne Dmitry lo capisce 😉); esistevano altri modi per migliorare le prestazioni senza dover usare JIT.
L'aumento di prestazioni di PHP nella versione 7 è stato un sottoprodotto del lavoro su JIT, sebbene paradossalmente non sia stato distribuito. Ciò avviene solo in PHP 8. Ma frenerò subito le aspettative eccessive: probabilmente non noterete alcuna accelerazione.
Perché allora JIT entra in PHP? Innanzitutto, altri modi per migliorare le prestazioni si stanno lentamente esaurendo e JIT è semplicemente il prossimo passo. Nelle normali applicazioni web non porta accelerazioni, ma accelera significativamente, ad esempio, i calcoli matematici. Si apre così la possibilità di iniziare a scrivere queste cose in PHP. E in realtà sarebbe possibile implementare direttamente in PHP funzioni che finora richiedevano l'implementazione direttamente in C per motivi di velocità.
JIT fa parte dell'estensione opcache
e si attiva insieme ad essa
in php.ini (leggi la documentazione
relativa a quella quaterna di cifre):
zend_extension=php_opcache.dll
opcache.jit=1205 ; configurazione tramite la quaterna OTRC
opcache.enable_cli=1 ; affinché funzioni anche in CLI
opcache.jit_buffer_size=128M ; memoria riservata per il codice compilato
Che JIT sia in esecuzione lo scoprirete ad esempio nel pannello informativo nella Tracy Bar.
JIT funziona molto bene quando tutte le variabili hanno tipi chiaramente
definiti e non possono cambiare durante la chiamata ripetuta dello stesso
codice. Sono quindi curioso di sapere se un giorno in PHP dichiareremo i tipi
anche per le
variabili: string $s = 'Ciao, questa è la conclusione della serie';
Per inviare un commento, effettuare il login