Terrain 1 — PHP moderne
Micro-défis sur le langage. 🔍 Renfort : Partie 2.
1. Enum de statut ⭐
🎯 But : créer une backed enum. 🕹️ Mission : une enum Priority: int avec Low=1, Medium=2, High=3 et une méthode label(): string. 🔍 Où chercher : 2.3.
✅ Solution
enum Priority: int {
case Low = 1; case Medium = 2; case High = 3;
public function label(): string {
return match($this) { self::Low => 'Basse', self::Medium => 'Moyenne', self::High => 'Haute' };
}
}🔁 Variante : ajoutez Priority::fromLabel('Haute') (statique) qui retrouve le cas.
2. DTO immuable ⭐
🎯 But : promotion + readonly. 🕹️ Mission : un final readonly class Money avec amountCents:int et currency:string, plus withAmount(int): self. 🔍 : 2.5.
✅ Solution
final readonly class Money {
public function __construct(public int $amountCents, public string $currency) {}
public function withAmount(int $amountCents): self { return new self($amountCents, $this->currency); }
}🔁 Variante : ajoutez add(Money $o): self qui refuse (exception) deux devises différentes.
3. Chasse aux faux-amis ⭐⭐
🎯 But : corriger le code d’un dev JS. 🕹️ Mission : corrigez echo "Total: " + $n; if ($u.role == "admin") {}. 🔍 : 2.8.
✅ Solution
echo "Total: " . $n; // . et non +
if ($u->role === 'admin') {} // -> et === strict4. match exhaustif ⭐⭐
🎯 But : remplacer un switch. 🕹️ Mission : convertissez un switch($role) renvoyant un niveau en match. 🔍 : 2.5.
✅ Solution
$level = match($role) { 'admin' => 3, 'provider' => 2, default => 1 };🔁 Variante : faites-le sur une enum Role (sans default, pour l’exhaustivité vérifiée).
5. Nullsafe ⭐⭐
🎯 But : chaîner sans if. 🕹️ Mission : extraire l’email du client d’une réservation possiblement nulle, en une ligne. 🔍 : 2.5.
✅ Solution
$email = $booking?->getCustomer()?->getEmail();6. Générateur paresseux ⭐⭐⭐
🎯 But : mémoire constante. 🕹️ Mission : un générateur readLines(string $path) qui yield chaque ligne d’un fichier. 🔍 : 2.7.
✅ Solution
function readLines(string $path): \Generator {
$h = fopen($path, 'r');
try { while (($l = fgets($h)) !== false) yield rtrim($l); }
finally { fclose($h); }
}7. Attribut personnalisé ⭐⭐⭐
🎯 But : écrire + lire un attribut. 🕹️ Mission : un #[AuditLog(string $action)] applicable aux méthodes, puis le lire par réflexion. 🔍 : 2.4.
✅ Solution
#[Attribute(Attribute::TARGET_METHOD)]
final class AuditLog { public function __construct(public string $action) {} }
$m = new ReflectionMethod(Ctrl::class, 'cancel');
foreach ($m->getAttributes(AuditLog::class) as $a) echo $a->newInstance()->action;8. Enum + interface ⭐⭐⭐
🎯 But : polymorphisme sur enum. 🕹️ Mission : BookingStatus implémente HasColor { color():string }. 🔍 : 2.3.
✅ Solution
interface HasColor { public function color(): string; }
enum BookingStatus: string implements HasColor {
case Pending='pending'; case Confirmed='confirmed'; case Cancelled='cancelled';
public function color(): string {
return match($this){ self::Pending=>'orange', self::Confirmed=>'green', self::Cancelled=>'red' };
}
}9. Traduire du TS ⭐⭐
🎯 But : TS → PHP. 🕹️ Mission : traduisez const active = bookings.filter(b => b.status === 'confirmed').map(b => b.email);. 🔍 : 2.8.
✅ Solution
$confirmed = array_filter($bookings, fn(Booking $b) => $b->status === BookingStatus::Confirmed);
$active = array_map(fn(Booking $b) => $b->email, $confirmed);10. throw comme expression ⭐⭐
🎯 But : concision. 🕹️ Mission : convertir $data['email'] en variable, ou lever si absent, en une ligne. 🔍 : 2.7.
✅ Solution
$email = $data['email'] ?? throw new \InvalidArgumentException('Email requis');Terrain suivant : Console & commandes.