Léo a encore frappé. Il a proposé son vieux vélo cassé dans les petites annonces, au prix de -5 € : « Je vous paie pour l'emporter ! » Madame Lou n'a pas ri. Un prix ne doit jamais être négatif, quoi qu'on écrive dans new Annonce(...). Et les annonces à 0 € doivent afficher « Gratuit » plutôt que « 0 € ».
Depuis PHP 8.4, une propriété peut faire elle-même ce travail, avec des hooks : du code qui s'exécute quand on lit (get) ou quand on écrit (set) la propriété.
Votre mission
- Dans le constructeur d'
Annonce, le prix devientpublic private(set) int $prix { set => max(0, $value); }. Il se lit partout, ne se change que dans la classe, et le hooksetremplace un prix négatif par 0 :max(0, $value)renvoie le plus grand des deux nombres, donc 0 quand$valueest négatif. - Ajoutez à
Annonceune propriété virtuellepublic string $prixAffiche, avec un hookgetqui renvoie'Gratuit'pour un prix de 0, et sinon le prix suivi d'une espace et de « € » :'40 €'. - Dans
versHtml(), affichez$this->prixAffichedans<p class="prix">.
La une ne change pas : le vélo de Madame Lou coûte toujours 40 €. Les tests, eux, essaieront l'annonce de Léo.
Pourquoi plus readonly ?
Un hook ne peut pas se poser sur une propriété readonly. private(set) joue un rôle voisin : tout le monde peut lire la propriété (public), mais seule la classe peut l'écrire (private(set)). C'est la visibilité asymétrique, arrivée elle aussi avec PHP 8.4.
Rappel
Un hook set reçoit la valeur qu'on essaie de ranger dans $value, et décide de ce qui est vraiment stocké. Écrit sous la forme courte set => expression;, il range le résultat de l'expression. Un hook get calcule ce qu'on obtient en lisant la propriété. Une propriété qui n'a qu'un get et ne se sert pas d'elle-même ne stocke rien : elle est virtuelle, recalculée à chaque lecture, comme une méthode qu'on lirait sans parenthèses. Avant PHP 8.4, on écrivait pour cela des méthodes getPrix() et setPrix().