Question

En JavaScript, les fonctions imbriquées sont très utiles: fermetures, méthodes privées et ainsi de suite.

À quoi servent les fonctions PHP imbriquées? Est-ce que quelqu'un les utilise et pour quoi faire?

Voici une petite enquête que j'ai faite

<?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()
Était-ce utile?

La solution

Il n'y en a pas fondamentalement, j'ai toujours traité cela comme un effet secondaire de l'analyseur.

Eran Galperin se trompe en affirmant que ces fonctions sont en quelque sorte privées, elles sont simplement non déclarées jusqu'à ce que outer () soit exécuté. Ils ne sont pas non plus privés, ils règlent la portée mondiale bien que différée. Et en tant que rappel, le rappel externe ne peut toujours être appelé qu'une seule fois. Je ne vois toujours pas en quoi cela serait utile de l'appliquer à un tableau qui appelle très probablement l'alias plus d'une fois.

Le seul exemple concret que je puisse trouver est this qui ne peut être exécuté qu’une seule fois et pourrait être réécrit plus propre IMO.

La seule utilisation à laquelle je peux penser est que les modules appellent une méthode [name] _include qui définit plusieurs méthodes imbriquées dans l'espace global combiné avec

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

vérifie.

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

Autres conseils

Si vous utilisez PHP 5.3, vous pouvez obtenir davantage de comportements similaires à ceux 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

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

Ce n'est pas un simple effet secondaire, mais une fonctionnalité très utile pour modifier dynamiquement la logique de votre programme. Cela date des jours PHP procéduraux, mais peut également s'avérer utile avec les architectures OO si vous souhaitez fournir des implémentations alternatives pour certaines fonctions autonomes de la manière la plus simple possible. (Même si OO est le meilleur choix la plupart du temps, il s'agit d'une option et non d'un mandat. Certaines tâches simples ne nécessitent pas de ressources supplémentaires.)

Par exemple, si vous chargez de manière dynamique / conditionnelle des plug-ins à partir de votre infrastructure et souhaitez simplifier la vie de leurs auteurs, vous pouvez fournir des implémentations par défaut pour certaines fonctions critiques que le plug-in n'a pas écrasées:

<?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... ;)
        }
    }
}

Fonctions définies au sein de fonctions pour lesquelles je ne vois pas beaucoup d’utilisation, mais pour lesquelles je peux utiliser des fonctions définies conditionnellement. 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

Et tout ce que votre code doit faire est d’utiliser la fonction 'cmp' dans des appels tels que les appels usort () afin que vous ne contrôliez pas la langue dans votre code. Maintenant, je n'ai pas fait cela, mais je peux voir les arguments pour le faire.

Tout ce qui précède étant dit, on pourrait simplement créer une fonction imbriquée pour remplacer un code répétitif localisé dans une fonction (qui ne sera utilisée que dans la fonction parent). Une fonction anonyme en est un parfait exemple.

Certains diront peut-être simplement créer des méthodes privées (ou des blocs de code plus petits) dans une classe, mais cela brouille les pistes lorsqu'une tâche ultra-spécifique (qui est exclusive au parent) doit être modularisée, mais pas nécessairement disponible pour. le reste d'une classe. La bonne nouvelle est que s'il s'avère que vous avez besoin de cette fonction ailleurs, le correctif est plutôt élémentaire (déplacez la définition vers un emplacement plus central).

De manière générale, l’utilisation de JavaScript comme norme permettant d’évaluer d’autres langages de programmation basés sur le langage C est une mauvaise idée. JavaScript est définitivement son propre animal comparé à PHP, Python, Perl, C, C ++ et Java. Bien sûr, il y a beaucoup de similitudes générales, mais les détails les plus importants (référence JavaScript: Le Guide définitif, 6ème édition, chapitres 1 à 12 ), rendent le code JavaScript unique, unique, beau, différent, simple et complexe tout à la fois. C'est mes deux cents.

Pour être clair, je ne dis pas que les fonctions imbriquées sont privées. Juste cette imbrication peut aider à éviter l'encombrement lorsque quelque chose trivial doit être modularisé (et n'est nécessaire que par la fonction parent).

Tout mon php est OO, mais je vois une utilisation pour les 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 est récursive et doit par la suite être une fonction.

Il est inutile de créer une nouvelle méthode permettant d’utiliser expressément une seule autre méthode. Pour moi, c'est un code maladroit et une sorte de point essentiel de OO. Si vous n'appelez jamais cette fonction ailleurs, imbibez-la.

Dans les appels de service Web, nous avons constaté une surcharge beaucoup moins importante (mémoire et vitesse), y compris de manière dynamique, des fonctions individuelles imbriquées par rapport à des bibliothèques contenant des milliers de fonctions. La pile d'appels typique peut comprendre entre 5 et 10 appels de profondeur, nécessitant uniquement la liaison dynamique d'une douzaine de fichiers de 1 à 2 Ko, mieux que d'inclure des mégaoctets. Cela a été fait simplement en créant une petite fonction de wrapping. Les fonctions incluses deviennent imbriquées dans les fonctions situées au-dessus de la pile d'appels. Considérez cela à la différence de classes remplies de centaines de fonctions qui n’étaient pas requises à chaque appel de service Web, mais qui auraient également pu utiliser les fonctionnalités de chargement paresseux intégrées de php.

Je sais que c’est un vieux message mais j’utilise souvent des fonctions imbriquées pour donner une approche nette et ordonnée à un appel récursif lorsque j’ai uniquement besoin de la fonctionnalité localement - par exemple. pour construire des objets hiérarchiques, etc. (vous devez évidemment faire attention, la fonction parent n'est appelée qu'une fois):

function main() {
    // Some code

    function addChildren ($parentVar) {
        // Do something
        if ($needsGrandChildren) addChildren ($childVar);
    }
    addChildren ($mainVar); // This call must be below nested func

    // Some more code
}

Un point important dans php par rapport à JS par exemple est que l’appel à la fonction imbriquée doit être effectué après, c’est-à-dire en dessous de la déclaration de la fonction (comparé à JS où l’appel de fonction peut être n'importe où dans la fonction parent

si vous êtes en php 7, voyez ceci: Cette implémentation vous donnera une idée claire de la fonction imbriquée. Supposons que nous ayons trois fonctions (too (), boo () et zoo ()) imbriquées dans la fonction foo (). boo () et zoo () ont la même fonction imbriquée nommée xoo (). Maintenant, dans ce code, j'ai clairement commenté les règles des fonctions imbriquées.

   function foo(){
        echo 'foo() is called'.'<br>';
        function too(){
            echo 'foo()->too() is called'.'<br>';
        }
        function boo(){
            echo 'foo()->boo() is called'.'<br>';
            function xoo(){
                echo 'foo()->boo()->xoo() is called'.'<br>';
            }
            function moo(){
                echo 'foo()->boo()->moo() is called'.'<br>';
            }
        }
        function zoo(){
            echo 'foo()->zoo() is called'.'<br>';
            function xoo(){     //same name as used in boo()->xoo();
                echo 'zoo()->xoo() is called'.'<br>';
            }
        #we can use same name for nested function more than once 
        #but we can not call more than one of the parent function
        }
    }

/****************************************************************
 * TO CALL A INNER FUNCTION YOU MUST CALL OUTER FUNCTIONS FIRST *
 ****************************************************************/
    #xoo();//error: as we have to declare foo() first as xoo() is nested in foo()

    function test1(){
        echo '<b>test1:</b><br>';
        foo(); //call foo()
        too();
        boo();
        too(); // we can can a function twice
        moo(); // moo() can be called as we have already called boo() and foo()
        xoo(); // xoo() can be called as we have already called boo() and foo()
        #zoo(); re-declaration error
        //we cannont call zoo() because we have already called boo() and both of them have same named nested function xoo()
    }

    function test2(){
        echo '<b>test2:</b><br>';
        foo(); //call foo()
        too();
        #moo(); 
        //we can not call moo() as the parent function boo() is not yet called
        zoo(); 
        xoo();
        #boo(); re-declaration error
        //we cannont call boo() because we have already called zoo() and both of them have same named nested function xoo()

    }

Maintenant, si nous appelons test1 (), le résultat sera le suivant:

test1:
foo() is called
foo()->too() is called
foo()->boo() is called
foo()->too() is called
foo()->boo()->moo() is called
foo()->boo()->xoo() is called

si nous appelons test2 (), le résultat sera le suivant:

test2:
foo() is called
foo()->too() is called
foo()->zoo() is called
zoo()->xoo() is called

Mais nous ne pouvons pas appeler simultanément text1 () et test2 () pour éviter les erreurs de nouvelle déclaration

Je n'ai vraiment utilisé cette caractéristique que lorsqu'il était utile d'exécuter une petite fonction récursive dans une fonction primaire plus catégorique, mais je ne voulais pas le déplacer dans un fichier différent, car c'était fondamental pour le comportement d'un primaire processus. Je me rends compte qu’il existe d’autres "meilleures pratiques". façons de faire cela, mais je veux m'assurer que mes développeurs voient cette fonction chaque fois qu'ils regardent mon analyseur, c'est probablement ce qu'ils devraient modifier de toute façon ...

Les fonctions imbriquées sont utiles dans la mémorisation (les résultats de la mise en cache améliorent les performances).

<?php
function foo($arg1, $arg2) {
    $cacheKey = "foo($arg1, $arg2)";
    if (! getCachedValue($cacheKey)) {
        function _foo($arg1, $arg2) {
            // whatever
            return $result;
        }
        $result = _foo($arg1, $arg2);
        setCachedValue($cacheKey, $result);
    }
    return getCachedValue($cacheKey);
}
?>

Les fonctions imbriquées sont utiles si vous souhaitez que la fonction imbriquée utilise une variable déclarée dans la fonction parent.

<?php
ParentFunc();
function ParentFunc()
{
  $var = 5;
  function NestedFunc()
  {
    global $var;
    $var = $var + 5;
    return $var;
  };
  echo NestedFunc()."<br>";
  echo NestedFunc()."<br>";
  echo NestedFunc()."<br>";
}
?>
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top