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


80

因此,我以一种不错的,最新的,面向对象的方式进行编程。我经常利用PHP实现的OOP的各个方面,但是我想知道何时需要使用闭包。是否有任何专家可以揭示何时实施闭包有用?

Answers:


80

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

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

这样一来,您就可以replacement在内部内部定义函数replace_spaces(),而不必这样:
1)弄乱全局名称空间
2)让人们三年后想知道为什么有一个全局定义的函数仅在另一个函数内部使用

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

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

您还可以使用关键字将范围之外的变量访问到闭包中use。考虑这个例子。

// Set a multiplier  
 $multiplier = 3;

// Create a list of numbers  
 $numbers = array(1,2,3,4);

// Use array_walk to iterate  
 // through the list and multiply  
 array_walk($numbers, function($number) use($multiplier){  
 echo $number * $multiplier;  
 }); 

这里给出了一个很好的解释什么是php lambda和闭包


1
这是一个很棒的解释。+1
David J Eddy

4
我很喜欢为什么要使用闭包的解释。大多数人并不真正理解这一点。+1
凯莉·肯德尔

10
这是对匿名函数的解释,而不是对闭包的解释。正如您所说,匿名函数就像命名函数一样,只是它们不是全局的。另一方面,闭包是包含词汇范围内的自由变量(用“ use”声明)的函数;即。他们可以从声明的作用域中复制和引用值,即使其他所有内容都已被垃圾回收也是如此。
Warbo

@Warbo这是事实;当时我并没有真正理解匿名函数和闭包之间的区别。闭包实际上只有在您使用了匿名函数后才有意义,但是直到今天,我仍然发现闭包是什么的“解释”(例如我的名字,来自7年前;-)),但并没有解释闭包的范围方面。
土边2015年

这就是为什么我要说的是,请检查JavaScript中频繁使用闭包的地方-但请记住,可变范围规则在PHP中是不同的。
罗尔夫(Rolf)

16

将来何时需要执行您现在确定的任务的功能。

例如,如果您读取一个配置文件,并且其中一个参数告诉您hash_methodfor的算法是multiply而不是square,则可以创建一个闭包,该闭包将在需要散列某些内容的任何地方使用。

可以在(例如)中创建闭包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)
}

我不能简单地复制粘贴此示例并运行它。首选一个我可以简单运行的示例。
Kim Stacks 2013年

3
这个答案很差。这是毫无意义的陈述:“将来您需要一个执行您现在决定的任务的功能时。”
放松在塞浦路斯,2014年

15

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

在PHP的上下文中,它们有些奇怪,因为PHP已经非常注重基于类的,面向对象的范例以及较旧的过程范例。通常,具有闭包的语言具有完整的词汇范围。为了保持向后兼容性,PHP不会做到这一点,因此这意味着与其他语言相比,闭包在这里将有所不同。我认为我们尚未确切了解如何使用它们。


10

我喜欢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

当然,如果您希望它随处可见,则可以将所有内容设为静态...


2

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

在PHP中,由于函数中“全局”(或“外部”)变量的范围和可访问性方面的差异,它们的效果不如JavaScript。但是,从PHP 5.4开始,闭包可以在对象内部运行时访问$ this对象,这使它们更有效。

这就是闭包的含义,足以理解上面的内容。

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

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


坦白说,我根本不清楚,即使对于我来说,作者也是如此。基本上,我是说:要了解在JavaScript中检查了哪些闭包,但请记住,JavaScript和PHP之间的可变范围是不同的。
罗夫(Rolf)

1

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

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

$param='captain'在func中sayhello()是func的局部变量sayhello()$param='ironman'以上sayhello()是全局变量。如果您只想在脚本中创建一个$ param变量,则应调用:函数global $param;sayhello()
vlakov

0

这是php中关闭的示例

// Author: HishamDalal@gamil.com
// 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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.