PHP 8.0 : Nouvelles fonctions, classes et JIT (4/4)
La version 8.0 de PHP est sortie. Elle est pleine de nouvelles fonctionnalités comme aucune autre version auparavant. Leur présentation méritait quatre articles distincts. Dans la dernière partie, nous jetterons un coup d'œil aux nouvelles fonctions et classes et nous présenterons le compilateur Just in Time.
Nouvelles fonctions
La bibliothèque PHP standard comporte des centaines de fonctions et dans la version 8.0, six nouvelles fonctions sont apparues. Cela ne semble pas beaucoup, mais la plupart d'entre elles remédient aux points faibles du langage. Ce qui s'inscrit parfaitement dans le concept de la version 8.0, qui resserre et consolide PHP comme aucune autre version auparavant. Un aperçu de toutes les nouvelles fonctions et méthodes peut être trouvé dans le guide de migration.
str_contains()
str_starts_with()
str_ends_with()
Fonctions permettant de déterminer si une chaîne de caractères commence, se termine ou contient une sous-chaîne.
if (str_contains('Nette', 'te')) {
...
}
Avec l'avènement de cette trinité, PHP définit comment gérer une chaîne vide lors d'une recherche, ce à quoi toutes les autres fonctions connexes adhèrent, et qui est une chaîne vide est trouvée partout:.
str_contains('Nette', '') // true
str_starts_with('Nette', '') // true
strpos('Nette', '') // 0 (previously false)
Grâce à cela, le comportement de la trinité est complètement identique aux analogues de Nette :
str_contains() # Nette\Utils\String::contains()
str_starts_with() # Nette\Utils\String::startsWith()
str_ends_with() # Nette\Utils\String::endsWith()
Pourquoi ces fonctions sont-elles si importantes ? Les bibliothèques
standard de tous les langages sont toujours marquées par l'évolution
historique ; les incohérences et les faux pas ne peuvent être évités. Mais
en même temps, c'est un témoignage de la langue respective. Il est surprenant
de constater que le PHP, vieux de 25 ans, manque de fonctions pour des
opérations aussi basiques que le retour du premier ou du dernier élément d'un
tableau, l'échappement du HTML sans mauvaises surprises
(htmlspecialchars
n'échappe pas une apostrophe), ou simplement la
recherche d'une chaîne dans une chaîne. Il n'est pas possible de le contourner
d'une manière ou d'une autre, car le résultat n'est pas un code lisible et
compréhensible. C'est une leçon pour tous les auteurs d'API. Lorsque vous
voyez qu'une grande partie de la documentation de la fonction est occupée par
des explications sur les pièges (comme les valeurs de retour de
strpos
), c'est un signe clair pour modifier la bibliothèque et
ajouter str_contains
.
get_debug_type()
Remplace le désormais obsolète get_type()
. Au lieu des types
longs comme integer
, il renvoie l'actuel int
, dans le
cas des objets, il renvoie directement le type :
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) |
Migration des ressources vers les objets
Les valeurs du type ressource viennent d'une époque où PHP n'avait pas encore d'objets, mais en avait besoin. C'est ainsi que les ressources sont nées. Aujourd'hui, nous avons les objets et, comparés aux ressources, ils fonctionnent beaucoup mieux avec le garbage collector, donc le plan est de les remplacer progressivement par des objets.
A partir de PHP 8.0, les ressources images, curl joins, openssl, xml, etc. sont changées en objets. En PHP 8.1, les connexions FTP, etc. suivront.
$res = imagecreatefromjpeg('image.jpg');
$res instanceof GdImage // true
is_resource($res) // false - BC break
Ces objets n'ont pas encore de méthodes, et vous ne pouvez pas les
instancier directement. Jusqu'à présent, il s'agit simplement de se
débarrasser des ressources obsolètes de PHP sans modifier l'API. Et c'est une
bonne chose, car créer une bonne API est une tâche distincte et difficile.
Personne ne souhaite la création de nouvelles classes PHP telles que
SplFileObject avec des méthodes nommées fgetc()
ou
fgets()
.
PhpToken
Le tokenizer et les fonctions autour de token_get_all
sont
également migrés vers des objets. Cette fois, il ne s'agit pas de se
débarrasser des ressources, mais d'obtenir un objet à part entière
représentant un jeton 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
La méthode isIgnorable()
retourne vrai pour les jetons
T_WHITESPACE
, T_COMMENT
, T_DOC_COMMENT
,
et T_OPEN_TAG
.
Cartes faibles
Les cartes faibles sont liées au ramasseur d'ordures, qui libère de la mémoire tous les objets et valeurs qui ne sont plus utilisés (c'est-à-dire qu'aucune variable ou propriété ne les contient). Comme les threads PHP sont de courte durée et que nous disposons de beaucoup de mémoire sur nos serveurs, nous n'abordons généralement pas les questions relatives à la libération effective de la mémoire. Mais pour les scripts plus longs, ils sont essentiels.
L'objet WeakMap
est similaire à SplObjectStorage
.
Tous deux utilisent des objets comme clés et permettent de stocker des valeurs
arbitraires sous ces objets. La différence est que WeakMap
n'empêche pas l'objet d'être libéré par le ramasseur de déchets.
C'est-à-dire que si le seul endroit où l'objet existe actuellement est une
clé dans la carte faible, il sera supprimé de la carte et de la mémoire.
$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 'data for $obj';
dump(count($map)); // 1
unset($obj);
dump(count($map)); // 0
A quoi cela sert-il ? Par exemple, pour la mise en cache. Prenons une
méthode loadComments()
à laquelle nous passons un article de blog
et qui renvoie tous ses commentaires. Comme la méthode est appelée à
plusieurs reprises pour le même article, nous allons créer une autre
getComments()
, qui mettra en cache le résultat de la première
méthode :
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]
}
...
}
L'idée est que lorsque l'objet $article
est libéré (par
exemple, l'application commence à travailler avec un autre article), son
entrée est également libérée du cache.
PHP JIT (Just in Time Compiler)
Vous savez peut-être que PHP est compilé en ce que l'on appelle des opcodes, qui sont des instructions de bas niveau que vous pouvez voir ici, par exemple et qui sont exécutées par une machine virtuelle PHP. Et qu'est-ce qu'un JIT ? Le JIT peut compiler PHP de manière transparente directement en code machine, qui est exécuté directement par le processeur, de sorte que l'exécution plus lente par la machine virtuelle est contournée.
Le JIT est donc destiné à accélérer PHP.
L'effort pour implémenter le JIT dans PHP remonte à 2011 et est soutenu par Dmitry Stogov. Depuis, il a essayé 3 implémentations différentes, mais aucune d'entre elles ne s'est retrouvée dans une version finale de PHP pour trois raisons : le résultat n'a jamais été une augmentation significative des performances pour les applications web typiques ; complique la maintenance de PHP (c'est-à-dire que personne à part Dmitry ne le comprend 😉 ) ; il y avait d'autres moyens d'améliorer les performances sans avoir à utiliser un JIT.
Le saut d'augmentation des performances observé dans la version 7 de PHP était un sous-produit du travail sur le JIT, bien que paradoxalement il n'ait pas été déployé. Ce n'est qu'aujourd'hui que cela se produit en PHP 8. Mais je retiens des attentes exagérées : vous ne verrez probablement pas d'accélération.
Alors pourquoi le JIT fait-il son entrée en PHP ? Tout d'abord, les autres moyens d'améliorer les performances s'épuisent lentement, et le JIT est simplement l'étape suivante. Dans les applications web courantes, il n'apporte aucune amélioration de la vitesse, mais il accélère significativement, par exemple, les calculs mathématiques. Cela ouvre la possibilité de commencer à écrire ces choses en PHP. En fait, il serait possible d'implémenter directement en PHP certaines fonctions qui nécessitaient auparavant une implémentation directe en C pour des raisons de vitesse.
Le JIT fait partie de l'extension opcache
et est activé avec
elle dans le php.ini (lisez la documentation
sur ces quatre chiffres) :
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
Vous pouvez vérifier que le JIT est en cours d'exécution, par exemple, dans le panneau d'information de la barre Tracy.
Le JIT fonctionne très bien si toutes les variables ont des types clairement
définis et ne peuvent pas changer même en appelant le même code à plusieurs
reprises. Je me demande donc si un jour nous déclarerons également des types
en PHP pour les variables
: string $s = 'Bye, this is the end of the series';
Pour soumettre un commentaire, veuillez vous connecter