PHP

10 façons de coder la même chose en PHP

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

→ Corrigé du Challenge : Pierre-Feuille-Ciseaux

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.


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.

Variante de la fonction counterPlay avec l’instruction match (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 9 : 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)));

Si 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.


Version 10 : 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.


Qui a codé ce superbe contenu ?


Ta newsletter chaque mois

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