86

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

13 回答 13

91

如果您使用的是 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
于 2011-05-20T10:12:32.110 回答
90

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

Eran Galperin 错误地认为这些函数在某种程度上是私有的。它们只是outer()在运行之前未声明。它们也不是私有范围的;它们确实污染了全球范围,尽管延迟了。而作为回调,外部回调仍然只能被调用一次。我仍然看不出将它应用于数组有什么帮助,这很可能不止一次调用别名。

我可以挖掘的唯一“现实世界”示例是这个,它只能运行一次,并且可以被重写得更干净,IMO。

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

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

检查。

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

于 2009-01-06T10:19:22.110 回答
10

[根据@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... ;)
        }
    }
}
于 2013-12-15T12:57:48.780 回答
8

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

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”函数,这样您就不会在代码中到处乱扔语言检查。现在我还没有这样做,但我可以看到这样做的论据。

于 2009-01-06T12:11:45.237 回答
7

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

有人可能会说只是在一个类中创建私有方法(或更小的代码块),但是当一个超特定的任务(它是父级独有的)需要模块化时,这会使水变得混乱,但不一定对其他人可用一类。好消息是,如果事实证明您在其他地方确实需要该功能,则修复相当简单(将定义移动到更中心的位置)。

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

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

于 2015-09-27T01:39:12.980 回答
2

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

为明确使用一种其他方法而制定一种新方法没有什么意义。对我来说,这是笨拙的代码,有点不是面向对象的重点。如果您永远不会在其他任何地方调用该函数,请嵌套它。

于 2012-04-30T20:41:51.163 回答
1

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

于 2014-01-09T06:27:28.113 回答
1

如果您在 php 7 中,请查看: 此实现将使您对嵌套函数有一个清晰的认识。假设我们在函数 foo() 中嵌套了三个函数(too()、boo() 和 zoo())。boo() 和 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() 以避免重新声明错误

于 2018-08-27T07:36:56.477 回答
0

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

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 相比,函数调用可以在父函数中的任何位置进行)

于 2018-04-27T18:38:24.457 回答
0

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

于 2019-02-07T18:10:34.370 回答
0

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

想象一下,我有一个名为 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 调用和内联包含!

所以嵌套函数在现实生活中很有用。

祝你今天过得愉快。

于 2021-06-27T07:35:34.773 回答
-1

嵌套函数在记忆中很有用(缓存函数结果以提高性能)。

<?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);
}
?>
于 2011-05-24T16:56:27.297 回答
-1

如果您希望嵌套函数利用在父函数中声明的变量,则嵌套函数很有用。

<?php
ParentFunc();
function ParentFunc()
{
  $var = 5;
  function NestedFunc()
  {
    global $var;
    $var = $var + 5;
    return $var;
  };
  echo NestedFunc()."<br>";
  echo NestedFunc()."<br>";
  echo NestedFunc()."<br>";
}
?>
于 2015-11-14T00:39:58.143 回答