在子类中使用Closure :: bind

时间:2014-02-21 22:09:35

标签: php closures

我正在尝试使用this trick从子类访问基类的私有变量,但我不完全理解闭包,所以我遇到了问题。以下示例几乎可以工作,除了我绑定到父类的新实例而不是子类的实例。

class Kitchen
{
    private $yummy = 'cake';

    public function change_treat()
    {
        $yummy = 'pie';
    }
}

class pantry extends Kitchen
{


    public function FindFood()
    {

        $yum = function ()
        {
            return $this->yummy;
        };

        $kit = new Kitchen();
        $yumers = Closure::bind($yum, $kit, $kit);
        echo "Look, a " . $yumers(). "!\n";
    }
}

$p = new pantry();
$p->FindFood();
$p->change_treat();
$p->FindFood();

输出:

Look, a cake!
Look, a cake!

期望的输出:

Look, a cake!
Look, a pie!

我理解为什么我的代码输出cake两次。这是因为我在绑定闭包时创建了一个完全独立的Kitchen对象。我遇到的问题是找出要传递给closure::bind的内容,说“绑定到我的父母”,或者我正在做什么甚至是可能的。有什么想法吗?

感谢。

编辑:对于那些与您分享您对此技术的关注的人,我了解这会打破封装,不建议这样做。目前,这是一个概念证明。情况是我有一个Wordpress插件,它具有我想要扩展的功能。这部分插件没有钩子,所以我试图找到其他方法来扩展它。其中一个变量是私有的,我需要检查它以确定是调用父方法还是使用我的自定义逻辑。我知道这听起来很可疑,我将此作为“口香糖和胶带”的努力。我计划向插件开发人员报告我尝试过的内容以及可以使插件更易于使用的内容。

2 个答案:

答案 0 :(得分:0)

首先,您的代码在这里是错误的:

public function change_treat()
{
    $yummy = 'pie';
}

应该是:

public function change_treat()
{
    $this->yummy = 'pie';
}

否则,您只需要为函数局部变量赋值,该函数在完成后将被丢弃。

其次,在每次调用pantry::FindFood()时,您都会创建一个新的Kitchen实例,然后将该闭包绑定到该实例。

解决这个问题的方法是打破继承链:

class pantry
{
    private $kitchen;

    function __construct(Kitchen $kitchen)
    {
        $this->kitchen = $kitchen;
    }

    function findFood()
    {
        $yum = function() {
            return $this->yummy;
        };

        $kit = new Kitchen();
        $yumers = Closure::bind($yum, $this->kitchen, $this->kitchen);
        echo "Look, a " . $yumers(). "!\n";
    }
}

$k = new Kitchen();
$p = new pantry($k);

$p->FindFood();
$k->change_treat();
$p->FindFood();

那就是说,最好不要使用这个hack并通过添加getter修复Kitchen类,例如getYummy()

答案 1 :(得分:0)

  

我知道创建一个新的厨房,如果FindFood意味着我会得到馅饼,那部分是错误的。我要问的是,我将什么传递给closure :: bind绑定到父Kitchen实例而不是Pantry实例。

修订回答:

在您的代码示例中,闭包 绑定到您在Kitchen中创建的FindFood实例。这就是你获得“蛋糕”价值的地方。当您致电change_treat时,您正在更改$yummy对象中的Pantry值。 Kitchen对象中的值保持不变。

<?php
class kitchen{

    private $_yummy = "cake";

    public function change_yummy( $newvalue ){
        $this->_yummy = $newvalue;
    }

    public function find_yummy(){
        return "Look, a {$this->_yummy}!";
    }
}
class pantry extends kitchen{

    public function __construct( kitchen $kitchen ){
        $this->_kitchen = $kitchen;
    }

    public function change_yummyInKitchen( $newvalue ){
        $closure = Closure::bind(
            function()use( $newvalue ){
                $this->change_yummy( $newvalue );
            },
            $this->_kitchen,
            "kitchen"
        );
        $closure();
    }
    public function find_yummyInKitchen(){
        $closure = Closure::bind(
            function(){
                return $this->find_yummy();
            },
            $this->_kitchen,
            "kitchen"
        );
        return $closure();
    }
}

$kitchen = new kitchen;
$pantry = new pantry( $kitchen );

print $pantry->find_yummy();           // prints "Look, a cake!"
print $pantry->find_yummyInKitchen();  // prints "Look, a cake!"

$pantry->change_yummy( "pie" );
print $pantry->find_yummy();           // prints "Look, a pie!"
print $pantry->find_yummyInKitchen();  // prints "Look, a cake!"

$pantry->change_yummyInKitchen( "pie" );
print $pantry->find_yummy();           // prints "Look, a pie!"
print $pantry->find_yummyInKitchen();  // prints "Look, a pie!"

另外,重新阅读你的问题和评论,我想你可能会误解“父母”的含义。它适用于类定义,而不适用于特定对象。在您的代码示例中,KitchenPantry的父类,但您在$kit中创建的对象 Pantry是不是 $p对象的“父级”。

这是一个如此复杂和不方便的原因:事情并不意味着以这种方式工作。我知道你已经知道这打破了封装等等,因此我不会试图以这些理由说服你。但除了“最佳实践”之外,重新设计这个想法将更简单并且更好地工作 更少的错误