T: / Corrigés des challenges / PHP
Un bon exercice pour mettre en pratique les principes de la POO en PHP avec quelques fonctions intéressantes comme les variable variable.
Un challenge Tainix est toujours l’occasion de mettre en pratique certains concepts intéressants de la programmation. Dans ce corrigé, on va utiliser des classes pour réaliser un challenge débutant. Mais ce n’est pas tout, on va aussi voir au passage quelques fonctionnalités intéressantes et réaliser des tests unitaires.
Au programme :
Les fonctions et concepts intéressants qui seront passés en revue : PHPDoc, parsing, promotion de propriétés, « variable variale », tri sur mesure, etc.
Pour résoudre ce challenge, il faut commencer par parser les données d’entrée. Elles sont sous ce format :
BEL : Bronze,Bronze,Silver,Gold,Bronze
Je vais mettre le code qui réalise ce parsing dans une classe dédiée, avec une méthode statique. Comme ça, pas besoin d’instancier la classe pour appeler la méthode :
class Parser
{
/**
* @return array{code: string, medals: string[]}
*/
public static function parse(string $informations): array
{
$data = explode(':', $informations);
return [
'code' => trim($data[0]),
'medals' => explode(',', trim($data[1]))
];
}
}
Un peu d’explications :
Je pourrais utiliser cette méthode statique de cette façon :
$data = Parser::parse($informations);
Si tu n’es pas familier avec la fonction explode ou avec le parsing en général, tu peux retrouver notre article dédié au parsing avec PHP.
Les Nations ont plusieurs informations :
On peut commencer à coder cette classe de cette façon :
class Nation
{
private int $nbGold = 0;
private int $nbSilver = 0;
private int $nbBronze = 0;
public function __construct(
public readonly string $code
) {}
}
Un peu d’explications :
Ensuite, j’ai besoin d’une méthode pour ajouter une médaille. Si on reprend la structure des données, les médailles sont représentées par une chaine de caractères : « Bronze, « Silver » ou « Gold ». Ca tombe bien, ça ressemble beaucoup aux noms de mes propriétés…
Je vais pouvoir utiliser une « variable variable » :
public function addMedal(string $medal): void
{
$var = 'nb' . $medal; // Utilisation d'une "variable variable"
$this->$var++;
}
Cette syntaxe me permet de définir le nom de ma variable, puis d’appeler ma variable en utilisant son nom. C’est ce qu’on appelle une « variable variable« . Attention, cette technique est à utiliser dans des cas précis et maitrisés. Utiliser cette technique sans réfléchir peut créer des problèmes de sécurité, un peu comme l’utilisation de eval en PHP.
Enfin, j’ai besoin de 2 autres méthodes :
Voici le code :
public function getScoreTotal(): int
{
return $this->nbGold * 10 + $this->nbSilver * 5 + $this->nbBronze * 2;
}
public function getCodeAndScore(): string
{
return $this->code . '_' . $this->getScoreTotal();
}
On utilise ici les propriétés et la méthode de l’objet, en utilisant $this, soit pour faire des calculs, soit pour faire de la concaténation.
Je crée une dernière classe « Table », qui représente le tableau des médailles avec toutes les nations.
Cette classe va contenir :
Voici le code :
class Table
{
/**
* @var Nation[] $nations
*/
private array $nations = [];
public function addNation(Nation $nation): void
{
$this->nations[] = $nation;
}
public function getBestNation(): Nation
{
usort($this->nations, function (Nation $n1, Nation $n2) {
return $n2->getScoreTotal() <=> $n1->getScoreTotal();
});
return $this->nations[0];
}
}
Un peu d’explications :
Pour rappel, les données du challenge sont stockés dans une variable $table. Voici le code du programme principal :
$tableOfMedals = new Table;
foreach ($table as $informations) {
$data = Parser::parse($informations);
$nation = new Nation($data['code']);
foreach ($data['medals'] as $medal) {
$nation->addMedal($medal);
}
$tableOfMedals->addNation($nation);
}
$result = $tableOfMedals->getBestNation()->getCodeAndScore();
Un peu d’explications :
Ce code pourrait être critiqué à quelques égards :
On va tester pour ce code :
Et voici les tests, avec la librairie de tests unitaires Pest PHP :
/**
* Parser
*/
test('Parsing des données', function() {
$informations = 'BEL : Bronze,Bronze,Silver,Gold,Bronze';
$data = Parser::parse($informations);
expect($data['code'])->toBe('BEL');
expect($data['medals'])->toBe(['Bronze', 'Bronze', 'Silver', 'Gold', 'Bronze']);
});
/**
* Nation
*/
test('Attribution d\'une médaille de bronze', function() {
$nation = new Nation('JPN');
$nation->addMedal('Bronze');
expect($nation->getScoreTotal())->toBe(2);
});
test('Attribution d\'une médaille d\'argent', function() {
$nation = new Nation('ITA');
$nation->addMedal('Silver');
expect($nation->getScoreTotal())->toBe(5);
});
test('Attribution d\'une médaille d\'or', function() {
$nation = new Nation('NOR');
$nation->addMedal('Gold');
expect($nation->getScoreTotal())->toBe(10);
});
test('Attribution d\'une médaille de chaque', function() {
$nation = new Nation('GER');
$nation->addMedal('Bronze');
$nation->addMedal('Silver');
$nation->addMedal('Gold');
expect($nation->getScoreTotal())->toBe(17); // 2 + 5 + 10
});
test('Affichage du code et score d\'une Nation', function() {
$nation = new Nation('CAN');
$nation->addMedal('Bronze');
$nation->addMedal('Bronze');
$nation->addMedal('Silver');
$nation->addMedal('Gold');
expect($nation->getCodeAndScore())->toBe('CAN_19'); // 2 + 2 + 5 + 10
});
/**
* Table
*/
test('Sortie de la meilleure Nation', function() {
$aus = new Nation('AUS');
$aus->addMedal('Bronze');
$swe = new Nation('SWE');
$swe->addMedal('Silver');
$jam = new Nation('JAM');
$jam->addMedal('Gold');
$table = new Table;
$table->addNation($aus);
$table->addNation($swe);
$table->addNation($jam);
expect($table->getBestNation())->toBe($jam);
});
N’hésite pas à choisir un autre challenge de code pour mettre en pratique la POO avec PHP !
Other content to discover