Comprendre le mécanisme
Une fonction possède une portée locale ; elle ne voit pas spontanément les variables du contexte appelant. Les paramètres sont passés par valeur sauf déclaration explicite par référence. Une closure peut capturer une valeur avec use ; la capture par référence partage au contraire la variable. Une fonction fléchée capture les variables utilisées par valeur. Les paramètres nommés rendent les noms de paramètres importants pour les appels ; une liste variadique recueille des arguments supplémentaires.
Raisonner et vérifier
Documentez les effets de bord et préférez des fonctions dont les dépendances sont explicites. Un générateur suspend son exécution à yield : créer le générateur ne parcourt pas déjà tout le fichier. Son intérêt mémoire disparaît si le consommateur matérialise tous les éléments. Une déclaration de type assure un contrat technique, pas la positivité d’une quantité ni les droits de l’utilisateur.
La portée d’une closure
Une fonction anonyme possède une portée locale. Pour utiliser certaines variables du contexte de création, elle doit les capturer explicitement avec use. Cette déclaration rend visible une dépendance qui serait sinon absente du corps. Une fonction fléchée capture automatiquement les variables utiles par valeur. Lors d’une revue, identifiez le moment de création et celui d’exécution : un callback peut être exécuté bien après la fin du bloc qui l’a construit.
Erreur fréquente à comprendre
Toute variable extérieure est automatiquement locale à la closure.
La portée et la capture suivent des règles explicites.
Capturer une valeur au bon moment
Une capture par valeur retient la valeur de la variable lors de la création de la closure. Si un taux scalaire vaut 4 à cet instant, une réaffectation externe à 9 ne change pas la valeur capturée. C’est utile pour figer un paramètre de calcul. Cette règle ne signifie pas copie profonde : lorsqu’une valeur désigne un objet mutable, les méthodes de cet objet peuvent encore modifier son état partagé.
Erreur fréquente à comprendre
use sans référence suit toujours toute réaffectation ultérieure externe.
La capture ne crée pas cette liaison générale.
Référence et état partagé
Une capture par référence relie la closure à la variable externe. Une réaffectation devient observable à travers cette liaison. Cela peut être voulu pour un compteur local, mais rend le résultat dépendant de l’ordre des appels. Documentez le propriétaire de cet état et sa durée de vie. Une référence n’ajoute ni verrou ni isolation ; dans un environnement où l’objet est réutilisé, elle peut conserver un contexte inattendu.
Erreur fréquente à comprendre
Ajouter une référence rend tout état immuable.
La référence permet des effets partagés.
Les noms deviennent un contrat d’appel
Avec des arguments nommés, l’appelant désigne les paramètres par leur nom. Renommer un paramètre public peut donc casser un usage pourtant inchangé en nombre et en type d’arguments. Avant une modification d’API, recherchez ces appels et appliquez une politique de compatibilité. Le nom doit décrire la responsabilité du paramètre ; il ne faut pas le considérer comme un simple détail interne interchangeable à chaque refactorisation.
Erreur fréquente à comprendre
Renommer un paramètre ne peut affecter aucun appel nommé.
Le nom peut faire partie du contrat consommé.
Variadiques, itérations et générateurs
Un paramètre variadique collecte un nombre variable d’arguments dans une structure accessible au corps de la fonction. Ce mécanisme ne doit pas être confondu avec un générateur. Une fonction utilisant yield produit des valeurs au fil de l’itération, ce qui permet un traitement progressif. Le bénéfice mémoire dépend du consommateur : convertir immédiatement tout le générateur en tableau recrée une accumulation complète. Testez aussi une itération interrompue avant la fin.
Erreur fréquente à comprendre
Le variadique interdit tout nombre variable d’arguments.
C’est précisément son usage.
Le retour exprime une obligation
Un type de retour annonce ce que la fonction doit fournir, mais ne définit pas toutes les propriétés de ce résultat. Retourner un int ne prouve pas une quantité positive ni un stock suffisant. Placez les invariants au niveau où leur sens est connu. Lors d’un test, séparez incompatibilité de type, valeur hors borne et règle métier : ces échecs peuvent demander des messages et traitements différents.
Erreur fréquente à comprendre
Un commentaire remplace toujours le contrat de retour natif.
Les déclarations natives sont vérifiées selon le langage.
Comprendre la durée d’un état local statique
Une variable statique locale conserve son état entre des appels dans le cycle où elle existe. Elle ne se comporte donc pas comme une variable locale ordinaire recréée à chaque entrée. Un cache ou compteur de ce type peut rendre des tests dépendants de leur ordre. Définissez sa portée réelle dans votre modèle d’exécution ; ne supposez pas qu’elle représente un stockage durable et coordonné entre plusieurs processus.
Erreur fréquente à comprendre
Une variable statique locale est forcément réinitialisée à chaque appel.
Son cycle diffère d’une variable locale ordinaire.
Isoler le calcul des effets
Une fonction pure décrit idéalement un résultat à partir de ses entrées, sans lecture ou écriture externe cachée. Un commentaire ne rend pas cette propriété exécutoire. Injectez les dépendances nécessaires et séparez calcul, accès aux données et notification. Vous pourrez alors vérifier le calcul avec des valeurs fixes et tester les effets à une frontière dédiée, sans déclencher un email réel pour chaque combinaison de données.
Erreur fréquente à comprendre
Déclarer une fonction pure dans un commentaire bloque toute écriture PHP.
La propriété demande une implémentation conforme.
Les valeurs par défaut ne dispensent pas de contrat
Un argument par défaut exprime une décision lorsque l’appelant omet une valeur. Cette expression doit suivre les possibilités autorisées par la version du langage. Choisissez une valeur dont le sens est clair : zéro peut signifier une quantité interdite plutôt qu’une absence. Pour un paramètre obligatoire au sens métier, un défaut permissif peut retarder l’erreur et produire un traitement avec des hypothèses jamais confirmées.
Erreur fréquente à comprendre
Toute expression arbitraire est toujours autorisée comme défaut.
La syntaxe et le contexte imposent des contraintes.
Objet référencé ou copie indépendante
Affecter une variable contenant un objet permet généralement de désigner le même objet. Modifier son état par une référence peut donc être visible par l’autre. Une capture de cette valeur ne garantit pas une duplication récursive du graphe. Pour obtenir une indépendance, définissez une stratégie explicite de copie ou d’immuabilité. Vérifiez le cas des objets imbriqués ; une copie superficielle peut encore conserver des références partagées.
Erreur fréquente à comprendre
Affecter un objet duplique récursivement tout son graphe.
Les références peuvent désigner le même objet.
Atelier guidé : lire, expliquer et vérifier le code
<?php
declare(strict_types=1);
$n = 4;
$read = function () use ($n): int { return $n; };
$n = 9;
echo $read(); Le programme crée read lorsque n vaut 4. La clause use conserve cette valeur scalaire dans la closure. La ligne suivante remplace la valeur de la variable externe, mais ne réécrit pas la capture. L’appel affiche donc 4. Il n’y a ni variable absente ni conversion mystérieuse : les deux contextes possèdent simplement des états différents.
Refaites l’expérience avec une capture par référence et observez alors la réaffectation. Remplacez ensuite le nombre par un objet mutable pour distinguer valeur de référence et duplication de l’objet. Écrivez enfin un test qui crée deux callbacks avec deux taux différents et les appelle dans un ordre inversé. Le résultat doit rester cohérent avec la stratégie de capture que vous avez choisie.
Exemple commenté
<?php
declare(strict_types=1);
$rate = 2;
$price = fn (int $amount): int => $amount * $rate;
$rate = 3;
echo $price(5);La fonction fléchée conserve la valeur 2 capturée à sa création et affiche 10. Elle ne suit pas la réaffectation ultérieure de rate.
Exercice de mise en pratique
Implémentez un générateur de lignes CSV qui ignore l’en-tête et ferme le fichier même si le consommateur s’arrête.
Voir une proposition de correction
Ouvrez le fichier dans le générateur, utilisez try/finally autour de la boucle fgetcsv et fermez la ressource dans finally. Testez un fichier vide et un arrêt anticipé.
Points de vérification
- Identifier les mutations partagées.
- Éviter les dépendances globales.
- Mesurer la mémoire du consommateur aussi.
Contrôle de connaissances
Cours associé : Fonctions, portée, closures et générateurs ↗Validation du module
101 questions, objectif pédagogique de 75 %. Durée conseillée de 150 minutes, sans limite imposée. Les questions et les choix sont mélangés ; le corrigé apparaît après validation. Le contrôle rapide de la leçon et l’examen final du cours restent des exercices distincts.
Commencer ou reprendre les 101 questions →Cette marque suit votre propre avancement ; elle ne certifie pas la maîtrise du sujet. Effectuez l’exercice avant de passer à la suite.
