T: / Articles techniques / PHP
Présentation des tableaux en PHP et techniques plus avancées pour les remplir, les tester, les fusionner.
Si tu n’est pas encore familier des tableaux, ou array, en PHP, tu vas découvrir les bases de l’utilisation de cet outil indispensable.
Concepts de base :
Si tu connais déjà tes fondamentaux, regarde du côté de l’initialisation ou de la fusion des tableaux, il y a sans doute des trucs et astuces à découvrir. La section « Aller + loin » peut te donner plus d’informations aussi 😉
Concepts avancés :
On démarre…
Quand on débute la programmation, peu importe le langage, on apprend que dans une variable, on peut stocker une seule information : un nombre, une chaîne de caractères, un booléen (vrai ou faux), etc. Mais on a souvent besoin de manipuler un ensemble d’informations. Et un tableau peut justement stocker plusieurs informations au même endroit. Principalement pour deux raisons :
// Exemple 1
$nombres = [1, 2, 3, 4, 5]; // Nombres entiers
$mots = ['Il', 'était', 'une', 'fois']; // Chaines de caractères
// Exemple 2
$personne = [
'prenom' => 'John',
'nom' => 'Doe',
'age' => 30,
'profession' => 'PHP Expert'
];
Bonne pratique : un tableau associatif peut contenir des éléments de différents types, mais pas un tableau « plat ». On évitera donc des tableaux de ce genre :
$personne = ['John', 'Doe', 30, 'PHP Expert']; // Mélange chaînes de caractères et nombre.
Bonne pratique : dans un tableau associatif les clés sont écrites en snake_case, sans caractère spécial, majuscule ou espace.
Les tableaux de la section précédente sont des tableaux à 1 dimension, car toutes les données sont au même « niveau », à la même « profondeur ». Un tableau peut contenir des nombres, des chaînes de caractères… mais il peut aussi contenir d’autres tableaux !
En reprenant l’exemple précédent :
$personne = [
'prenom' => 'John',
'nom' => 'Doe',
'age' => 30,
'profession' => 'PHP Expert',
'skills' => ['PHP', 'POO', 'HTML', 'CSS']
];
Sur ma clé « skills », la valeur est un tableau, j’ai donc une seconde dimension à mon tableau.
Cela fonctionne aussi pour les tableaux non associatifs :
$morpion = [
['X', 'O', 'X'],
['O', 'X', 'X'],
['X', 'O', 'O'],
];
Pour accéder aux valeurs, je dois utiliser les « index » de mon tableau, entre crochets. Dans un tableau plat, les index sont gérés automatiquement, ce sont des valeurs entières, qui commencent à zéro. Voici des exemples en reprenant les variables déclarées plus haut :
echo $nombres[0]; // Affiche 1
echo $nombres[2]; // Affiche 3
echo $mots[1]; // Affiche "était"
Pour un tableau associatif, l’index correspond à la clé qu’on a définie manuellement.
echo $personne['prenom']; // Affiche "John"
echo $personne['age']; // Affiche 30
Bonne pratique : les tableaux associatifs permettent de bien identifier les données stockées et donc d’avoir un code très lisible. Il est plus facile de comprendre ce que contient $personne[‘prenom’] que $personne[0].
S’il y a plusieurs dimensions, on va cumuler les [] selon la dimension :
echo $profession['skills'][1]; // Affiche "POO"
echo $morpion[1][1]; // Affiche "X"
Dans les exemples précédents, on initialise tout de suite un tableau « rempli ». Mais on peut créer des tableaux vides, puis les remplir ensuite :
$tableau = []; // Tableau vide
On peut ajouter les éléments un par un, de 2 façons :
$tableau[] = 1; // Avec les crochets
array_push($tableau, 1); // Avec la fonction array_push
Pour initialiser un tableau associatif, on va pouvoir créer une clé à la volée de cette façon :
$tableau['nouvelle_cle'] = 1;
Pour mettre à jour une valeur dans un tableau on utilisera l’index correspondant :
$tableau[1] = 2;
$tableau['nouvelle_cle'] = 2;
$morpion[1][1] = 'O'; // Comme ça je gagne ;)
Point d’attention : si la clé n’existe pas, aucune erreur ne sera retounée car PHP va donc créer un nouvel élément dans le tableau (cf. points précédents).
Petite pause…
Si tu débutes en PHP, tu peux t’arrêter là pour le moment et poursuivre avec les boucles. Sinon, tu peux poursuivre avec les concepts plus avancés ci-dessous.
Le moyen le plus rapide de parcourir un tableau en PHP est d’utiliser la structure foreach.
Cette structure, comme son nom l’indique, va permettre de parcourir le tableau « pour chaque » élément de celui-ci. Il n’y a aucun besoin de connaitre la longueur du tableau par exemple, tous les éléments du tableau seront passés en revue, dans l’ordre dans lequel les index sont déjà organisés.
Voici un exemple :
$languages = ['PHP', 'HTML', 'CSS', 'JAVASCRIPT', 'PYTHON'];
foreach ($languages as $language) {
echo '<p>Pour faire du web, on peut utiliser du ' . $language . '</p>';
}
Un peu d’explications :
On peut également récupérer la clé de chaque élément du tableau, en même temps que la valeur de chaque élément.
Voici un exemple :
$informations = [
'prenom' => 'John',
'nom' => 'Doe',
'age' => 30,
'profession' => 'PHP Expert',
];
// J'affiche une liste avec le nom de l'information puis sa valeur
echo '<ul>';
foreach ($informations as $information => $valeur) {
echo '<li>' . $information . ' : ' . $valeur . '</li>';
}
echo '</ul>';
Cela est souvent plus utile lorsqu’on travaille avec un tableau associatif, mais cela fonctionne aussi très bien avec un tableau auto indexé.
Voici pour les bases du foreach, mais il y a encore pas mal de subtilités… N’hésite pas à toutes les découvrir dans notre guide dédié du foreach en PHP.
Un autre moyen de parcourir un tableau, au début moins intuitif, est d’utiliser la fonction array_map qui permet d’appliquer une fonction à tous les éléments d’un tableau.
Tu peux découvrir son fonctionnement et ses subtilités dans notre guide complet de array_map en PHP.
On peut utiliser une boucle, si par exemple je veux un tableau avec les 100 premiers entiers :
$tableau = [];
for ($i = 1; $i <= 100; $i++) {
$tableau[] = $i;
}
// $tableau va contenir [1, 2, 3, ..., 100];
// Attention à bien gérer l'initialisation et la condition d'arrêt :
for ($i = 1; $i <= 100; $i++) {} // => [1, 2, 3, ..., 100] 100 éléments
for ($i = 0; $i <= 100; $i++) {} // => [0, 1, 2, ..., 100] 101 éléments
for ($i = 1; $i < 100; $i++) {} // => [1, 2, 3, ..., 99] 99 éléments
for ($i = 0; $i < 100; $i++) {} // => [0, 1, 2, ..., 99] 100 éléments
On peut imaginer des traitements + complexes dans la boucle.
La même chose peut être réalisé avec la fonction range :
$tableau = range(1, 100);
// Le pas (step) peut également être géré :
$tableau = range(0, 100, 2);
// $tableau va contenir [0, 2, 4, 6, ..., 98, 100]
Il existe 2 autres fonctions qui permettent de remplir un tableau de façon dynamique : array_fill et array_fill_keys :
$tableau = array_fill(
0, // Index de départ
100, // Nombre d'éléments
'OK' // Valeur répétée
);
// $tableau va contenir ['OK', 'OK', 'OK', ... 'OK'] 100 fois, des index 0 à 99
//$keys est un tableau qui contient les clés que je vais utiliser
$keys = ['prenom', 'nom', 'profession'];
// Remplissage de $tableau
$tableau = array_fill_keys(
$keys, // Les clés, index du tableau
'text example' // Valeur répétée
);
// $tableau va contenir ['prenom' => 'text example', ..., 'profession' => 'text example']
Il y a 3 façons de trier un tableau.
On va utiliser les fonctions sort (ordre croissant) ou rsort (ordre décroissant). Le « r » signifie « reverse ». sort et rsort fonctionnent par référence :
$tableau = [3, 5, 1, 7, 2, 4, 6];
sort($tableau); // Et non $tableau = sort($tableau)
// $tableau contiendra [0 => 1, 1 => 2, ..., 6 => 7]
En utilisant sort, les index du tableau sont recréés au moment du tri. Si on veut conserver les index, on utilisera asort ou arsort. Le « a » signifie « associative ».
$tableau = [3, 5, 1, 7, 2, 4, 6];
asort($tableau); // Et non $tableau = asort($tableau)
// $tableau contiendra [2 => 1, 4 => 2, ..., 3 => 7]
On va utiliser les fonctions ksort ou krsort. Le « k » signifie « keys ».
Imaginons que j’ai plusieurs « personnes », structurées comme au début de cet article. Je veux les ordonner selon leur âge. Je dois donc les trier selon une valeur précise, je ne peux donc ni utiliser sort ni ksort, il va falloir que je précise que c’est la clé « age » que je veux utiliser pour mon tri.
Pour cela, on va créer une fonction dédiée, et utiliser un opérateur particulier : <=>
function triSelonAge(array $personne1, array $personne2): int
{
return $personne1['age'] <=> $personne2['age'];
}
L’opérateur <=> va retourner 0, 1 ou -1 :
Une fois cette fonction créée, on va pouvoir l’appeler pour trier notre tableau :
$personnes = [
[
'age' => 28,
...
],
[
'age' => 30,
...
],
[
'age' => 27,
...
],
];
usort($personnes, 'triSelonAge'); // Encore un fonctionnement par référence
On peut aussi créer une fonction, dite anonyme, directement dans usort :
usort($personnes, function($a, $b) {
return $a['age'] <=> $b['age'];
});
On a pour habitude d’utiliser la fonction empty pour vérifier qu’un tableau est vide ou non :
if (empty($tableau)) {
// ...
}
Mais si on regarde la documentation de PHP, on réalise que empty va aussi vérifier si la variable existe ou non, et retourner simplement false si elle n’existe pas. Ce qui peut engendrer des erreurs si jamais le tableau n’a pas été initialisé.
Du coup, c’est une bonne pratique d’utiliser la comparaison stricte par rapport à un tableau vide :
if ($tableau === []) {
// ...
}
ou
if ($tableau !== []) {
// ...
}
On n’y pense pas toujours mais on peut comparer des tableaux plus ou moins complexes entre eux :
if ($tableauComplexe1 === $tableauComplexe2) {
// Il faut que toutes les clés et valeurs soient identiques
}
On peut également faire des choses comme ça :
if ($monTableau === range(1, 5)) {
// Je vérifie que mon tableau contient [1, 2, 3, 4, 5]
}
On a vu + haut comment accéder à la valeur d’un tableau. Mais si je veux accéder à $tableau[‘cle’] mais que l’index « cle » n’existe pas, PHP va déclencher une erreur. Il est donc souvent question de s’assurer qu’un index existe avant de vouloir accéder à la valeur correspondante. Il y a 2 principales possibilités : isset ou array_key_exists, regardons un peu les différences :
// On crée un tableau
$tableau = [
'cle' => 'valeur'
];
isset($tableau['cle']); // => true
array_key_exists('cle', $tableau); // true
isset($tableau['autre_cle']); // => false
array_key_exists('autre_cle', $tableau); // false
Jusque là, tout va bien, les 2 fonctions sont équivalentes. Si la clé existe, elles renvoient true, si elle n’existe pas elles renvoient false.
Maintenant selon les valeurs :
// On crée un tableau, avec une clé, mais dont la valeur est null
$tableau = [
'cle' => null
];
isset($tableau['cle']); // => false
array_key_exists('cle', $tableau); // true
$test = null;
isset($test); // false
Cette fois-ci, les 2 fonctions ne renvoient plus la même chose. isset renvoie en effet false quand il rencontre une valeur null. Alors que array_key_exists renvoie toujours true car la clé existe bien et qu’il ne vérifie pas la valeur contenue.
Une dernière différence selon l’existence ou non du tableau :
// Pas de tableau créé
isset($pasDeTableau['cle']); // false
array_key_exists('cle', $pasDeTableau); // Déclenche une erreur
Le fait que $pasDeTableau n’existe pas ne pose pas de problème à la fonction isset qui va donc renvoyer false. Par contre, array_key_exists a besoin d’un tableau comme paramètre et va donc cette fois-ci déclencher une erreur.
Bonne pratique : Il vaut mieux donc utiliser array_key_exists quand on vérifie la présence d’une clé dans un tableau, car isset peut présenter quelques effets de bord qui pourraient être complexes à détecter.
PHP est parfois critiquer car l’ordre des paramètres d’une fonction change d’une fonction à une autre. C’est notamment vrai dans les fonctions liées aux tableaux. La documentation nomme toujours les paramètres « needle » et « haystack ». « needle » c’est toujours l’aiguille qu’on recherche dans la botte de foin « haystack ».
Comme son nom l’indique, on va rechercher si une valeur est « dans tableau » :
$lettres = ['a', 'b', 'c', 'd'];
in_array('a', $lettres); // => true
in_array('z', $lettres); // => false
Comme son nom l’indique un peu moins, array_search va retourner l’index de la première valeur correspondante trouvée :
$lettres = ['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd'];
array_search('b', $lettres); // => 1
array_search('z', $lettres); // => false
Si la valeur n’existe pas, array_search renverra false.
La fusion de tableaux est un problème qui peut s’avérer plus complexe qu’il n’y paraît. PHP propose nativement 3 outils pour fusionner des tableaux : array_merge, array_merge_recursive et l’opérateur +.
Ce qui peut être complexe c’est qu’il ne se comporte pas de la même façon que l’on travaille sur un tableau « plat » (indexé numériquement) ou un tableau associatif.
Pour des tableaux plats, array_merge et array_merge_recursive vont se comporter de la même façon, en mettant bout à bout les différentes valeurs des tableaux et en reconstruisant les clés numériques de 0 à N… Par exemple :
$nombresA = [0, 1, 2]; // Indexé de 0 à 2
$nombresB = [3, 4, 5]; // Indexé de 0 à 2
array_merge($nombresA, $nombresB); // [0, 1, 2, 3, 4, 5] indexé de 0 à 5
array_merge_recursive($nombresA, $nombresB); // [0, 1, 2, 3, 4, 5] indexé de 0 à 5
// En précisant les index :
$nombresA = [3 => 0, 4 => 1, 5 => 2];
$nombresB = [10 => 3, 11 => 4, 12 => 5];
array_merge($nombresA, $nombresB); // [0, 1, 2, 3, 4, 5] indexé de 0 à 5
array_merge_recursive($nombresA, $nombresB); // [0, 1, 2, 3, 4, 5] indexé de 0 à 5
Ce sont des index numériques et ils sont donc reconstruits.
Avec des tableaux associatifs, ça se complique. En cas de conflit, array_merge va privilégier les données du « dernier » tableau ou du tableau « de droite ». array_merge_recursive va se comporter complètement différemment pour conserver toutes les valeurs rencontrées :
$personneA = [
'prenom' => 'John',
'nom' => 'Doe',
'competence_unique' => 'Sport'
];
$personneB = [
'prenom' => 'Jack',
'nom' => 'Smith',
'competence_speciale' => 'Musique'
];
array_merge($personneA, $personneB);
/*
Array
(
[prenom] => Jack
[nom] => Smith
[competence_unique] => Sport
[competence_speciale] => Musique
)
*/
array_merge_recursive($personneA, $personneB);
/*
Array
(
[prenom] => Array
(
[0] => John
[1] => Jack
)
[nom] => Array
(
[0] => Doe
[1] => Smith
)
[competence_unique] => Sport
[competence_speciale] => Musique
)
*/
Enfin, l’opérateur + va lui toujours comparer les clés et favoriser les valeurs du « premier » tableau, du tableau « de gauche ». On aura donc :
// Index identiques
$nombresA = [0, 1, 2];
$nombresB = [3, 4, 5];
$nombresA + $nombresB; // [0, 1, 2] puisque les index 0, 1, 2 sont communs entre les 2 tableaux, ce sont les valeurs du premier tableau qui sont conservées.
// Index différents
$nombresA = [3 => 0, 4 => 1, 5 => 2];
$nombresB = [10 => 3, 11 => 4, 12 => 5];
$nombresA + $nombresB;
/*
Array
(
[3] => 0
[4] => 1
[5] => 2
[10] => 3
[11] => 4
[12] => 5
)
*/
// Tableau associatif
$personneA + $personneB;
/*
Array
(
[prenom] => John
[nom] => Doe
[competence_unique] => Sport
[competence_speciale] => Musique
)
Pour les conflits d'index, ce sont les valeurs "de gauche" qui sont conservées.
*/
Précision : les exemples ci-dessus sont faits avec 2 tableaux. Mais toutes ces fonctions et opérateur permettent de fusionner autant de tableaux que nécessaire.
Pour des raisons de performances, on évitera au maximum d’avoir un array_merge dans une boucle. Explications et exemple de code :
Merci pour l’organisation 👍Très content d’avoir participé ! On apprend toujours quelque chose quand son code se fait review. Parmi les enseignements du jour, on ne met pas de array_merge dans une boucle ⬇️ #PHP https://t.co/N8mvTuOLzD pic.twitter.com/JYEsnuPENm
— Arthur Weill (@arthurWeill) January 29, 2022
D’autres contenus sur Tainix te permettront de voir d’autres concepts liés aux tableaux en PHP :
Voici quelques challenges qui te feront manipuler des tableaux :
Other content to discover