PHP 8.0 : Nouveautés dans les types de données (2/4)
La version 8.0 de PHP vient d'être publiée. Elle est pleine de nouvelles fonctionnalités, comme aucune autre version auparavant. Leur présentation mérite quatre articles distincts. Dans la deuxième partie, nous allons nous intéresser aux types de données.
Remontons dans le temps. L'introduction des indices de type scalaire a été
une avancée significative en PHP 7. Elle a failli ne pas voir le jour. Andreu Faulds, l'auteur de cette solution
ingénieuse, entièrement rétrocompatible et optionnelle grâce à
declare(strict_types=1)
, a été sévèrement rejeté par la
communauté. Heureusement, Anthony
Ferrara a pris sa défense et celle de la proposition à l'époque, a lancé
une campagne et le RFC est passé de très près. Ouf. La plupart des
nouveautés de PHP 8 sont l'œuvre du légendaire Nikita Popov et elles ont toutes passé le
vote comme un couteau dans du beurre. Le monde change pour le mieux.
PHP 8 amène les types à la perfection. La grande majorité des annotations
phpDoc comme @param
, @return
et @var
seront remplacées par une notation native. Mais surtout, les types seront
vérifiés par le moteur PHP. Seules les descriptions de structures telles que
string[]
ou les annotations plus complexes pour PHPStan resteront
dans les commentaires.
Types d'union
Les types d'union sont une énumération de deux ou plusieurs types qu'une variable peut accepter :
class Button
{
private string|object $caption;
public function setCaption(string|object $caption)
{
$this->caption = $caption;
}
}
Certains types d'union ont déjà été introduits en PHP. Les types nuls,
par exemple. Comme ?string
, qui est équivalent au type union
string|null
. La notation du point d'interrogation peut être
considérée comme une abréviation. Bien sûr, elle fonctionne également en
PHP 8, mais vous ne pouvez pas la combiner avec des barres verticales. Ainsi, au
lieu de ?string|object
, vous devez écrire
string|object|null
. Par ailleurs, iterable
a toujours
été équivalent à array|Traversable
. Vous serez peut-être
surpris de constater que float
est également un type d'union, car
il accepte les deux int|float
, mais convertit la valeur en
float
.
Vous ne pouvez pas utiliser les pseudotypes void
et
mixed
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)
{
...
}
D'autre part, le câblage automatique dans Nette DI rejette les types d'union. Jusqu'à présent, il n'y a pas de cas d'utilisation où il serait logique que le constructeur accepte un objet ou un autre. Bien sûr, si un tel cas d'utilisation 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 du type union (c'est-à-dire dans la version
3.1 ; dans la version 3.0 antérieure, ces méthodes reviennent pour maintenir
la compatibilité nulle).
mixed
Le pseudotype mixed
accepte n'importe quelle valeur.
Dans le cas des paramètres et des propriétés, il entraîne le même
comportement que si nous ne spécifions aucun type. Alors, quel est son but ?
A distinguer quand un type est simplement absent et quand il est
intentionnellement mixed
.
Dans le cas des valeurs de retour des fonctions et des méthodes, ne pas
spécifier le type diffère de l'utilisation de mixed
. C'est le
contraire de void
, car la fonction doit retourner quelque chose. Un
retour manquant produit alors une erreur fatale.
En pratique, vous devriez rarement l'utiliser, car grâce aux types d'union, vous pouvez spécifier la valeur avec précision. Elle ne convient donc que dans des situations uniques :
function dump(mixed $var): mixed
{
// print variable
return $var;
}
false
Contrairement à mixed
, vous pouvez utiliser le nouveau
pseudotype false
exclusivement dans les types d'union. Il est né
de la nécessité de décrire le type de retour des fonctions natives, qui
renvoient historiquement false en cas d'échec :
function strpos(string $haystack, string $needle): int|false
{
}
Par conséquent, il n'existe pas de type true
. Vous ne pouvez
pas non plus utiliser false
seul, ni false|null
, ni
bool|false
.
statique
Le pseudotype static
ne peut être utilisé que comme type de
retour d'une méthode. Il indique que la méthode retourne un objet du même
type que l'objet lui-même (alors que self
indique qu'elle retourne
la classe dans laquelle la méthode est définie). Il est excellent pour
décrire des 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();
ressource
Ce type n'existe pas en PHP 8 et ne sera pas introduit dans le futur. Les
ressources sont une relique de l'époque où PHP n'avait pas d'objets.
Progressivement, les ressources vont être remplacées par des objets.
Finalement, elles disparaîtront complètement. Par exemple, PHP 8.0 remplace
la ressource image par un objet GdImage
, et la ressource curl par
un objet CurlHandle
.
Stringable
C'est une interface qui est automatiquement implémentée par chaque objet
avec une méthode magique __toString()
.
class Email
{
public function __toString(): string
{
return $this->value;
}
}
function print(Stringable|string $s)
{
}
print('abc');
print(new Email);
Il est possible d'indiquer explicitement
class Email implements Stringable
dans la définition de la classe,
mais ce n'est pas nécessaire.
Nette\Utils\Html
Latte reflète également ce schéma de
dénomination en implémentant l'interface Nette\HtmlStringable
au
lieu de l'ancienne IHtmlString
. Les objets de ce type, par exemple,
ne sont pas échappés par Latte.
Type variance, contravariance, covariance
Le principe de substitution de Liskov (LSP) stipule que les classes d'extension et les implémentations d'interface ne doivent jamais exiger plus et fournir moins que le parent. En d'autres termes, la méthode enfant ne doit pas exiger plus d'arguments ou accepter une gamme plus étroite de types pour les paramètres que le parent, et vice versa, elle ne doit pas retourner une gamme plus large de types. Mais elle peut en renvoyer moins. Pourquoi ? Parce que sinon, l'héritage serait rompu. Une fonction accepterait un objet d'un type spécifique, mais elle n'aurait aucune idée des paramètres qu'elle peut passer à ses méthodes et des types qu'elles renverraient. N'importe quel enfant pourrait la casser.
Donc, dans la POO, la classe enfant peut :
- accepter une gamme plus large de types dans les paramètres (c'est ce qu'on appelle la contravariance)
- retourner une gamme plus étroite de types (covariance)
- et les propriétés ne peuvent pas changer de type (elles sont invariantes).
PHP est capable de faire cela depuis la version 7.4, et tous les nouveaux types introduits en PHP 8.0 supportent également la contravariance et la covariance.
Dans le cas de mixed
, l'enfant peut réduire la valeur de retour
à n'importe quel type, mais pas void
, car il ne représente pas
une valeur, mais plutôt son absence. L'enfant ne peut pas non plus omettre une
déclaration de type, car cela permet également une absence de valeur.
class A
{
public function foo(mixed $foo): mixed
{}
}
class B extends A
{
public function foo($foo): string
{}
}
Les types d'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
{}
}
En outre, false
peut être étendu à bool
dans le
paramètre ou vice versa bool
à false
dans la valeur
de retour.
Toutes les infractions à la covariance/contravariance conduisent à une erreur fatale en PHP 8.0.
Dans les prochaines parties de cette série, nous montrerons ce que sont les attributs, quelles nouvelles fonctions et classes sont apparues en PHP et nous présenterons Just in Time Compiler.
Pour soumettre un commentaire, veuillez vous connecter