PHP 中的闭包...确切地说,它们是什么,什么时候需要使用它们?

Closures in PHP... what, precisely, are they and when would you need to use them?

提问人:rg88 提问时间:9/29/2008 最后编辑:rg88 更新时间:4/29/2022 访问量:30893

问:

因此,我正在以一种漂亮的、最新的、面向对象的方式进行编程。我经常使用 PHP 实现的 OOP 的各个方面,但我想知道什么时候需要使用闭包。有没有专家可以阐明何时实施关闭是有用的?

PHP OOP 闭包

评论


答:

17赞 Dan Udey 9/29/2008 #1

当您将来需要一个函数来执行您现在决定的任务时。

例如,如果你读取一个配置文件,其中一个参数告诉你 for 你的算法是 而不是 ,你可以创建一个闭包,它将在你需要对某些东西进行哈希处理的任何地方使用。hash_methodmultiplysquare

闭包可以在 (例如) 中创建;它创建一个函数,称为 using variables local to(来自配置文件)。每当调用时,它都可以访问本地范围内的变量,即使它没有在该范围内被调用。config_parser()do_hash_method()config_parser()do_hash_method()config_parser()

一个希望很好的假设例子:

function config_parser()
{
    // Do some code here
    // $hash_method is in config_parser() local scope
    $hash_method = 'multiply';

    if ($hashing_enabled)
    {
        function do_hash_method($var)
        {
            // $hash_method is from the parent's local scope
            if ($hash_method == 'multiply')
                return $var * $var;
            else
                return $var ^ $var;
        }
    }
}


function hashme($val)
{
    // do_hash_method still knows about $hash_method
    // even though it's not in the local scope anymore
    $val = do_hash_method($val)
}

评论

0赞 Kim Stacks 3/21/2013
我不能简单地复制粘贴这个例子并运行它。首选一个我可以简单地运行的例子。
3赞 Relaxing In Cyprus 9/11/2014
这个答案很差。这是一个毫无意义的陈述:“当你将来需要一个函数来执行你现在决定的任务时。
17赞 troelskn 9/29/2008 #2

除了技术细节之外,闭包是称为面向函数编程的编程风格的基本先决条件。闭包的用途大致与在面向对象编程中使用对象的用途相同;它将数据(变量)与一些代码(函数)绑定在一起,然后您可以将其传递到其他地方。因此,它们会影响你编写程序的方式,或者 - 如果你不改变你编写程序的方式 - 它们根本不会产生任何影响。

在PHP的上下文中,它们有点奇怪,因为PHP已经大量使用基于类的面向对象的范式,以及较旧的过程范式。通常,具有闭包的语言具有完整的词汇范围。为了保持向后兼容性,PHP不会得到这个,所以这意味着这里的闭包将与其他语言略有不同。我认为我们还没有确切地看到它们将如何被使用。

10赞 grossvogel 9/29/2008 #3

我喜欢 troelskn 的帖子提供的背景。当我想在 PHP 中做一些类似 Dan Udey 的例子时,我使用 OO 策略模式。在我看来,这比引入一个新的全局函数要好得多,该函数的行为是在运行时确定的。

http://en.wikipedia.org/wiki/Strategy_pattern

您还可以使用 PHP 中保存方法名称的变量来调用函数和方法,这很棒。所以对 Dan 的例子的另一种看法是这样的:

class ConfigurableEncoder{
        private $algorithm = 'multiply';  //default is multiply

        public function encode($x){
                return call_user_func(array($this,$this->algorithm),$x);
        }

        public function multiply($x){
                return $x * 5;
        }

        public function add($x){
                return $x + 5;
        }

        public function setAlgorithm($algName){
                switch(strtolower($algName)){
                        case 'add':
                                $this->algorithm = 'add';
                                break;
                        case 'multiply':        //fall through
                        default:                //default is multiply
                                $this->algorithm = 'multiply';
                                break;
                }
        }
}

$raw = 5;
$encoder = new ConfigurableEncoder();                           // set to multiply
echo "raw: $raw\n";                                             // 5
echo "multiply: " . $encoder->encode($raw) . "\n";              // 25
$encoder->setAlgorithm('add');
echo "add: " . $encoder->encode($raw) . "\n";                   // 10

当然,如果你想让它随处可用,你可以让一切都是静态的......

89赞 dirtside 9/30/2008 #4

PHP 将在 5.3 中原生支持闭包。当您想要仅用于某些小的特定目的的本地函数时,闭包是很好的选择。闭包的 RFC 给出了一个很好的例子:

function replace_spaces ($text) {
    $replacement = function ($matches) {
        return str_replace ($matches[1], ' ', ' ').' ';
    };
    return preg_replace_callback ('/( +) /', $replacement, $text);
}

这样一来,你就可以在本地定义函数,这样它就不会
1)弄乱全局命名空间
2)让人们在三年后想知道为什么有一个全局定义的函数只在另一个函数中使用
replacementreplace_spaces()

它使事情井井有条。请注意,函数本身没有名称,它只是被定义并分配为对 的引用。$replacement

但请记住,您必须等待 PHP 5.3 :)

评论

1赞 David J Eddy 3/21/2013
这是一个很棒的解释。+1
4赞 Carrie Kendall 5/22/2013
我喜欢解释为什么你会使用闭包的解释。大多数人并没有真正理解这一点。+1
13赞 Warbo 6/28/2014
这是对匿名函数的解释,而不是对闭包的解释。正如您所说,匿名函数就像命名函数一样,只是它们不是全局的。另一方面,闭包是包含词法范围的自由变量(用“use”声明)的函数;WEP它们可以从声明的范围内复制和引用值,即使在其他所有内容都已进行垃圾回收之后也是如此。
0赞 dirtside 10/3/2015
@Warbo 这是真的;当时我并没有真正弄清楚匿名函数和闭包之间的区别。闭包只有在你摸索匿名函数时才有意义,但直到今天,我仍然发现关于闭包是什么的“解释”(就像我的,从 7 年前开始的;-)),并没有解释它的范围方面。
0赞 Rolf 9/28/2017
这就是为什么我说,看看大量使用闭包的 JavaScript - 但请记住,可变范围规则在 PHP 中是不同的。
2赞 Rolf 8/26/2014 #5

闭包基本上是一个函数,您可以在一个上下文中为其编写定义,但在另一个上下文中运行。Javascript 帮助我理解了这些,因为它们在 JavaScript 中无处不在。

在 PHP 中,它们不如在 JavaScript 中有效,因为函数内部的“全局”(或“外部”)变量的范围和可访问性存在差异。然而,从 PHP 5.4 开始,闭包在对象内部运行时可以访问$this对象,这使得它们更加有效。

这就是闭包的意义所在,理解上面写的内容就足够了。

这意味着应该可以在某个地方编写一个函数定义,并在函数定义中使用$this变量,然后将函数定义分配给一个变量(其他人已经给出了语法示例),然后将这个变量传递给一个对象并在对象上下文中调用它,然后函数可以通过$this访问和操作该对象,就好像它只是它的另一个方法一样, 而实际上,它不是在该对象的类定义中定义的,而是在其他地方定义的。

如果不是很清楚,那么不要担心,一旦你开始使用它们,它就会变得清晰。

评论

0赞 Rolf 9/28/2017
老实说,我根本不清楚,即使对于我这个作者来说也是如此。基本上我是说:要了解什么是闭包,请在 JavaScript 中查看它们,但请记住,JavaScript 和 PHP 之间的变量范围是不同的。
1赞 Hisham Dalal 8/28/2017 #6

以下是 php 中的闭包示例

// Author: [email protected]
// Publish on: 2017-08-28

class users
{
    private $users = null;
    private $i = 5;

    function __construct(){
        // Get users from database
        $this->users = array('a', 'b', 'c', 'd', 'e', 'f');
    }

    function displayUsers($callback){
        for($n=0; $n<=$this->i; $n++){
            echo  $callback($this->users[$n], $n);
        }
    }

    function showUsers($callback){
        return $callback($this->users);

    }

    function getUserByID($id, $callback){
        $user = isset($this->users[$id]) ? $this->users[$id] : null;
        return $callback($user);
    }

}

$u = new users();

$u->displayUsers(function($username, $userID){
    echo "$userID -> $username<br>";
});

$u->showUsers(function($users){
    foreach($users as $user){
        echo strtoupper($user).' ';
    }

});

$x = $u->getUserByID(2, function($user){

    return "<h1>$user</h1>";
});

echo ($x);

输出:

0 -> a
1 -> b
2 -> c
3 -> d
4 -> e
5 -> f

A B C D E F 

c
1赞 Harsh Gehlot 3/4/2019 #7

从根本上说,闭包是内部函数 tat 可以访问外部变量,并用作 anonmyous 函数(没有任何名称的函数)的回调函数。

 <?php
      $param='ironman';
      function sayhello(){
          $param='captain';
          $func=function () use ($param){
                $param='spiderman';
          };
       $func();
       echo  $param;
       }
      sayhello();
?>

//output captain

//and if we pass variable as a reference as(&$param) then output would be spider man;

评论

0赞 vlakov 4/23/2019
$param='captain'在 func 中是 func 的局部变量。 上面是全局变量。如果只想在脚本中创建一个$param变量,则应调用: in funcsayhello()sayhello()$param='ironman'sayhello()global $param;sayhello()
1赞 Willem van der Veen 12/11/2020 #8

闭 包:

MDN 对 IMO 有最好的解释:

闭包是捆绑在一起(封闭)的功能的组合 并参考其周围状态(词汇环境)。在 换言之,闭包允许您访问外部函数的作用域 从内部功能。

即闭包是一个函数,可以访问父范围内的变量。闭包允许我们方便地动态创建函数,因为在某些情况下,只需要在一个地方使用函数(回调、可调用参数)。

例:

$arr = [1,2,3,3];
$outersScopeNr = 2;

// The second arg in array_filter is a closure
// It would be inconvenient to have this function in global namespace
// The use keyword lets us access a variable in an outer scope
$newArr = array_filter($arr, function ($el) use ($outersScopeNr) {
    return $el === 3 || $el === $outersScopeNr;
});

var_dump($newArr);
// array (size=3)
//  1 => int 2
//  2 => int 3
//  3 => int 3