web-dev-qa-db-fra.com

Quels sont PHP fonctions imbriquées pour?

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

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

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()
72
meouw

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 c'est utile de l'appliquer à un tableau qui appelle très probablement l'alias plus d'une fois.

Le seul exemple du «monde réel» que je pourrais creuser est ceci qui ne peut être exécuté qu’une seule fois et pourrait être réécrit plus proprement, à l’OMI.

Le seul usage auquel 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ées avec 

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

chèques.

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

80
Martijn Laarman

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
78
user641463

[Réécrit d'après le commentaire de @PierredeLESPINAY.]

Ce n'est pas simplement un effet secondaire, mais en fait une fonctionnalité très utile pour modifier dynamiquement la logique de votre programme. Cela remonte aux jours procéduraux PHP, 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. (Bien que OO soit la plupart du temps le meilleur choix, il s'agit d'une option et non d'un mandat, et 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 remplacé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... ;)
        }
    }
}
9
Sz.

Les fonctions définies dans les fonctions pour lesquelles je ne vois pas beaucoup d’utilisation, mais les 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.

6
cletus

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'il n'est pas appelé en dehors de la fonction dans laquelle il est imbriqué, mais est récursif et doit ensuite être une fonction.

Il ne sert à rien de concevoir 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. 

2

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 pourraient dire simplement créer des méthodes privées (ou de plus petits blocs de code) 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 du monde. 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, utiliser JavaScript comme norme pour évaluer d'autres langages de programmation basés sur le langage C est une mauvaise idée. JavaScript est définitivement son animal quand on le compare à 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 ), lorsque l'attention est portée à, rendre le code JavaScript unique, beau, différent , simple et complexe, tout à la fois. C'est mes deux centimes.

Juste pour être clair, je ne dis pas que les fonctions imbriquées sont privées. Cette simple imbrication peut aider à éviter l'encombrement lorsqu'un élément trivial doit être modularisé (et n'est nécessaire que par la fonction parent).

2
Anthony Rutledge

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 sur des bibliothèques contenant des milliers de fonctions. La pile d'appels typique peut contenir entre 5 et 10 appels de profondeur, nécessitant uniquement la liaison dynamique d'une douzaine de fichiers de 1 à 2 Ko, ce qui est préférable à l'inclusion de 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.

1
ZhuLien

si vous êtes en php 7, voyez ceci: Cette implémentation vous donnera une idée précise de la fonction imbriquée . Supposons que nous ayons trois fonctions (trop (), boo () et Zoo ()) imbriqués 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.

0
Prince Billy Graham

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>";
}
?>
0
Matthew

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 alors que 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 ci-dessous, la déclaration de la fonction (comparée à JS où l’appel de fonction peut se situer n'importe où dans la fonction parent

0
user8333817

Les fonctions imbriquées sont utiles dans la mémorisation (la mise en cache des résultats pour améliorer 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);
}
?>
0
Paul

Je n'ai vraiment utilisé cette caractéristique que lorsqu'il était utile d'exécuter une petite fonction récursive à l'intérieur d'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 processus primaire. Je me rends compte qu'il existe d'autres moyens "exemplaires" de faire cela, mais je veux m'assurer que mes développeurs voient cette fonction à chaque fois qu'ils consultent mon analyseur, c'est probablement ce qu'ils devraient modifier de toute façon ...

0
MJHd