PHP 嵌套函数有啥用?

Posted

技术标签:

【中文标题】PHP 嵌套函数有啥用?【英文标题】:What are PHP nested functions for?PHP 嵌套函数有什么用? 【发布时间】:2010-09-29 18:44:10 【问题描述】:

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

【问题讨论】:

我可以发誓我读到 PHP6 已经放弃了对此的支持,但我在任何地方都找不到。 @greg 我认为 PHP6 的整个计划还是悬而未决? 它们非常适合大型函数——排序递归组织 PHP 中也有闭包,不费吹灰之力。 【参考方案1】:

如果您使用的是 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

【讨论】:

+1 用于实际回答(基本上)功能性主题的功能性答案,而不是 OOP 问题作者应该将接受的问题更新为这个问题。直到我这个时候,这才是真正回答这个问题的原因。【参考方案2】:

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

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

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

祝你有美好的一天。

【讨论】:

【参考方案3】:

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

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

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

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

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

检查。

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

【讨论】:

是的,真的。这太糟糕了。 很好的示例链接。我应该开始实现它而不是继承! 与 Ruby 中的 def 声明相同 即使它们不完全是私有函数,除非调用了外部函数,否则它们仍然不能被调用,因此这给了它们一种依赖,作为与“结合”运行的函数外部函数... 如果没有这种行为,自动加载将无法工作。如果函数内的声明在某种程度上是私有的,那么您的自动加载处理程序执行的包含/要求最终将什么都不做。【参考方案4】:

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

【讨论】:

【参考方案5】:

如果您使用的是 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() 以避免重新声明错误

【讨论】:

如果函数名称反映了每个函数的一些独特特征,而不是随意的、押韵的、看起来相似的名称,这将更容易阅读和消化。它令人困惑且难以跟踪。选择可以帮助我们跟踪每个位置的名称将使用户友好并减少阅读和理解所需的认知负担。我没有时间,也没有惩罚的意愿来完成这篇文章,虽然你可能在那里隐藏了一个很好的观点,但我怀疑很少有人会留下来挖掘它。阅读 SO 不是一个研究项目。亲吻【参考方案6】:

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

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

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

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

【讨论】:

【参考方案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

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

【讨论】:

【参考方案8】:

[根据@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... ;)
        
    

【讨论】:

但是,根据OP,嵌套函数范围似乎不限于容器函数... @PierredeLESPINAY:糟糕,非常正确,非常感谢您的指出! :-o 我相应地更新(重写)了答案。 (即它仍然是一个非常方便的功能,但出于完全不同的原因。)【参考方案9】:

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

<?php
ParentFunc();
function ParentFunc()

  $var = 5;
  function NestedFunc()
  
    global $var;
    $var = $var + 5;
    return $var;
  ;
  echo NestedFunc()."<br>";
  echo NestedFunc()."<br>";
  echo NestedFunc()."<br>";

?>

【讨论】:

这是你永远不应该做的事情。 @fiddler 因为 NestedFunc 并不是真正嵌套的,它是全局的。【参考方案10】:

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

【讨论】:

【参考方案11】:

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

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

【讨论】:

你已经赚到了钱,但我认为更好的例子是为 array_filter()、array_map()、preg_replace_callback()、uasort() 等声明回调函数。我以相当多的频率使用这些函数,并且很少需要我在调用它的 OOP 方法之外声明的回调,因此避免使用回调函数污染全局甚至类命名空间感觉更干净.我终于可以用 PHP 5.3 做到这一点(如 user614643 的回答中所述)!【参考方案12】:

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

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

?>

【讨论】:

我原则上喜欢这个想法,但是,我认为这不适用于接受参数的函数并且您正在基于这些参数进行缓存。第二次调用函数时,使用不同的参数,结果不会被缓存,您将尝试重新声明_foo(),这将导致致命错误。【参考方案13】:

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

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

【讨论】:

在过去,我们会称之为自修改代码。一个很棒的工具,但与 GOTO 一样危险...... 坏主意。更好:使用面向对象而不是侵入脚本引擎细节。 请注意 - 可以取消设置变量分配的匿名函数。

以上是关于PHP 嵌套函数有啥用?的主要内容,如果未能解决你的问题,请参考以下文章

嵌套函数有啥好处(一般/在 Swift 中)

MySQL LIKE函数中的%有啥用

将嵌套数组转换为嵌套 html 块的递归 php 函数

PHP 5.2 的 DateTime::diff() 有啥用?

php中那个__set()魔术方法到底有啥用??有没有通俗一点的解释?

构造函数有啥用? [关闭]