Ce qui se passe
Le composant Serializer s'appuie sur TypeInfo pour connaître le type de chaque propriété qu'il dénormalise. Symfony 8.2 élargit ce que les deux comprennent de vos PHPDoc — génériques et unions dans les tableaux — et les rend plus stricts sur ce qu'ils acceptent.
Les génériques en dénormalisation
La normalisation fonctionnait déjà avec les classes génériques. En
dénormalisation, le Serializer ignorait les types template : une propriété
@var T était dénormalisée comme sa borne, ou laissée en tableau brut quand le
template n'en avait pas.
En 8.2, le type template est résolu depuis le type générique déclaré sur la propriété qui porte l'objet :
src/Dto/ProductCatalog.phpPHPfinal class ProductCatalog
{
/** @var Page<Product> */
public Page $products;
}Le Serializer lit Page<Product>, résout T en Product, et utilise ce type
pour dénormaliser les éléments de Page::$items. Cela vaut aussi pour le
nullable, les génériques imbriqués, plusieurs templates, les unions, les
arguments de constructeur et les collections de génériques comme
list<Page<Product>>.
Deux conditions pratiques :
- déclarer les types concrets sur la propriété, pas sur la classe passée à
denormalize()— ce nom de classe ne transporte pas cette information ; - disposer du
PhpStanExtractorde PropertyInfo et du paquetphpstan/phpdoc-parser.
Contribution de Yonel Ceruto, PR #66005.
Les deux ruptures à anticiper
Unions dans une collection. Le Serializer savait dénormaliser une valeur
typée CardPayment|BankTransfer, mais les éléments de la même union dans une
collection restaient des tableaux bruts. En 8.2, list<CardPayment|BankTransfer>
voit ses éléments dénormalisés — et c'est un changement de comportement : un
élément qui ne correspond à aucun membre de l'union, ou une clé qui ne
correspond pas au type de clé, lève désormais une exception au lieu d'être
renvoyé tel quel. Contribution de Pascal Cescon, PR
#63811.
Une propriété list doit contenir une liste. Un list<string> promet des
clés entières consécutives à partir de zéro, ce qu'un array<int, string> ne
promet pas. Le Serializer traitait les deux de la même façon et acceptait donc un
objet JSON dans une propriété list. En 8.2, cela fonctionne encore mais
déclenche une dépréciation ; en Symfony 9.0, ce sera une
NotNormalizableValueException. Les listes valides et les propriétés typées avec
d'autres types de tableau ne sont pas concernées. Contribution de Hubert Lenoir,
PR #63971.
Si vos clés portent du sens, ce n'est pas un défaut à corriger côté données : déclarez un type de tableau qui les autorise. Si la propriété représente bien une liste, envoyez un tableau JSON.
Deux ajouts côté TypeInfo
@template-covariantest pris en charge, avec ses variantes préfixées@phpstan-et@psalm-. La nouvelle méthodeType::map()fournit la traversée nécessaire pour remplacer un type template à l'intérieur d'un type plus large (list<T>devientlist<Product>) ; son callback visite les types internes avant ceux qui les contiennent, et seules les parties qui changent sont reconstruites.Type::accepts()répondait par oui ou non, sans dire où ça coinçait.Type::getMismatches()rapporte chaque échec avec le chemin de propriété, le type attendu et le type réel (nullsi une clé ou une propriété manque) ; les clés inattendues sont signalées avecnevercomme type attendu. Cette méthode travaillant plus queaccepts(), elle ne doit être appelée qu'après unaccepts()négatif.
Source : blog Symfony, 27 octobre 2026. Source en anglais.