Programmation fonctionnelle en PHP : fonctions d’ordre supérieur

Si vous examinez plusieurs frameworks et applications à grande échelle, vous verrez certainement une fonction d’ordre supérieur à un moment donné. De nombreux langages prennent en charge l’idée de fonctions d’ordre supérieur, notamment JavaScript, Java, .NET, Python et même PHP, pour n’en nommer que quelques-uns.

Mais qu’est-ce qu’une fonction d’ordre supérieur et pourquoi voudrions-nous en utiliser une ? Quels avantages cela nous apporte-t-il et pouvons-nous l’utiliser pour simplifier notre code ? Dans cet article, nous parlerons spécifiquement des fonctions d’ordre supérieur en PHP, mais nous montrerons comment elles ont été utilisées dans d’autres langages à des fins de comparaison.

Fonctions de première classe

Avant de pouvoir entrer dans ce qu’est une fonction d’ordre supérieur, nous devons d’abord comprendre que nous devons avoir un langage qui peut prendre en charge une fonctionnalité appelée fonctions de première classe (ou fonctions de premier ordre). Cela signifie que la langue traite les fonctions comme des citoyens de première classe. En d’autres termes, le langage traite les fonctions comme des variables. Vous pouvez stocker une fonction dans une variable, la transmettre à d’autres fonctions en tant que variable, les renvoyer à partir de fonctions telles que des variables et même les stocker dans des structures de données telles que des tableaux ou des propriétés d’objet, comme vous pouvez le faire avec des variables. La plupart des langues modernes de nos jours ont cette fonctionnalité par défaut. Tout ce que vous avez vraiment besoin de savoir, c’est qu’une fonction peut être transmise et utilisée comme des variables.

Pour nos besoins, nous nous concentrerons principalement sur le passage de fonctions en tant qu’arguments et le retour de fonctions en tant que résultats, et aborderons brièvement le concept de variables non locales et de fermetures. (Vous pouvez en savoir plus sur ces concepts dans les sections 1.1, 1.4 et 1.3 de l’article de Wikipédia auquel était lié le paragraphe précédent.)

Que sont les fonctions d’ordre supérieur ?

Deux caractéristiques principales identifient une fonction d’ordre supérieur. Une fonction d’ordre supérieur peut implémenter une ou les deux idées suivantes : une fonction qui prend une ou plusieurs fonctions en entrée ou renvoie une fonction en sortie. En PHP, il existe un mot-clé qui indique clairement qu’une fonction est d’ordre supérieur : le mot-clé callable. Bien que ce mot-clé n’ait pas à être présent, le mot-clé permet de les identifier facilement. Si vous voyez une fonction ou une méthode qui a un paramètre appelable, cela signifie qu’elle prend une fonction en entrée. Un autre signe facile est si vous voyez une fonction renvoyer une fonction en utilisant son return déclaration. L’instruction return peut être simplement le nom de la fonction, ou peut même être une fonction anonyme/en ligne. Vous trouverez ci-dessous quelques exemples de chaque type.


function echoHelloWorld() {
  echo "Hello World!";
}


function higherOrderFunction(callable $func) {
  $func();
}




higherOrderFunction('echoHelloWorld'); 

Voici quelques exemples simples de fonctions d’ordre supérieur qui renvoient une fonction :


function trimMessage1() {
  return 'trim';
}


function trimMessage2() {
    return function($text) {
        return trim($text);
    };
}


$trim1 = trimMessage1(); 


echo $trim1('  hello world  ');


$trim2 = trimMessage1(); 


echo $trim2('  hello world  ');

Comme vous pouvez l’imaginer, vous pouvez intégrer une fonction et l’utiliser également pour générer une autre fonction renvoyée. Jolie astuce, non ? Mais pourquoi voudriez-vous faire tout cela ? Cela ressemble à quelque chose qui rendrait simplement les choses plus compliquées.

Pourquoi utiliseriez-vous ou créeriez-vous une fonction d’ordre supérieur ?

Il existe plusieurs raisons pour lesquelles vous pourriez souhaiter créer des fonctions d’ordre supérieur dans votre code. Tout, depuis la flexibilité du code, la réutilisation du code, l’extension du code ou pour imiter une solution de code que vous avez vue dans un autre programme. Bien que les raisons soient nombreuses, nous en aborderons quelques-unes ici.

Ajout de la flexibilité du code

Les fonctions d’ordre supérieur ajoutent une tonne de flexibilité. Sur la base des exemples précédents, vous pouvez probablement voir quelques utilisations pour quelque chose comme ça. Vous pouvez écrire une seule fonction qui prend toute une suite de fonctions différentes et les utilise sans avoir à écrire de code pour les exécuter individuellement.

Peut-être que la fonction d’ordre supérieur elle-même ne sait pas quel type de fonction elle recevra. Qui a dit que la fonction devait savoir quoi que ce soit sur la fonction qu’elle intègre ? Une minute, la fonction d’entrée pourrait être un add() fonction, et dans le prochain ce pourrait être un divide() fonction. Dans les deux cas, ça marche.

Développez facilement votre code

Cette fonctionnalité facilite également l’ajout ultérieur de fonctions d’entrée supplémentaires. Disons que vous avez add() et divide()mais plus tard, vous devez ajouter un sum() fonction. Vous pouvez écrire le sum() fonction et dirigez-la vers la fonction d’ordre supérieur sans jamais avoir à la modifier. Dans un exemple ultérieur, nous montrerons comment nous pouvons utiliser cette fonctionnalité pour créer notre propre calc() fonction.

Imiter les caractéristiques d’une autre langue

Une autre raison pour laquelle vous pourriez vouloir utiliser une fonction d’ordre supérieur en PHP est de simuler le comportement de quelque chose comme un décorateur en Python. Vous “enveloppez” une fonction dans une autre fonction pour modifier le comportement de cette fonction. Par exemple, vous pouvez écrire une fonction qui multiplie par d’autres fonctions. Vous écrivez une fonction d’ordre supérieur qui prend une fonction, démarre une minuterie, appelle la fonction, puis termine la minuterie pour voir combien de temps s’est écoulé.

Exemples de fonctions d’ordre supérieur existantes en PHP

Il est très simple de trouver des exemples de fonctions d’ordre supérieur en PHP. Regardez la documentation PHP et trouvez une fonction/méthode qui prend un callable paramètre d’entrée et vous en avez trouvé un. Vous trouverez ci-dessous quelques exemples de fonctions d’ordre supérieur couramment utilisées. Vous les avez peut-être même utilisées sans jamais savoir qu’il s’agissait de fonctions d’ordre supérieur.

Le duo dynamique d’ordre supérieur : array_map et array_filter

$arrayOfNums = [1,2,3,4,5];


$doubledNums = array_map(function($num) {
  return $num * 2;
}, $arrayOfNums);

var_dump($doubledNums); 

Voyons comment en utiliser une autre, cette fois avec une fonction de filtrage que nous avons définie séparément :

$arrayOfNums = [1,2,3,4,5];


function isEven($num) {
  return ($num % 2) === 0;
}


$evenNums = array_filter($arrayOfNums, 'isEven');

var_dump($evenNums); 

array_filter et array_map sont deux fonctions d’ordre supérieur très populaires que vous trouverez dans de nombreux projets de code. Un autre que vous pourriez utiliser est call_user_func, qui prend une fonction et une liste d’arguments et appelle cette fonction. Il s’agit essentiellement d’une fonction d’ordre supérieur personnalisée. Tout son but est d’appeler d’autres fonctions que l’utilisateur a définies :

function printCustomMessage($message) {
  echo "$message";
}

call_user_func('printCustomMessage', 'Called custom message through the use of call_user_func!');

Comment créer vos propres fonctions d’ordre supérieur

Nous avons montré de nombreux exemples du fonctionnement des fonctions d’ordre supérieur en PHP, et nous en avons déjà créé quelques-unes personnalisées pour illustrer leurs différents objectifs. Mais montrons un peu plus la flexibilité avec une nouvelle fonction que nous appellerons calc(). Cette fonction prendra deux arguments et une fonction qui opérera sur ces deux arguments pour nous donner un résultat :

function add($a, $b) {
  return $a + $b;
}

function subtract($a, $b) {
  return $a - $b;
}

function multiply($a, $b) {
  return $a * $b;
}


function calc($n1, $n2, $math_func) {
  return $math_func($n1, $n2);
}

$addedNums = calc(1, 5, 'add');
$subtractedNums = calc(1, 5, 'subtract');
$multipliedNums = calc(1, 5, 'multiply');


echo "Added numbers: $addedNumsn";


echo "Subtracted numbers: $subtractedNumsn";


echo "Multiplied numbers: $multipliedNumsn";

Remarquez que notre calc() la fonction a été écrite de manière très générique pour prendre en compte un ensemble d’autres fonctions et les appelle avec des paramètres $n1 et $n2. Dans ce cas, notre fonction de calcul ne se soucie pas de ce que fait la fonction qu’elle prend. Il transmet simplement les paramètres à la fonction et renvoie le résultat.

Mais attendez une minute : notre patron vient de nous demander d’ajouter une fonction pour ajouter deux chaînes ensemble dans notre système existant. Mais la concaténation de chaînes n’est pas votre opération mathématique habituelle. Cependant, avec notre configuration ici, nous avons juste besoin d’ajouter la concaténation de chaînes et de la diriger à travers calc():

function addTwoStrings($a, $b) {
  return $a . " " . $b;
}


$concatenatedStrings = calc('Hello', 'World!', 'addTwoStrings');

Bien que cet exemple soit très artificiel, il montre comment vous pouvez utiliser le concept dans des projets plus importants. Peut-être que la fonction d’ordre supérieur détermine la manière dont les événements sont gérés. Peut-être, en fonction de l’événement déclenché, vous pouvez l’acheminer via une fonction de gestionnaire que vous transmettez. Les possibilités sont infinies.

Comment cela se compare-t-il aux autres langages qui ont des fonctions d’ordre supérieur ? Prenons un simple exemple de décorateur en Python et comparons-le à JavaScript, puis comparons-le à PHP. De cette façon, si vous connaissez Python ou JS, vous pouvez voir en quoi c’est équivalent.

Une comparaison côte à côte avec Python et JavaScript

def say_message(func):
    def wrapper():
        print('Print before function is called')
        func()
        print('Print after function is called')
    return wrapper

def say_hello_world():
    print("Hello World!")




say_hello_world = say_message(say_hello_world)


say_hello_world()





Voyons maintenant comment cela pourrait être réalisé dans les fonctions JavaScript d’ordre supérieur :


function say_message(func) {
  return function() {
      console.log("Print before function is called");
      func();
      console.log("Print after function is called");
  }
}

function say_hello_world() {
  console.log("Hello World!");
}



say_hello = say_message(say_hello_world);


say_hello();





Enfin, comparons ceci avec PHP :


function say_message($func) {
  
  return function() use ($func) {
      echo "Print before function is called";
      $func();
      echo "Print after function is called";
  };
}

function say_hello_world() {
  echo "Hello World!";
}



$say_hello = say_message('say_hello_world');


$say_hello();





Comme vous pouvez le voir dans la comparaison côte à côte, la version PHP est très similaire à la syntaxe JavaScript. Mais si vous connaissez un peu Python et les décorateurs, vous pouvez voir comment les décorateurs peuvent être traduits dans l’un des deux autres langages de programmation.

Un mot rapide sur les Lambdas/Fonctions anonymes

Souvent, lorsque vous travaillez avec des fonctions d’ordre supérieur, vous pouvez voir l’utilisation de fonctions anonymes en ligne (alias lambdas). Dans certains des exemples que nous avons vus ci-dessus, j’ai utilisé ce format. En plus de cela, j’ai également utilisé la version plus explicite consistant à définir d’abord la fonction, puis à la transmettre à notre fonction d’ordre supérieur. C’est pour que vous vous habituiez à voir les deux versions. Le plus souvent, vous verrez la version lambda en ligne dans les frameworks qui utilisent beaucoup les fonctions d’ordre supérieur. Ne laissez pas ce format vous décourager. Ils créent simplement une fonction simple, sans nom, et l’utilisent à la place de l’endroit où vous auriez donné le nom de la fonction autonome.

Conclusion

Dans cet article, nous couvrons la définition de base de ce qui fait d’une fonction une fonction d’ordre supérieur. Toute fonction qui prend une fonction ou renvoie une fonction (ou les deux) peut être considérée comme une fonction d’ordre supérieur. Nous avons également expliqué pourquoi vous pourriez vouloir créer ces types de fonctions et quels sont leurs avantages.

Nous avons ensuite montré quelques exemples de fonctions d’ordre supérieur existantes en PHP, comme array_map et array_filterpuis a créé notre propre fonction d’ordre supérieur appelée calc(), qui prenait plusieurs fonctions d’entrée et fonctionnait sur quelques arguments. Nous avons ensuite procédé à une comparaison côte à côte, montrant à quoi ressemble la solution PHP par rapport à un décorateur Python et à une implémentation JavaScript.
J’espère que vous en avez appris un peu plus sur les fonctions d’ordre supérieur et pourquoi vous voudrez peut-être les prendre en compte dans votre prochaine solution à grande échelle. Ils peuvent offrir de nombreux avantages et réduire le code passe-partout qui peut être redondant.

Si vous souhaitez en savoir plus sur la programmation fonctionnelle en PHP, vous pouvez consulter notre tutoriel.

Merci d’avoir lu!

Leave a Comment