Top Code 2024, les challenges sont de nouveau disponibles dans les boards pour les participant(e)s => Boards

Comment améliorer son code ? 11 approches différentes en PHP

On commence par se passer des if/else et on termine avec des fonctions de callback.

→ Corrigé du Challenge : Pierre-Feuille-Ciseaux

Introduction

Lorsque l’on aborde un langage de programmation, c’est fascinant comme une même problématique peut être codée de plein de façons différentes. Même ici, résoudre le challenge « Pierre – Feuille – Ciseaux » peut se faire de (au moins) 11 façons différentes !

Avec cet article, on va voir ensemble plusieurs façons de coder une même problématique, en utilisant des outils du langage de plus en plus avancés. Cela te permettra de découvrir des techniques pour mieux structurer ton code.

Au programme :

Version 1 : données d’entrée et de sortie, un for, du if/else et plein de variables

// Données d'entrée
$input = 'PFCFFPC';

// Résultat attendu
$result = 'FCPCCFP';

$response = '';

$length = strlen($input);
for ($i = 0; $i < $length; $i++) {

	$play = $input[$i];

	if ($play === 'P') {
		$counterPlay = 'F';
	} elseif ($play === 'F') {
		$counterPlay = 'C';
	} else {
		$counterPlay = 'P';
	}

	$response .= $counterPlay;
}

// Test de la solution trouvée
if ($response === $result) {
	echo 'Challenge OK';
}

Il est important ici de bien effectuer le strlen avant le for. Sinon le strlen va s’effectuer à chaque itération.

Pour les prochains exemples, je ne reprendrais pas à chaque fois la déclaration de $input, $result, ni le test final.


Version 2 : j’enlève des variables et je rajoute un traitement d’erreur

$response = '';

$length = strlen($input);
for ($i = 0; $i < $length; $i++) {

	if ($input[$i] === 'P') {
		$response .= 'F';
	} elseif ($input[$i] === 'F') {
		$response .= 'C';
	} elseif ($input[$i] === 'C') {
		$response .= 'P';
	} else {
		// Erreur... encore un qui veut jouer Puit !
	}
}

Je teste donc bien précisément le cas du « C » et je garde un dernier else pour une éventuelle erreur.

Je fais l’économie de mes variables $play et $counterPlay.


Version 3 : je retire les else grâce au continue

$response = '';

$length = strlen($input);
for ($i = 0; $i < $length; $i++) {

	if ($input[$i] === 'P') {
		$response .= 'F';
		continue;
	}

	if ($input[$i] === 'F') {
		$response .= 'C';
		continue;
	}

	if ($input[$i] === 'C') {
		$response .= 'P';
		continue;
	}

	// Erreur... non, Puit ça n'existe pas !
}

Comme le continue va directement passer à l’itération suivante, plus besoin de else !


Version 4 : je retire les if grâce au switch

$response = '';

$length = strlen($input);
for ($i = 0; $i < $length; $i++) {

	switch ($input[$i]) {
		case 'P':
			$response .= 'F';
		break;

		case 'F':
			$response .= 'C';
		break;

		case 'C':
			$response .= 'P';
		break;

		default:
			// Erreur... il triche le coquin
		break;
	}
}

Le switch se prête très bien à ce genre de situation.

Pour aller + loin, tu peux consulter l’article dédié à se passer des else et if en PHP.


Version 5 : je passe du for au foreach avec str_split

$response = '';

foreach (str_split($input) as $play) {

	switch ($play) {
		case 'P':
			$response .= 'F';
		break;

		case 'F':
			$response .= 'C';
		break;

		case 'C':
			$response .= 'P';
		break;

		default:
			// Erreur... Euh... Lézard ? Sérieux ?
		break;
	}
}

Le str_split peut être mis directement dans le foreach, pas nécessaire de créer une variable avant.


Version 6 : je crée des constantes pour améliorer la lisibilité

define('PIERRE', 'P');
define('FEUILLE', 'F');
define('CISEAUX', 'C');

$response = '';

foreach (str_split($input) as $play) {

	switch ($play) {
		case PIERRE:
			$response .= FEUILLE;
		break;

		case FEUILLE:
			$response .= CISEAUX;
		break;

		case CISEAUX:
			$response .= PIERRE;
		break;

		default:
			// Erreur... Euh... Le Lézard mange la Feuille ?
		break;
	}
}

On a un code qui se lit tout seul, et qui nous protège des fautes de frappe dans les chaînes de caractères.


Version 7 : je crée un tableau de correspondances

$response = '';

$match = [
	PIERRE => FEUILLE,
	FEUILLE => CISEAUX,
	CISEAUX => PIERRE
];

foreach (str_split($input) as $play) {
	$response .= $match[$play];
}

D’un coup on économise quelques lignes ! Plus besoin de switch non plus.


Version 8 : je crée une fonction

$response = '';

foreach (str_split($input) as $play) {
	$response .= counterPlay($play);
}

function counterPlay(string $play): string
{
	$match = [
		PIERRE => FEUILLE,
		FEUILLE => CISEAUX,
		CISEAUX => PIERRE
	];

	if (isset($match[$play])) {
		return $match[$play];
	}

	// Erreur... Quoi ? Spock ? Qu'est ce qu'il vient faire là ?
}

Ma fonction est typée.

J’ai rajouté le test d’existence de la correspondance par rapport à la version précédente.


Version 9 : variante avec l’instruction match :

L’instruction match est disponible à partir de PHP 8.

function counterPlay(string $play): string
{
    return match($play) {
        PIERRE => FEUILLE,
        FEUILLE => CISEAUX,
        CISEAUX => PIERRE,
        default => ''
    };
}

Toutes les infos sur match, qui permet de faire des choses bien + complexes et puissantes que l’exemple ci-dessus.


Version 10 : je me passe du foreach grâce à array_map

$plays = str_split($input);
$counterPlays = array_map('counterPlay', $plays);
$response = implode($counterPlays);

// ou en 1 ligne
$response = implode(array_map('counterPlay', str_split($input)));

Lorsque la fonction implode est appelée avec un seul paramètre, qui est un tableau, alors la « glue » est «  » (chaîne de caractère vide).

Si vous trouvez la version en 1 ligne complexe à lire, préférez une version en 2 ou 3 lignes, avec 1 ou 2 variables intermédiaires.

Si tu n’es pas familier de array_map, n’hésite pas à découvrir d’autres exemples d’utilisation de array_map en PHP.


Version 11 : avec array_reduce et une constante tableau

define('MATCH', [
	PIERRE => FEUILLE,
	FEUILLE => CISEAUX,
	CISEAUX => PIERRE
]);

$response = array_reduce(
	str_split($input),
	'counterPlayReduce',
	'' // Permet de définir $counterPlays à '' initialement
);

function counterPlayReduce(string $counterPlays, string $play): string
{
	if (isset(MATCH[$play])) {
		return $counterPlays . MATCH[$play];
	}

	// Erreur... Donc Spock mange le Lézard ?
}

Il est possible de mettre un tableau dans une constante.

Il faut que je définisse « initial » de array_reduce à «  » sinon il prend null et ne respecte pas le typage de ma fonction.

Conclusion

Chaque technique présentée est voulue ici comme une opportunité d’affiner ta compréhension de PHP. C’est en diversifiant tes approches d’une problématique donnée que tu trouveras des méthodes pour optimiser ton code.

Continue de tester, d’explorer, d’expérimenter pour monter en compétences… Et tu peux le faire tout de suite avec un de nos challenges de code 😉

Pour aller + loin, ce challenge Pierre Feuille Ciseaux a aussi été l’occasion d’une introduction aux tests unitaires avec PHPUnit.


Qui a codé ce superbe contenu ?

Keep learning

Autres contenus à découvrir


Ta newsletter chaque mois

Corrigés, challenges, actualités, veille technique... aucun spam.