PHP 5.4 - '闭包$this支持'

68

我看到PHP 5.4的新计划功能包括:traits、数组解引用、JsonSerializable接口以及被称为'closure $this support'的东西。

http://en.wikipedia.org/wiki/Php#Release_history

虽然其他内容(如JsonSerialiable、数组解引用)要么很清晰,要么我查找了具体信息(traits),但“closure $this support”是什么我不确定。我在谷歌上搜索它或在php.net上找到任何相关信息都没有成功。

有人知道这应该是什么吗?

如果我必须猜测的话,它可能意味着这样:

$a = 10; $b = 'strrrring';
//'old' way, PHP 5.3.x
$myClosure = function($x) use($a,$b)
             {
                 if (strlen($x) <= $a) return $x;
                 else return $b;
             };

//'new' way with closure $this for PHP 5.4
$myNewClosure = function($x) use($a as $lengthCap,$b as $alternative)
                 {
                     if(strlen($x) <=  $this->lengthCap)) return $x;
                     else 
                     {
                         $this->lengthCap++;  //lengthcap is incremented for next time around
                         return $this->alternative;
                     }
                 };

这个例子可能微不足道,但其意义在于过去一旦构建了闭包,绑定的“use”变量就会固定。而使用“闭包$this支持”,它们更像是您可以随意操作的成员。

这听起来正确、接近或合理吗?有人知道什么是“闭包$this支持”吗?


就此而言,5.4 还不是 PHP 主干的官方名称——它仍然在内部被称为“5.3.99”,并且存在一些争议,即它是否真的是 5.4(与大型 Unicode 重写“6.0”分支无关),或者是 6.0。 - Charles
4个回答

74

这个功能在 PHP 5.3 中已经计划过了,但是

由于没有达成一致如何以合理的方式实现它,因此在 PHP 5.3 中取消了对闭包的 $this 支持。这个 RFC 描述了在下一个 PHP 版本中可以采取的可能路径。

这确实意味着您可以引用对象实例 (演示)。

<?php
class A {
  private $value = 1;
  public function getClosure() 
  {
    return function() { return $this->value; };
  }
}

$a = new A;
$fn = $a->getClosure();
echo $fn(); // 1

更多讨论请参见PHP Wiki

以及出于历史兴趣:


哦,我完全走错了方向。你是说$thisFoo的实例,对吗?我认为$this是闭包本身。还需要使用use吗? - jon_darkstar
3
不行,使用 use 是不可能的。到目前为止,在任何 PHP 版本中都不能将 $this 用作词法变量。我已经更新了维基上的示例,并提供了一个链接到 codepad,展示了当前 PHP 主干版本的结果。 - Gordon
1
谢谢,这很棒。顺便说一句,“public $closure”并没有什么作用,只是让你的观点有些分散,而你的其他表述非常好。 - jon_darkstar
1
@Gordon - 我认为Jon的意思是在一般情况下使用 use,而不是特定于 $this。据我所知,访问局部变量仍然需要使用 use。我期待着放弃 use ($self) 的技巧。 :) - David Harkness
1
@Bracketworks 我不是100%确定,但更深入决策的人告诉我他们认为是 A。 - Gordon
显示剩余5条评论

53

戈登没提到的一件事是重新绑定$this。虽然他描述的是默认行为,但可以重新绑定它。

示例

class A {
    public $foo = 'foo';
    private $bar = 'bar';

    public function getClosure() {
        return function ($prop) {
            return $this->$prop;
        };
    }
}

class B {
    public $foo = 'baz';
    private $bar = 'bazinga';
}

$a = new A();
$f = $a->getClosure();
var_dump($f('foo')); // prints foo
var_dump($f('bar')); // works! prints bar

$b = new B();
$f2 = $f->bindTo($b);
var_dump($f2('foo')); // prints baz
var_dump($f2('bar')); // error

$f3 = $f->bindTo($b, $b);
var_dump($f3('bar')); // works! prints bazinga

使用闭包的bindTo实例方法(也可以使用静态的Closure::bind)将返回一个重新绑定到给定值的新闭包。
通过传递第二个参数来设置范围,这将确定从闭包内部访问私有和受保护成员的可见性。


6
感谢您对 bindTo() 功能的描述,这是一个非常重要的特性。+1 - Darragh Enright
你知道是否有任何方法可以重新绑定从方法创建的闭包吗? - rela589n

22

在@Gordon的回答基础上,可以在PHP 5.3中模仿闭包-$this的一些hacky方面。

<?php
class A
{
    public $value = 12;
    public function getClosure()
    {
        $self = $this;
        return function() use($self)
        {
            return $self->value;
        };
    }
}

$a = new A;
$fn = $a->getClosure();
echo $fn(); // 12

10
是的,但值得注意的区别是在闭包内部无法访问 $self 上的任何私有或受保护的属性或方法。 - jgivoni
3
哈哈,我发现这个答案一年后解决了我的问题。 ;) - Xeoncross
@jgivoni的意思是,PHP 5.3中无法访问私有或受保护的变量吗? - Shiro
2
@Shiro,是的。这个5.3版本的解决方法只适用于公共属性。 - jgivoni
@Pacerier,我不知道在PHP 5.3中访问闭包内的非公共属性的任何解决方法。最好的建议是升级到5.4或更高版本。 - jgivoni

2

在其他答案的基础上,我认为以下示例可以展示PHP 5.4+的可能性:

<?php

class Mailer {
    public    $publicVar    = 'Goodbye ';
    protected $protectedVar = 'Josie ';
    private   $privateVar   = 'I love CORNFLAKES';

    public function mail($t, $closure) {
        var_dump($t, $closure());
    }
}

class SendsMail {
    public    $publicVar    = 'Hello ';
    protected $protectedVar = 'Martin ';
    private   $privateVar   = 'I love EGGS';

    public function aMailingMethod() {
        $mailer = new Mailer();
        $mailer->mail(
            $this->publicVar . $this->protectedVar . $this->privateVar,
            function() {
                 return $this->publicVar . $this->protectedVar . $this->privateVar;
            }
        );
    }
}

$sendsMail = new SendsMail();
$sendsMail->aMailingMethod();

// prints:
// string(24) "Hello Martin I love EGGS"
// string(24) "Hello Martin I love EGGS"

请参考:

请查看:https://eval.in/private/3183e0949dd2db


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接