代码之家  ›  专栏  ›  技术社区  ›  meouw

PHP嵌套函数的用途是什么?

  •  92
  • meouw  · 技术社区  · 17 年前

    在JavaScript中,嵌套函数非常有用:闭包、私有方法等等。。

    嵌套的PHP函数是用来做什么的?有人使用它们吗?

    这是我做的一个小调查

    <?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()
    
    12 回复  |  直到 15 年前
        1
  •  91
  •   Gus Hogg-Blake    4 年前

    如果你使用的是PHP 5.3,你可以通过匿名函数获得更多类似JavaScript的行为:

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

    输出:

    test
    test
    
        2
  •  90
  •   SherylHohman David    5 年前

    基本上没有。我一直将此视为解析器的副作用。

    Eran Galperin错误地认为这些功能在某种程度上是私有的。它们只是未申报,直到 outer() 正在运行。它们也不属于私人范围;它们确实污染了全球范围,尽管被推迟了。作为回调,外部回调仍然只能被调用一次。我仍然不明白在数组上应用它有什么帮助,数组很可能会多次调用别名。

    我能找到的唯一一个“现实世界”的例子是 this ,它只能运行一次,可以重写得更干净,IMO。

    我能想到的唯一用途是让模块调用 [name]_include 方法,在全局空间中设置多个嵌套方法,结合

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

    检查。

    PHP的OOP显然是一个更好的选择:)

        3
  •  10
  •   Sz.    9 年前

    [根据@PierredeLESPINAY的评论重写。]

    这不仅仅是一个副作用,而且实际上是一个非常有用的功能 动态修改 你程序的逻辑。它来自程序化PHP时代,但如果你想以最直接的方式为某些独立函数提供替代实现,它也可以在OO架构中派上用场。(虽然OO在大多数情况下是更好的选择,但它是一种选择,而不是强制要求,一些简单的任务不需要额外的技巧。)

    例如,如果你从框架中动态/有条件地加载插件,并想让插件作者的生活变得超级简单,你可以为插件没有覆盖的一些关键功能提供默认实现:

    <?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... ;)
            }
        }
    }
    
        4
  •  8
  •   cletus    17 年前

    在函数中定义的函数我看不出有多大用处,但我可以有条件地定义函数。例如:

    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
    

    然后,你的代码需要做的就是在usort()调用中使用“cmp”函数,这样你就不会在代码中到处乱扔语言检查了。现在我还没有这样做,但我可以看到这样做的理由。

        5
  •  7
  •   Anthony Rutledge    7 年前

    综上所述,可以简单地创建一个嵌套函数来替换函数中的一些本地化的重复代码(这些代码只会在父函数中使用)。匿名函数就是一个完美的例子。

    有些人可能会说,只在类中创建私有方法(或较小的代码块),但当一个超特定的任务(仅限于父级)需要模块化,但不一定对类的其他部分可用时,这会让事情变得一团糟。好消息是,如果事实证明你确实需要在其他地方使用该功能,那么修复方法相当简单(将定义移动到更中心的位置)。

    一般来说,使用JavaScript作为评估其他基于C的编程语言的标准是一个坏主意。与PHP、Python、Perl、C、C++和Java相比,JavaScript绝对是自己的动物。当然,有很多普遍的相似之处,但关键的细节(参考 JavaScript:权威指南,第6版,第1-12章 ),当注意的时候,让核心JavaScript同时具有独特性、美观性、不同性、简单性和复杂性。那是我的两分钱。

    为了清楚起见,我并不是说嵌套函数是私有的。当一些琐碎的东西需要模块化时(并且只有父函数需要),嵌套可以帮助避免混乱。

        6
  •  2
  •   Jesse James Richard    14 年前

    我所有的php都是面向对象的,但我确实看到了嵌套函数的用途,特别是当你的函数是递归的,不一定是对象的时候。也就是说,它不会在嵌套的函数之外被调用,而是递归的,因此需要是一个函数。

    用一种新方法来表达另一种方法几乎没有意义。对我来说,这是笨拙的代码,有点不是面向对象的重点。如果你永远不会在其他任何地方调用这个函数,那就嵌套它。

        7
  •  1
  •   ZhuLien    12 年前

    在Web服务调用中,我们发现它的开销(内存和速度)要低得多,动态地以嵌套的方式包含单个函数,而不是充满1000个函数的库。典型的调用堆栈可能在5-10个调用之间,只需要动态链接十几个1-2kb的文件比包含兆字节要好。这只是通过创建一个小的util函数包装要求来完成的。包含的函数嵌套在调用堆栈上方的函数中。将其与充满100个函数的类进行对比,这些函数不是每次Web服务调用都需要的,但也可以使用php的内置延迟加载功能。

        8
  •  1
  •   Prince Billy Graham    7 年前

    如果你使用的是php 7,那么可以看到: 此实现将使您清楚地了解嵌套函数。 假设我们有三个函数(too()、bo()和zoo())嵌套在函数foo()中。 bo()和zoo()具有同名嵌套函数xoo()。现在,在这段代码中,我已经清楚地注释掉了嵌套函数的规则。

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

    现在,如果我们调用test1(),输出将是:

    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
    

    如果我们调用test2(),输出将是:

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

    但是我们不能同时调用text1()和test2()以避免重新声明错误

        9
  •  0
  •   user8333817 user8333817    8 年前

    对于那些认为嵌套函数没有实际用途的人。是的,它们是有用的,这是一个例子。

    想象一下,我有一个名为my_file.php的文件,用于从中获取ajax结果。但是,如果有时您不想通过ajax获得结果,但希望在同一页面中两次包含它而不发生冲突,该怎么办?

    假设ajax文件my_file.php:

    <?php
    // my_file.php used for ajax
    
    $ajax_json_in = 10; 
    
    function calculations($a, $b)
    {   $result = $a + $b;
        return $result;
    }
    
    $new_result = $ajax_json_in * calculations(1, 2);
    
    $ajax_json_out =  $new_result; 
       
    ?>
    

    以下示例包含上述文件两次,没有冲突。您可能不想使用ajax调用它,因为在某些情况下,您需要将其直接包含在HTML中。

    <?php
    // include the above file my_file.php instead of ajaxing it
    
    function result1 
    {
        $ajax_json_in = 20; 
        include("my_file.php");
        return $ajax_json_out; 
    }
    
    
    function result2 
    {
        $ajax_json_in = 20; 
        include("my_file.php");
        return $ajax_json_out;
    }
    
    ?>
    

    包含文件会使文件的函数嵌套。该文件既用于ajax调用,也用于内联包含!!!

    因此,嵌套函数在现实生活中是有用的。

    祝您有个美好的一天。

        10
  •  0
  •   MJHd    7 年前

    我知道这是一篇旧文章,但当我只需要本地功能时,我使用嵌套函数为递归调用提供了一种整洁的方法,例如用于构建层次结构对象等(显然你需要小心,父函数只被调用一次):

    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
    }
    

    例如,与JS相比,php中需要注意的一点是,对嵌套函数的调用需要在函数声明之后进行,即在函数声明下方(与JS相比的是,函数调用可以在父函数内的任何位置)

        11
  •  0
  •   Thanasis    4 年前

    只有当在主、更分类的函数中执行一个小递归函数有用时,我才真正使用了这个特性,但不想将其移动到另一个文件中,因为它是主进程行为的基础。我意识到还有其他“最佳实践”的方法可以做到这一点,但我想确保我的开发人员每次查看我的解析器时都能看到该函数,这可能是他们无论如何都应该修改的内容。..

        12
  •  -1
  •   Paul    15 年前

    建立在 this answer ,您可以使用 use 条款:

    <?php
    function outer() {
        $inner = function ($i = 0) use (&$inner) {
            if ($i < 10) {
                echo "test $i\n";
                $inner($i + 1);
            }
        };
    
        $inner();
    }
    
    outer();
    outer();
    
    ?>
    

    如果你还有其他变量要带入闭包,也可以添加它们:

    <?php
    function outer($count) {
        $inner = function ($i = 0) use (&$inner, $count) {
            if ($i < $count) {
                echo "test $i\n";
                $inner($i + 1);
            }
        };
    
        $inner();
    }
    
    outer(3);
    outer(5);
    
    ?>
    

    与父答案一样,此模式确保内部函数不会泄漏到全局范围。

        13
  •  -1
  •   Matthew    10 年前