PHP 8.4

๐Ÿ—ก๏ธ Exercise 1 โ€” Forge Your Hero

๐Ÿ“– The setup

You're building a character sheet for a tabletop RPG. The rules are strict: HP can never drop below 0 or exceed maxHp, your fullName should update when you change your first or last name, and nobody should be able to cheat XP into your character โ€” only the class itself hands out XP.

Below is the old-school version. Your job is to modernise it with PHP 8.4.

๐Ÿงฑ Starter code

hero.php โ€” starter

<?php

final class Character
{
    public int $hp;
    public int $maxHp = 10;
    public int $xp = 0;

    public function __construct(
        public string $firstName,
        public string $lastName,
        public int $level = 1,
    ) {
        $this->hp = $this->maxHp;
    }

    public function takeDamage(int $dmg): void
    {
        $this->hp = max(0, $this->hp - $dmg);
    }

    public function heal(int $amount): void
    {
        $this->hp = min($this->maxHp, $this->hp + $amount);
    }

    public function gainXp(int $amount): void
    {
        $this->xp += $amount;
        while ($this->xp >= 100) {
            $this->xp -= 100;
            $this->level++;
            $this->maxHp += 5;
        }
    }
}

$hero = new Character('Aria', 'Stormblade');
echo $hero->firstName . ' ' . $hero->lastName . PHP_EOL; // "Aria Stormblade"

$hero->takeDamage(4);
echo "HP: {$hero->hp}/{$hero->maxHp}" . PHP_EOL;      // 6/10

$hero->hp = 9999;     // ๐Ÿ˜ฑ we can just... cheat?
echo "HP: {$hero->hp}" . PHP_EOL;                    // 9999 (bug!)

$hero->xp = 999_999;  // ๐Ÿ˜ฑ and cheat XP too?
echo "Level: {$hero->level}, XP: {$hero->xp}" . PHP_EOL;

๐ŸŽฏ Your challenges

  1. Virtual property. Add a fullName property with a get hook that returns "$firstName $lastName". It should always reflect the current first and last name.
  2. Validated setter. Give hp a set hook that clamps any assignment into the range [0, maxHp]. Once this works, takeDamage() and heal() become one-liners (or delete them โ€” who needs them).
  3. Asymmetric visibility. Make xp public private(set) so it's readable from anywhere but only the class can change it. The final line of the starter should fail with a visibility error.
  4. ๐ŸŽ Bonus. Add an isAlive virtual property (get-only hook) that returns true when hp > 0.
  5. ๐ŸŽ Bonus bonus. Add a level with a set hook that also bumps maxHp by 5 per level gained (clean up gainXp() afterwards).
๐Ÿ’ก Hint โ€” property hook syntax
class Foo
{
    // virtual get-only property
    public string $fullName {
        get => "$this->firstName $this->lastName";
    }

    // set hook with validation
    public int $hp {
        set(int $value) => max(0, min($this->maxHp, $value));
    }

    // asymmetric visibility
    public private(set) int $xp = 0;
}
โœ… Show one possible solution

Answer both questions correctly to unlock the solution.

1. Which snippet correctly adds a set hook that clamps hp between 0 and maxHp?

2. You declare public private(set) int $xp = 0;. Which statement is true?

๐Ÿงช Try to break it: can you still overflow HP? What happens with negative damage? What if maxHp changes while HP is above the new max?
Copied to clipboard!