A price that holds up

Exercise 7 of 9 in chapter 2 · 150 XP · 5 objectives checked by tests

Topics covered: Property hookprivate(set)Virtual property

Léo has struck again. He listed his old broken bike in the classified ads, at a price of -€5: “I'll pay you to take it away!” Madame Lou did not laugh. A price must never be negative, whatever is written in new Annonce(...). And ads at €0 should show “Free” rather than “€0”.

Since PHP 8.4, a property can do this job itself, with hooks: code that runs when the property is read (get) or written (set).

Your mission

  1. In the constructor of Annonce, the price becomes public private(set) int $prix { set => max(0, $value); }. It can be read anywhere, changed only inside the class, and the set hook replaces a negative price with 0: max(0, $value) returns the larger of the two numbers, so 0 when $value is negative.
  2. Add to Annonce a virtual property public string $prixAffiche (displayed price), with a get hook that returns 'Free' for a price of 0, and otherwise “€” followed by the price: '€40'.
  3. In versHtml(), display $this->prixAffiche in <p class="prix">.

The front page does not change: Madame Lou's bike still costs €40. The tests, on the other hand, will try Léo's ad.

Why not readonly anymore?

A hook cannot be put on a readonly property. private(set) plays a similar role: everyone can read the property (public), but only the class can write it (private(set)). That is asymmetric visibility, which also arrived with PHP 8.4.

Reminder

A set hook receives the value someone is trying to store in $value, and decides what actually gets stored. Written in the short form set => expression;, it stores the result of the expression. A get hook computes what you get when you read the property. A property that only has a get and never uses its own value stores nothing: it is virtual, recomputed on every read, like a method you read without parentheses. Before PHP 8.4, you wrote getPrix() and setPrix() methods for this.