Pregunta

En JavaScript, las funciones anidadas son muy útiles: cierres, métodos privados y lo que tienes ...

¿Para qué sirven las funciones PHP anidadas? ¿Alguien los usa y para qué?

Aquí hay una pequeña investigación que hice

<?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()
¿Fue útil?

Solución

Básicamente no hay ninguno, siempre lo he tratado como un efecto secundario del analizador.

Eran Galperin se equivoca al decir que estas funciones son de alguna manera privadas, simplemente no se declaran hasta que se ejecuta external () . Tampoco tienen un ámbito privado, sino que contaminan el alcance global, aunque se retrasen. Y como una devolución de llamada, la devolución de llamada externa todavía podría llamarse solo una vez. Todavía no veo por qué es útil aplicarlo en una matriz que probablemente llame al alias más de una vez.

El único ejemplo de 'mundo real' que podría desenterrar es this que solo se puede ejecutar una vez y se puede volver a escribir con un IMO más limpio.

El único uso que se me ocurre es que los módulos llaman a un método [name] _include que establece varios métodos anidados en el espacio global combinados con

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

controles.

El OOP de PHP obviamente sería una mejor opción :)

Otros consejos

Si está utilizando PHP 5.3, puede obtener un comportamiento más similar a Javascript con una función anónima:

<?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
?>

Salida:

test
test

[Reescrito de acuerdo con el comentario de @PierredeLESPINAY.]

No es solo un efecto secundario, sino que en realidad es una característica muy útil para modificar dinámicamente la lógica de su programa. Es de los días de PHP de procedimientos, pero también puede ser útil con las arquitecturas OO, si desea proporcionar implementaciones alternativas para ciertas funciones independientes de la manera más directa posible. (Si bien OO es la mejor opción la mayor parte del tiempo, es una opción, no un mandato, y algunas tareas simples no necesitan un crucero adicional).

Por ejemplo, si carga complementos de forma dinámica / condicional desde su marco y desea hacer que la vida de los autores de los complementos sea muy fácil, puede proporcionar implementaciones predeterminadas para algunas funciones críticas que el complemento no anuló:

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

Funciones definidas dentro de funciones para las que no veo mucho uso pero sí funciones definidas condicionalmente. Por ejemplo:

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

Y luego, todo lo que debe hacer con su código es usar la función 'cmp' en cosas como las llamadas a usort () para que no ensucie las comprobaciones de idioma en todo el código. Ahora no he hecho esto, pero puedo ver argumentos para hacerlo.

Dicho todo lo anterior, uno podría simplemente crear una función anidada para reemplazar un código repetitivo localizado dentro de una función (que solo se usará dentro de la función principal). Una función anónima es un ejemplo perfecto de esto.

Algunos podrían decir que solo crean métodos privados (o bloques de código más pequeños) en una clase, pero eso es enturbiar las aguas cuando una tarea ultraespecífica (que es exclusiva del padre) necesita ser modularizada, pero no necesariamente disponible para El resto de una clase. La buena noticia es que si resulta que necesita esa función en otro lugar, la solución es bastante elemental (mueva la definición a una ubicación más central).

En términos generales, usar JavaScript como el estándar para evaluar otros lenguajes de programación basados ??en C es una mala idea. JavaScript es definitivamente su propio animal en comparación con PHP, Python, Perl, C, C ++ y Java. Por supuesto, hay muchas similitudes generales, pero los detalles pequeños y profundos (referencia JavaScript: The Definitive Guide, 6th Edition, Chapters 1-12 ), cuando se les presta atención, hacen que JavaScript sea único. Hermoso, diferente, simple y complejo, todo al mismo tiempo. Esos son mis dos centavos.

Para ser claros, no estoy diciendo que las funciones anidadas sean privadas. Solo ese anidamiento puede ayudar a evitar el desorden cuando algo trivial necesita ser modularizado (y solo es necesario para la función principal).

Todo mi php es OO, pero veo un uso para funciones anidadas, particularmente cuando su función es recursiva y no necesariamente un objeto. Es decir, no se llama fuera de la función en la que está anidado, pero es recursiva y, posteriormente, debe ser una función.

No tiene mucho sentido crear un nuevo método para el uso expreso de otro método único. Para mí, ese es un código torpe y no es el objetivo de OO. Si nunca va a llamar a esa función en otro lugar, anídela.

En las llamadas a servicios web, encontramos una sobrecarga mucho menor (memoria y velocidad) que incluye dinámicamente funciones anidadas sobre bibliotecas llenas de miles de funciones. La pila de llamadas típica podría tener entre 5 y 10 llamadas de profundidad, solo requerir vincular una docena de archivos de 1-2 kb dinámicamente era mejor que incluir megabytes. Esto se hizo simplemente creando una pequeña función de utilidad que requiere envoltura. Las funciones incluidas se anidan dentro de las funciones sobre la pila de llamadas. Considérelo en contraste con las clases llenas de cientos de funciones que no se requerían en cada llamada de servicio web, pero que también podrían haber utilizado las funciones incorporadas de carga perezosa de php.

Sé que esta es una publicación antigua, pero fwiw uso funciones anidadas para brindar un enfoque limpio y ordenado a una llamada recursiva cuando solo necesito la funcionalidad localmente, p. ej. para construir objetos jerárquicos, etc. (obviamente, debe tener cuidado de que la función principal solo se llame una vez):

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 punto de nota en php en comparación con JS, por ejemplo, es que la llamada a la función anidada debe realizarse después, es decir, a continuación, la declaración de la función (en comparación con JS, donde la llamada a la función puede estar en cualquier lugar dentro de la función principal

si está en php 7, vea esto: Esta implementación le dará una idea clara sobre la función anidada. Supongamos que tenemos tres funciones (también (), boo () y zoo ()) anidadas en la función foo (). boo () y zoo () tienen la misma función anidada con nombre xoo (). Ahora en este código he comentado claramente las reglas de las funciones anidadas.

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

    }

Ahora, si llamamos a test1 (), el resultado será este:

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 llamamos test2 () la salida será esta:

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

Pero no podemos llamar a text1 () y test2 () al mismo tiempo para evitar el error de re-declaración

Solo he usado realmente esta característica cuando fue útil ejecutar una pequeña función recursiva dentro de una función primaria, más categórica, pero no quería moverla a un archivo diferente porque era fundamental para el comportamiento de una primaria. proceso. Me doy cuenta de que hay otras "mejores prácticas" formas de hacerlo, pero quiero asegurarme de que mis desarrolladores vean esa función cada vez que miran mi analizador, es probable que sea lo que deberían modificar de todos modos ...

Las funciones anidadas son útiles en Memoization (resultados de la función de almacenamiento en caché para mejorar el rendimiento).

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

Las funciones anidadas son útiles si desea que la función anidada utilice una variable que se declaró dentro de la función principal.

<?php
ParentFunc();
function ParentFunc()
{
  $var = 5;
  function NestedFunc()
  {
    global $var;
    $var = $var + 5;
    return $var;
  };
  echo NestedFunc()."<br>";
  echo NestedFunc()."<br>";
  echo NestedFunc()."<br>";
}
?>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top