PHP 7.3 est sorti le 6 décembre. Pas de fonctionnalité phare. C’est une collection d’améliorations du quotidien qui, individuellement, semblent mineures, mais qui ensemble rendent le travail de tous les jours nettement moins agaçant.
Heredoc et nowdoc flexibles
Jusqu’à 7.3, le marqueur de fermeture d’un heredoc devait être en colonne zéro. Ce qui forçait une désindentation maladroite dans du code par ailleurs bien formaté :
// avant
$html = <<<HTML
<div>
<p>Hello</p>
</div>
HTML; // devait être en colonne 0, moche
// après
$html = <<<HTML
<div>
<p>Hello</p>
</div>
HTML;
Le marqueur de fermeture peut désormais être indenté pour correspondre au code environnant, et cette indentation est retirée du contenu. Ça paraît cosmétique. Ce n’est pas le cas. Les heredocs dans des contextes imbriqués (méthodes de classe, conditions) étaient visuellement dissonants avant. Maintenant ils s’intègrent.
array_key_first() et array_key_last()
Ce contournement existait depuis toujours :
$first = array_keys($array)[0];
7.3 ajoute les helpers évidents :
$first = array_key_first($array);
$last = array_key_last($array);
Et is_countable() pour vérifier proprement avant d’appeler count() sur quelque chose qui n’implémente peut-être pas Countable. Des fonctions qui auraient dû exister depuis des années.
PCRE2
Le moteur d’expressions régulières a migré de PCRE vers PCRE2. Largement invisible pour les patterns existants, mais PCRE2 est activement maintenu et gère mieux les cas limites. L’impact pratique principal : certains patterns qui produisaient auparavant un comportement indéfini lancent maintenant des erreurs. C’est le bon comportement, même si ça surprend lors du premier upgrade.
Virgules finales dans les appels de fonctions
7.2 autorisait les virgules finales dans les imports de namespaces groupés. 7.3 étend ça aux appels de fonctions et de méthodes :
$result = array_merge(
$defaults,
$overrides,
$extras, // plus besoin de retirer cette virgule avant la parenthèse fermante
);
Ça compte surtout pour les appels multiligne. Ajouter ou retirer un argument ne nécessite plus de toucher à la ligne adjacente. Les diffs restent honnêtes, les rebases deviennent un peu moins douloureux.
Assignments par référence dans la déstructuration de tableaux
La déstructuration de tableaux a gagné la capacité de capturer des références plutôt que des copies :
$data = ['Alice', 42];
[&$name, $age] = $data;
$name = 'Bob';
var_dump($data[0]); // string(3) "Bob"
Les références imbriquées fonctionnent aussi :
[$a, [&$b]] = [1, [2]];
Plus de niche que les virgules finales, mais le bon outil quand on a besoin d’aliaser profondément dans une structure sans un tas d’assignations intermédiaires.
instanceof avec des littéraux est maintenant légal
Avant, utiliser instanceof avec un littéral à gauche était une erreur de parsing. 7.3 le rend valide :
var_dump(null instanceof stdClass); // bool(false)
Ça retourne toujours false, ce qui est exactement correct. L’avantage, c’est que du code qui construit conditionnellement une valeur puis vérifie son type n’a plus besoin d’extraire la valeur dans une variable au préalable. Utile dans le code généré et les helpers de test.
json_decode() et json_encode() peuvent maintenant lever des exceptions
Avant 7.3, les erreurs JSON étaient silencieuses à moins de penser à vérifier json_last_error(). Facile à oublier, facile à rater :
$data = json_decode($response);
if (json_last_error() !== JSON_ERROR_NONE) {
// la plupart des gens oubliaient cette partie
}
7.3 ajoute JSON_THROW_ON_ERROR :
$data = json_decode($response, true, 512, JSON_THROW_ON_ERROR);
// lève JsonException sur une entrée malformée
JsonException étend RuntimeException. Attrapez-la spécifiquement ou laissez-la se propager. Ça aurait dû fonctionner comme ça dès le début.
setcookie() avec un tableau d’options
L’ancienne signature de setcookie() est un vestige : sept arguments positionnels, dont la plupart qu’on laisse à leurs valeurs par défaut juste pour atteindre celui qu’on veut vraiment. 7.3 ajoute une forme alternative qui prend un tableau associatif :
setcookie('session', $token, [
'expires' => time() + 3600,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
L’option samesite est la vraie raison pour laquelle ça a été ajouté — l’ancienne signature positionnelle n’avait pas de slot pour elle. session_set_cookie_params() a reçu le même traitement, et une nouvelle directive ini session.cookie_samesite couvre la valeur par défaut.
hrtime() pour un benchmarking qui mesure vraiment le temps
microtime() lit l’horloge murale. Très bien pour la plupart des cas. Mais elle est affectée par les ajustements NTP, et sa résolution dépend de l’implémentation. hrtime() lit l’horloge monotone haute résolution :
$start = hrtime(true); // nanosecondes sous forme d'entier
doWork();
$elapsed = hrtime(true) - $start;
echo $elapsed / 1e6 . " ms\n";
Sans l’argument true, elle retourne [secondes, nanosecondes] sous forme d’un tableau à deux éléments. Utilisez ça pour les microbenchmarks, ou partout où la dérive d’horloge corromprait silencieusement vos mesures.
gc_status() — regarder à l’intérieur du ramasse-miettes
Le ramasse-miettes cyclique de PHP se déclenche quand un buffer de cycles potentiels se remplit. Jusqu’à 7.3 il n’y avait pas de moyen simple de voir ce qu’il faisait réellement. gc_status() expose l’état interne :
$status = gc_status();
// [
// 'runs' => 3,
// 'collected' => 127,
// 'threshold' => 10001,
// 'roots' => 42,
// ]
Pas quelque chose que la plupart du code applicatif a besoin. Utile quand on essaie de comprendre pourquoi la mémoire continue de grimper sous des charges de travail spécifiques.
CompileError rejoint la hiérarchie des exceptions
Les erreurs de parsing sont catchables en tant que ParseError depuis PHP 7.0. 7.3 introduit CompileError comme classe parente pour les échecs à la compilation, avec ParseError qui devient une sous-classe :
Error
└── CompileError
└── ParseError
En pratique, le code qui catch ParseError continue de fonctionner. La nouvelle classe donne juste aux futures erreurs de compilation (qui ne sont pas des erreurs de parsing) une place correcte dans la hiérarchie.
bcscale() comme getter
L’échelle BC Math était toujours settable via bcscale($n). Obtenir l’échelle actuelle nécessitait de la suivre soi-même. 7.3 fait fonctionner bcscale() sans arguments :
bcscale(4);
echo bcscale(); // 4
Mineur. Utile à savoir si vous écrivez du code de bibliothèque qui doit respecter ou restaurer le paramètre d’échelle de l’appelant.
L’avertissement pour continue dans un switch
Celui-là est un correctif d’exactitude qui ressemble à une dépréciation. En PHP, continue dans un switch s’est toujours comporté comme break — il sort du switch, pas de la boucle englobante. Les développeurs venant d’autres langages écrivent souvent ça en espérant passer à l’itération suivante de la boucle :
foreach ($items as $item) {
switch ($item->type) {
case 'skip':
continue; // FAUX : sort du switch, pas du foreach
}
}
7.3 ajoute un warning pour ce pattern. Le correctif, c’est continue 2 pour cibler explicitement la boucle englobante. Le comportement n’a pas changé. Le silence, si.
Dépréciations
Les constantes insensibles à la casse déclarées via define() sont dépréciées :
define('MY_CONST', 42, true); // troisième argument déprécié
Passer une needle non-chaîne à strpos(), strstr(), et fonctions similaires est déprécié. En PHP 8, ces fonctions interpréteront la needle comme une chaîne, pas comme un codepoint ASCII. Si vous passez intentionnellement des entiers à ces fonctions, chr($n) est la forme explicite.
fgetss() est déprécié — c’était fgets() avec les balises HTML/PHP retirées. Utilisez fgets() et retirez les balises explicitement si nécessaire. Le filtre de stream string.strip_tags disparaît avec lui.
7.3 est le genre de version qu’on apprécie avec du recul. Rien d’individuellement dramatique, mais après six mois avec elle, la correction des heredocs seule a amorti le coût de la migration en lisibilité. Parfois le banal est exactement ce qu’il faut.