Chapitre 319 minIntermédiaire
Tableaux et fonctions en PHP
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- créer et parcourir des tableaux indexés et associatifs ;
- filtrer, transformer et trier un tableau sans boucle inutile ;
- écrire des fonctions typées avec des arguments nommés ;
- utiliser les fonctions anonymes et fléchées.
3 exercices corrigés
Un tableau PHP (array) range plusieurs valeurs sous une seule variable, chacune repérée par une clé : un numéro pour une liste, un nom pour un tableau associatif. Une fonction regroupe des instructions sous un nom, avec des paramètres typés et une valeur de retour. Ce chapitre montre comment créer, parcourir, trier et filtrer des tableaux, puis comment écrire des fonctions propres en PHP 8.5. Ce sont les deux outils que tu manipuleras en permanence : les lignes lues dans la base arrivent sous forme de tableaux.
Listes et tableaux associatifs
Un tableau indexé (une liste) a des clés numériques qui commencent à 0. Un tableau associatif a des clés de type chaîne, choisies par toi. En PHP, c'est le même type array.
<?php
// Tableau indexé : clés 0, 1, 2
$references = ['PC-0101', 'PC-0102', 'VP-0001'];
echo $references[0]; // PC-0101
$references[] = 'TAB-0003'; // ajoute à la fin, avec la clé 3
echo count($references); // 4
// Tableau associatif : une ligne de la table materiel
$materiel = [
'id' => 1,
'reference' => 'PC-0101',
'libelle' => 'Portable Dell Latitude 5450',
'valeur' => 1180.0,
];
echo $materiel['libelle']; // Portable Dell Latitude 5450
$materiel['etat'] = 'disponible'; // ajoute une clé
unset($materiel['valeur']); // supprime une clé
Lire une clé qui n'existe pas affiche l'avertissement « Undefined array key » et renvoie null. Teste avant avec isset($tab['cle']), ou donne une valeur par défaut avec $tab['cle'] ?? 'défaut'.
Un tableau peut contenir d'autres tableaux. Le résultat d'une requête SQL, par exemple, est une liste de tableaux associatifs : une ligne par élément, une clé par colonne. C'est la forme que tu verras partout dans la suite du cours.
<?php
$materiels = [
['id' => 1, 'reference' => 'PC-0101', 'categorie' => 'portable', 'valeur' => 1180.0, 'disponible' => true],
['id' => 3, 'reference' => 'VP-0001', 'categorie' => 'videoprojecteur', 'valeur' => 899.0, 'disponible' => true],
['id' => 2, 'reference' => 'PC-0102', 'categorie' => 'portable', 'valeur' => 1180.0, 'disponible' => false],
['id' => 4, 'reference' => 'TAB-0003', 'categorie' => 'tablette', 'valeur' => 449.9, 'disponible' => false],
];
echo $materiels[2]['reference']; // PC-0102 : 3e ligne, colonne reference
Parcourir un tableau avec foreach
foreach parcourt chaque élément. La forme $cle => $valeur donne aussi la clé.
<?php
$materiel = ['reference' => 'PC-0101', 'categorie' => 'portable', 'valeur' => 1180.0];
foreach ($materiel as $colonne => $valeur) {
echo "$colonne : $valeur\n";
}
// reference : PC-0101
// categorie : portable
// valeur : 1180
Pour une liste de lignes, une boucle suffit à produire un tableau HTML :
<?php $materiels = [['reference' => 'PC-0101', 'categorie' => 'portable'], ['reference' => 'VP-0001', 'categorie' => 'videoprojecteur']]; ?>
<table>
<tr><th>Référence</th><th>Catégorie</th></tr>
<?php foreach ($materiels as $m): ?>
<tr>
<td><?= htmlspecialchars($m['reference']) ?></td>
<td><?= htmlspecialchars($m['categorie']) ?></td>
</tr>
<?php endforeach; ?>
</table>
Chercher dans un tableau
| Besoin | Fonction | Exemple |
|---|---|---|
| La clé existe-t-elle ? | array_key_exists(), isset() | isset($m['photo']) est faux si la clé vaut null ; array_key_exists('photo', $m) est vrai |
| La valeur est-elle présente ? | in_array($v, $tab, true) | in_array($categorie, CATEGORIES, true) |
| À quelle clé est cette valeur ? | array_search($v, $tab, true) | array_search('VP-0001', $references, true) donne 1 |
| Le premier élément qui vérifie une condition | array_find() (PHP 8.4) | array_find($materiels, fn($m) => !$m['disponible']) |
| Au moins un élément vérifie-t-il la condition ? | array_any(), array_all() (PHP 8.4) | array_any($materiels, fn($m) => $m['categorie'] === 'camera') |
| Le premier ou le dernier élément | array_first(), array_last() (PHP 8.5) | array_first($materiels) |
Transformer, filtrer et trier
PHP fournit des fonctions qui évitent d'écrire des boucles à la main. Elles prennent souvent une fonction fléchée (fn) en paramètre, présentée plus bas.
<?php
$materiels = [
['id' => 1, 'reference' => 'PC-0101', 'categorie' => 'portable', 'valeur' => 1180.0, 'disponible' => true],
['id' => 3, 'reference' => 'VP-0001', 'categorie' => 'videoprojecteur', 'valeur' => 899.0, 'disponible' => true],
['id' => 2, 'reference' => 'PC-0102', 'categorie' => 'portable', 'valeur' => 1180.0, 'disponible' => false],
['id' => 4, 'reference' => 'TAB-0003', 'categorie' => 'tablette', 'valeur' => 449.9, 'disponible' => false],
];
// Extraire une colonne
$refs = array_column($materiels, 'reference'); // ['PC-0101', 'VP-0001', 'PC-0102', 'TAB-0003']
$refsParId = array_column($materiels, 'reference', 'id'); // [1 => 'PC-0101', 3 => 'VP-0001', 2 => 'PC-0102', 4 => 'TAB-0003']
// Filtrer : garder les éléments pour lesquels la fonction renvoie true
$disponibles = array_filter($materiels, fn(array $m): bool => $m['disponible']);
// Transformer chaque élément
$libelles = array_map(fn(array $m): string => $m['reference'] . ' (' . $m['categorie'] . ')', $materiels);
// Réduire à une seule valeur
$valeurTotale = array_sum(array_column($materiels, 'valeur')); // 3708.9
// Trier : du plus cher au moins cher, puis par référence
usort($materiels, fn(array $a, array $b): int
=> $b['valeur'] <=> $a['valeur'] ?: $a['reference'] <=> $b['reference']);
// PC-0101, PC-0102, VP-0001, TAB-0003
Quelques détails qui piègent :
array_filter()conserve les clés d'origine :$disponiblesa les clés 0 et 1 ici, mais si le premier élément avait été filtré, la liste commencerait à 1. Passe le résultat dansarray_values()si tu as besoin d'une liste numérotée à partir de 0 (pour la convertir en tableau JSON, par exemple).sort(),usort()et leurs variantes trient le tableau lui-même (il est passé par référence) et renvoienttrue, pas le tableau trié. N'écris pas$tries = sort($liste);.- L'opérateur
<=>renvoie -1, 0 ou 1 : c'est exactement ce qu'attend la fonction de comparaison deusort().?:passe au critère suivant quand le premier donne 0 (égalité).
| Fonction | Trie selon | Garde les clés ? |
|---|---|---|
sort() / rsort() | les valeurs, croissant / décroissant | non |
asort() / arsort() | les valeurs | oui |
ksort() / krsort() | les clés | oui |
usort() | ta fonction de comparaison | non |
uasort() | ta fonction de comparaison | oui |
Pour compter par catégorie, la boucle classique reste la plus lisible :
<?php
$materiels = [['categorie' => 'portable'], ['categorie' => 'videoprojecteur'], ['categorie' => 'portable'], ['categorie' => 'tablette']];
$parCategorie = [];
foreach ($materiels as $m) {
$parCategorie[$m['categorie']] = ($parCategorie[$m['categorie']] ?? 0) + 1;
}
// ['portable' => 2, 'videoprojecteur' => 1, 'tablette' => 1]
Si les données viennent de la base, fais plutôt ce calcul en SQL avec GROUP BY (cours SQL) : la base est faite pour ça, et tu ne transfères que le résultat.
Décomposer et fusionner
<?php
// Décomposer une liste
[$prefixe, $numero] = explode('-', 'PC-0101'); // 'PC' et '0101'
// Décomposer un tableau associatif
$materiel = ['reference' => 'PC-0101', 'valeur' => 1180.0];
['reference' => $ref, 'valeur' => $val] = $materiel;
// Fusionner : l'opérateur ... (spread) et array_merge()
$defauts = ['etat' => 'disponible', 'photo' => null];
$saisie = ['reference' => 'CAM-0002', 'photo' => 'cam-0002.jpg'];
$materiel = [...$defauts, ...$saisie];
// ['etat' => 'disponible', 'photo' => 'cam-0002.jpg', 'reference' => 'CAM-0002']
Avec des clés chaînes, la valeur de droite remplace celle de gauche. L'opérateur + fait l'inverse : $a + $b garde la valeur de $a quand la clé existe des deux côtés.
Écrire une fonction
Une fonction se déclare avec function, un nom en camelCase, des paramètres typés et un type de retour :
<?php
declare(strict_types=1);
function creerReference(string $categorie, int $numero, int $largeur = 4): string
{
$prefixe = match ($categorie) {
'portable' => 'PC',
'videoprojecteur' => 'VP',
'tablette' => 'TAB',
'camera' => 'CAM',
default => 'DIV',
};
return sprintf('%s-%0' . $largeur . 'd', $prefixe, $numero);
}
echo creerReference('portable', 42); // PC-0042
echo creerReference(numero: 7, categorie: 'camera'); // CAM-0007 : arguments nommés
echo creerReference('autre', 5, largeur: 3); // DIV-005
Ce que tu peux déclarer :
| Élément | Syntaxe | Exemple |
|---|---|---|
| Type simple | int, float, string, bool, array | int $numero |
| Type nullable | ?type | ?string $photo accepte une chaîne ou null |
| Type union | type1|type2 | int|float $valeur |
| Valeur par défaut | = valeur | int $largeur = 4 |
| Aucun retour | : void | function journaliser(string $msg): void |
| Ne revient jamais | : never | une fonction qui fait toujours exit ou lève une exception |
| Nombre variable d'arguments | type ...$nom | float ...$valeurs reçoit un tableau |
Les arguments nommés (numero: 7) rendent les appels lisibles et permettent de sauter les paramètres qui ont une valeur par défaut. Une fonction ne voit que ses paramètres et ses variables locales : une variable définie à l'extérieur n'est pas accessible à l'intérieur. N'utilise pas le mot-clé global pour contourner cela : passe la valeur en paramètre.
Par défaut, un tableau passé à une fonction est copié : la fonction travaille sur sa copie. Pour qu'elle modifie le tableau d'origine, déclare le paramètre par référence avec & (function ajouter(array &$liste, string $ref): void). Réserve cette possibilité aux cas où elle est vraiment utile : une fonction qui renvoie une nouvelle valeur est plus facile à comprendre et à tester.
Fonctions anonymes, fléchées et callables
Une fonction anonyme (ou closure) n'a pas de nom ; on la range dans une variable ou on la passe en argument. Pour utiliser une variable extérieure, elle doit la capturer avec use. La fonction fléchée fn, plus courte, capture automatiquement les variables extérieures et ne contient qu'une expression.
<?php
$tva = 0.2;
$ttc = function (float $ht) use ($tva): float {
return round($ht * (1 + $tva), 2);
};
$ttcFleche = fn(float $ht): float => round($ht * (1 + $tva), 2);
echo $ttc(100.0); // 120
echo $ttcFleche(100.0); // 120
Les deux capturent la valeur de $tva au moment de leur création : si $tva change ensuite, elles gardent l'ancienne.
La syntaxe nomDeFonction(...) crée un callable à partir d'une fonction existante : array_map(strtoupper(...), $refs) met chaque référence en majuscules.
PHP 8.5 ajoute l'opérateur pipe |>, qui passe une valeur d'une fonction à la suivante, de gauche à droite :
<?php
// PHP 8.5
$identifiant = ' Vidéoprojecteur Epson '
|> trim(...)
|> mb_strtolower(...)
|> (fn(string $s): string => str_replace(' ', '-', $s));
// 'vidéoprojecteur-epson'
C'est l'équivalent de str_replace(' ', '-', mb_strtolower(trim($texte))), en plus lisible. Si tu travailles en PHP 8.4, garde la forme imbriquée.
Exercices
Exercice 1 sur 3 Manipuler la liste du matériel Réussi À revoir
À partir du tableau $materiels de la section Transformer, filtrer et trier, écris une instruction (ou une courte boucle) pour obtenir :
- la liste des références des matériels prêtés (non disponibles), numérotée à partir de 0 ;
- la valeur totale du matériel disponible ;
- un tableau
[référence => valeur]; - le libellé « 2 portable(s), 1 videoprojecteur(s), 1 tablette(s) », dans l'ordre d'apparition des catégories.
Voir la réponseMasquer la réponse de l'exercice 1
<?php
// 1. ['PC-0102', 'TAB-0003']
$pretes = array_values(array_column(
array_filter($materiels, fn(array $m): bool => !$m['disponible']),
'reference'
));
// 2. 2079 (1180 + 899)
$valeurDispo = array_sum(array_column(
array_filter($materiels, fn(array $m): bool => $m['disponible']),
'valeur'
));
// 3. ['PC-0101' => 1180.0, 'VP-0001' => 899.0, 'PC-0102' => 1180.0, 'TAB-0003' => 449.9]
$valeurParRef = array_column($materiels, 'valeur', 'reference');
// 4.
$parCategorie = [];
foreach ($materiels as $m) {
$parCategorie[$m['categorie']] = ($parCategorie[$m['categorie']] ?? 0) + 1;
}
$morceaux = [];
foreach ($parCategorie as $categorie => $nb) {
$morceaux[] = "$nb $categorie(s)";
}
echo implode(', ', $morceaux);
Pour la question 1, array_column() renvoie déjà une liste numérotée à partir de 0 ; le array_values() est une précaution, sans effet ici. array_column() accepte un troisième argument, la colonne qui sert de clé : c'est la question 3.
Tu avais trouvé ?
Exercice 2 sur 3 Écrire une fonction typée Réussi À revoir
Écris la fonction dureePret(string $categorie, bool $prioritaire = false): int qui renvoie la durée maximale d'un prêt en jours : 14 pour un portable ou une tablette, 3 pour un vidéoprojecteur ou une caméra, 7 sinon. Un emprunteur prioritaire (un commercial en déplacement, par exemple) a le double. Appelle-la avec un argument nommé pour un vidéoprojecteur prioritaire.
Voir la réponseMasquer la réponse de l'exercice 2
<?php
declare(strict_types=1);
function dureePret(string $categorie, bool $prioritaire = false): int
{
$jours = match ($categorie) {
'portable', 'tablette' => 14,
'videoprojecteur', 'camera' => 3,
default => 7,
};
return $prioritaire ? $jours * 2 : $jours;
}
echo dureePret('videoprojecteur', prioritaire: true); // 6
La fonction ne fait qu'un calcul et renvoie un int : elle ne lit ni la base ni la session, ce qui la rend facile à tester avec quelques valeurs (portable, caméra, une catégorie inconnue, avec et sans priorité).
Tu avais trouvé ?
Exercice 3 sur 3 Trier correctement Réussi À revoir
Ce code doit afficher les matériels du moins cher au plus cher. Il affiche un avertissement d'obsolescence, « 1 », puis un avertissement sur le foreach, et aucune référence. Corrige-le.
<?php
$tries = usort($materiels, fn($a, $b) => $a['valeur'] > $b['valeur']);
echo $tries;
foreach ($tries as $m) {
echo $m['reference'] . "\n";
}
Voir la réponseMasquer la réponse de l'exercice 3
Deux erreurs. usort() trie $materiels sur place et renvoie true (affiché « 1 ») : $tries n'est pas un tableau, d'où l'avertissement « foreach() argument must be of type array|object, true given » : la boucle ne s'exécute pas. Et la fonction de comparaison doit renvoyer un entier négatif, nul ou positif, pas un booléen : depuis PHP 8, un booléen est accepté mais signalé comme obsolète (deprecated), et le tri peut être faux.
<?php
usort($materiels, fn(array $a, array $b): int => $a['valeur'] <=> $b['valeur']);
foreach ($materiels as $m) {
echo $m['reference'] . "\n";
}
// TAB-0003, VP-0001, puis PC-0101 et PC-0102
Les deux portables ont la même valeur : le tri de PHP est stable depuis la version 8.0, ils restent dans leur ordre d'origine.
Tu avais trouvé ?