87 votes

À quoi servent les fonctions imbriquées de PHP ?

En JavaScript, les fonctions imbriquées sont très utiles : fermetures, méthodes privées et autres

À quoi servent les fonctions PHP imbriquées ? Quelqu'un les utilise-t-il et à quoi servent-elles ?

Voici une petite enquête que j'ai menée

<?php
function outer( $msg ) {
    function inner( $msg ) {
        echo 'inner: '.$msg.' ';
    }
    echo 'outer: '.$msg.' ';
    inner( $msg );
}

inner( 'test1' );  // Fatal error:  Call to undefined function inner()
outer( 'test2' );  // outer: test2 inner: test2
inner( 'test3' );  // inner: test3
outer( 'test4' );  // Fatal error:  Cannot redeclare inner()

1 votes

J'aurais pu jurer avoir lu que le support pour cela avait été abandonné dans PHP6 mais je ne le trouve nulle part.

2 votes

@greg Je pensais que le plan pour PHP6 était en suspens de toute façon ?

0 votes

Ils sont parfaits pour les grandes fonctions - une sorte d'organisation récursive.

93voto

user641463 Points 281

Si vous utilisez PHP 5.3, vous pouvez obtenir un comportement plus proche de celui de JavaScript avec une fonction anonyme :

<?php
function outer() {
    $inner=function() {
        echo "test\n";
    };

    $inner();
}

outer();
outer();

inner(); //PHP Fatal error:  Call to undefined function inner()
$inner(); //PHP Fatal error:  Function name must be a string
?>

Sortie :

test
test

11 votes

+1 pour avoir répondu à un sujet (fondamentalement) fonctionnel par une réponse fonctionnelle, et non par la POO.

0 votes

L'auteur de la question devrait mettre à jour la question acceptée pour celle-ci. C'est ce qui répond réellement à la question jusqu'à mon époque.

90voto

Martijn Laarman Points 8097

Il n'y en a pas, en fait. J'ai toujours considéré cela comme un effet secondaire de l'analyseur.

Eran Galperin se trompe en pensant que ces fonctions sont en quelque sorte privées. Elles sont simplement non déclarées jusqu'à ce que outer() est exécutée. Ils n'ont pas non plus de portée privée ; ils polluent la portée globale, bien que de façon différée. Et en tant que callback, le callback externe ne peut être appelé qu'une seule fois. Je ne vois toujours pas comment il est utile de l'appliquer sur un tableau, qui appelle très probablement l'alias plus d'une fois.

Le seul exemple du "monde réel" que j'ai pu trouver est ce qui ne peut être exécuté qu'une seule fois et qui pourrait être réécrit plus proprement, selon l'OMI.

La seule utilisation à laquelle je peux penser, c'est pour les modules d'appeler une [name]_include qui définit plusieurs méthodes imbriquées dans l'espace global, combinée à la méthode

if (!function_exists ('somefunc')) {
  function somefunc() { }
}

des contrôles.

La POO de PHP serait évidemment un meilleur choix :)

9 votes

Oui, vraiment. C'est brutalement mauvais.

1 votes

Excellent exemple de lien. Je devrais commencer à mettre en œuvre cette méthode plutôt que l'héritage !

0 votes

Même que def déclarations en Ruby

10voto

Sz. Points 360

[Réécrit selon le commentaire de @PierredeLESPINAY].

Il ne s'agit pas d'un effet de bord, mais d'une fonctionnalité très utile pour en modifiant dynamiquement la logique de votre programme. Cela date de l'époque du PHP procédural, mais peut aussi être utile avec les architectures OO, si vous voulez fournir des implémentations alternatives pour certaines fonctions autonomes de la manière la plus directe possible. (Bien que OO soit le meilleur choix la plupart du temps, c'est une option, pas un mandat, et certaines tâches simples n'ont pas besoin de ce surplus).

Par exemple, si vous chargez dynamiquement/conditionnellement des plugins à partir de votre framework, et que vous souhaitez faciliter la vie des auteurs de plugins, vous pouvez fournir des implémentations par défaut pour certaines fonctions critiques que le plugin ne surcharge pas :

<?php // Some framework module

function provide_defaults()
{
    // Make sure a critical function exists:
    if (!function_exists("tedious_plugin_callback"))
    {
        function tedious_plugin_callback()
        {
        // Complex code no plugin author ever bothers to customize... ;)
        }
    }
}

2 votes

Cependant, selon le PO, la portée de la fonction imbriquée ne semble pas limitée à la fonction conteneur...

1 votes

@PierredeLESPINAY : Oups, très vrai, merci beaucoup de l'avoir signalé ! :-o J'ai mis à jour (réécrit) la réponse en conséquence. (C'est-à-dire que c'est toujours une fonctionnalité très pratique, mais pour une raison complètement différente alors).

8voto

cletus Points 276888

Les fonctions définies dans les fonctions ne sont pas très utiles, mais les fonctions définies de manière conditionnelle le sont. Par exemple :

if ($language == 'en') {
  function cmp($a, $b) { /* sort by English word order */ }
} else if ($language == 'de') {
  function cmp($a, $b) { /* sort by German word order; yes it's different */ }
} // etc

Ensuite, tout ce que votre code doit faire, c'est utiliser la fonction 'cmp' dans des choses comme les appels usort(), afin de ne pas mettre des contrôles de langue partout dans votre code. Je ne l'ai pas fait, mais je vois des arguments pour le faire.

1 votes

À l'époque, nous aurions appelé cela un code auto-modifiant. Un outil formidable, mais aussi dangereux que GOTO pour les abus...

2 votes

Mauvaise idée. Mieux : utiliser OO et ne pas s'immiscer dans les particularités du moteur de script.

1 votes

Attention, il est possible d'annuler les variables affectées aux fonctions anonymes.

2voto

Tout mon php est OO, mais je vois une utilité aux fonctions imbriquées, en particulier lorsque votre fonction est récursive et pas nécessairement un objet. C'est-à-dire qu'elle n'est pas appelée en dehors de la fonction dans laquelle elle est imbriquée, mais elle est récursive et doit donc être une fonction.

Il n'y a pas grand intérêt à créer une nouvelle méthode pour l'utilisation expresse d'une seule autre méthode. Pour moi, c'est du code maladroit et ce n'est pas le but de l'OO. Si vous n'avez pas l'intention d'appeler cette fonction ailleurs, immergez-la.

1 votes

Vous avez tout à fait raison, mais je pense qu'un meilleur exemple serait de déclarer des fonctions de rappel pour array_filter(), array_map(), preg_replace_callback(), uasort(), et autres. J'utilise ces fonctions assez fréquemment, et j'ai rarement besoin de la fonction de rappel que je déclare en dehors de la méthode OOP à partir de laquelle je l'appelle, donc il est beaucoup plus propre d'éviter de polluer l'espace de nom global ou même de classe avec la fonction de rappel. Et je peux enfin le faire avec PHP 5.3 (comme expliqué dans la réponse de user614643) !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X