PHP 8.0 : Types de données (2/4)
La version 8.0 de PHP est sortie. Elle est tellement pleine de nouveautés qu'aucune version précédente ne l'a été. Leur présentation a nécessité quatre articles distincts. Dans ce deuxième, nous allons examiner les types de données.

Revenons à l'histoire. La percée majeure de PHP 7 a été l'introduction
des type hints scalaires. Cela a failli ne pas se produire. L'auteure de la
solution géniale Andreu Faulds, qui était
entièrement rétrocompatible et optionnelle grâce à
declare(strict_types=1)
, a été méchamment rejetée par la
communauté. Heureusement, Anthony
Ferrara a pris sa défense et celle de sa proposition à l'époque, a lancé
une campagne et le RFC est passé de justesse. Ouf. La plupart des nouveautés
de PHP 8 sont dues au légendaire Nikita
Popov et elles sont passées comme une lettre à la poste lors du vote. Le
monde s'améliore.
PHP 8 perfectionne les types. La grande majorité des annotations phpDoc
@param
, @return
et @var
disparaîtront du
code et seront remplacées par une notation native et surtout par un contrôle
par le moteur PHP. Seules les descriptions de structures comme
string[]
ou des annotations plus complexes pour PHPStan resteront
dans les commentaires.
Types Union
Les types Union sont une énumération de deux ou plusieurs types qu'une valeur peut prendre :
class Button
{
private string|object $caption;
public function setCaption(string|object $caption)
{
$this->caption = $caption;
}
}
PHP connaissait déjà certains types d'union spéciaux auparavant. Par
exemple, les types nullable comme ?string
, qui est l'équivalent du
type d'union string|null
, et la notation avec point d'interrogation
peut être considérée comme un simple raccourci. Bien sûr, cela fonctionne
aussi en PHP 8, mais il ne peut pas être combiné avec des barres verticales,
donc au lieu de ?string|object
, il faut écrire
string|object|null
en entier. De plus, iterable
a
toujours été l'équivalent de array|Traversable
. Vous serez
peut-être surpris d'apprendre que float
est en fait aussi un type
d'union qui accepte int|float
, mais qui le convertit en
float
.
Les pseudo-types void
et mixed
ne peuvent pas être
utilisés dans les unions, car cela n'aurait aucun sens.
Nette est prête pour les types union. Dans Schema, Expect::from()
les accepte, et les présentateurs les acceptent également. Vous pouvez les
utiliser dans les méthodes render et action, par exemple :
public function renderDetail(int|array $id)
{
...
}
En revanche, l'autowiring dans Nette DI refuse les types union. Il manque pour l'instant un cas d'utilisation où il serait logique qu'un constructeur accepte, par exemple, tel ou tel objet. Bien sûr, s'il apparaît, il sera possible d'adapter le comportement du conteneur en conséquence.
Les méthodes getParameterType()
, getReturnType()
et getPropertyType()
dans Nette\Utils\Reflection lèvent une
exception dans le cas d'un type union (dans la version 3.1, dans l'ancienne
version 3.0, elles renvoient null pour des raisons de compatibilité).
mixed
Le pseudo-type mixed
indique que la valeur peut être absolument
n'importe quoi.
Dans le cas des paramètres et des propriétés, il s'agit en fait du même
comportement que si nous n'indiquions aucun type. À quoi sert-il alors ? Pour
pouvoir distinguer quand le type est simplement manquant et quand il est
vraiment mixed
.
Dans le cas de la valeur de retour d'une fonction ou d'une méthode, ne pas
indiquer de type diffère de l'indication du type mixed
. Il s'agit
en fait de l'opposé de void
, car il indique que la fonction doit
retourner quelque chose. L'absence de return est alors une erreur fatale.
En pratique, vous devriez l'utiliser rarement, car grâce aux types union, vous pouvez spécifier la valeur plus précisément. Il est donc utile dans des situations exceptionnelles :
function dump(mixed $var): mixed
{
// afficher la variable
return $var;
}
false
Le nouveau pseudo-type false
ne peut être utilisé que dans les
types union. Il a été créé pour décrire nativement le type de la valeur de
retour des fonctions natives qui, historiquement, renvoient false en cas
d'échec :
function strpos(string $haystack, string $needle): int|false
{
}
Pour cette raison, il n'existe pas de type true
, on ne peut pas
non plus utiliser false
seul, ni false|null
ou
bool|false
.
static
Le pseudo-type static
ne peut être utilisé que comme type de
retour d'une méthode. Il indique que la méthode renvoie un objet du même type
que l'objet lui-même (tandis que self
indique qu'elle renvoie la
classe dans laquelle la méthode est définie). Ce qui est parfaitement adapté
pour décrire les interfaces fluides :
class Item
{
public function setValue($val): static
{
$this->value = $val;
return $this;
}
}
class ItemChild extends Item
{
public function childMethod()
{
}
}
$child = new ItemChild;
$child->setValue(10)
->childMethod();
resource
Ce type n'existe pas en PHP 8 et ne sera pas non plus introduit à l'avenir.
Les ressources sont une relique historique de l'époque où PHP n'avait pas
encore d'objets. Progressivement, les ressources seront remplacées par des
objets et ce type finira par disparaître complètement. Par exemple, PHP
8.0 remplace la ressource représentant une image par l'objet
GdImage
et la ressource de connexion curl
par l'objet
CurlHandle
.
Stringable
Il s'agit d'une interface que chaque objet doté de la méthode magique
__toString()
implémente automatiquement.
class Email
{
public function __toString(): string
{
return $this->value;
}
}
function print(Stringable|string $s)
{
}
print('abc');
print(new Email);
Dans la définition de la classe, il est possible d'indiquer explicitement
class Email implements Stringable
, mais ce n'est pas
nécessaire.
Ce style de nommage est également reflété par
Nette\Utils\Html
, qui implémente l'interface
Nette\HtmlStringable
au lieu de l'ancienne
IHtmlString
. Les objets de ce type ne sont alors, par exemple, pas
échappés par Latte.
Variance de type, contravariance, covariance
Le principe de substitution de Liskov (Liskov Substitution Principle – LSP) stipule que les descendants d'une classe et les implémentations d'une interface ne doivent jamais exiger plus et fournir moins que le parent. C'est-à-dire que la méthode d'un descendant ne doit pas exiger plus d'arguments ou accepter une plage de types plus étroite pour les paramètres que l'ancêtre, et inversement, elle ne doit pas renvoyer une plage de types plus large. Mais elle peut renvoyer une plage plus étroite. Pourquoi ? Parce que sinon, l'héritage ne fonctionnerait pas du tout. Une fonction accepterait certes un objet d'un certain type, mais elle ne saurait pas quels paramètres peuvent être passés aux méthodes et ce qu'elles retourneront réellement, car n'importe quel descendant pourrait le perturber.
Donc, en POO, il est vrai que le descendant peut :
- accepter une plage de types plus large dans les paramètres (c'est ce qu'on appelle la contravariance)
- renvoyer une plage de types plus étroite (covariance)
- et les propriétés ne peuvent pas changer de type (elles sont invariantes)
PHP sait faire cela depuis la version 7.4 et tous les types nouvellement introduits en PHP 8.0 prennent également en charge la contravariance et la covariance.
Dans le cas de mixed
, le descendant peut restreindre la valeur
de retour à n'importe quel type, mais pas void
, car il ne s'agit
pas d'un type de valeur, mais de son absence. Le descendant ne peut pas non plus
ne pas indiquer de type, car cela autorise également l'absence.
class A
{
public function foo(mixed $foo): mixed
{}
}
class B extends A
{
public function foo($foo): string
{}
}
Les types union peuvent également être étendus dans les paramètres et restreints dans les valeurs de retour :
class A
{
public function foo(string|int $foo): string|int
{}
}
class B extends A
{
public function foo(string|int|float $foo): string
{}
}
De plus, false
peut être étendu à bool
dans un
paramètre, ou inversement, bool
peut être restreint à
false
dans la valeur de retour.
Toutes les infractions à la covariance/contravariance entraînent une erreur fatale en PHP 8.0.
Dans les prochaines parties, nous verrons ce que sont les attributs, quelles nouvelles fonctions et classes sont apparues en PHP, et nous présenterons le Just in Time Compiler.
Pour soumettre un commentaire, veuillez vous connecter