[英]Using Closure::bind in a child class
我正在尝试使用此技巧从子类访问基类的私有变量,但我不完全了解闭包,因此遇到了问题。 下面的示例几乎可以正常工作,除了我绑定到父类的新实例而不是子类的实例。
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插件,该插件具有我想扩展的功能。 插件的这一部分没有任何钩子,因此我正在尝试寻找其他方法来扩展它。 变量之一是私有的,我需要检查它以确定是调用父方法还是使用我的自定义逻辑。 我知道这听起来很可疑,因此我将其视为“口香糖和胶带”。 我正计划向插件开发人员报告我已经尝试过的内容以及可以使该插件更易于使用的内容。
首先,您的代码在这里是错误的:
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()
。
我知道,如果FindFood意味着创建一个新的Kitchen意味着我会得到馅饼,而那部分是错误的。 我要问的是,我要传递给闭包::: bind绑定到父级Kitchen实例而不是Pantry实例的信息。
在您的代码示例中,闭包确实绑定到您在FindFood
创建的Kitchen
实例。 那就是您获得“蛋糕”价值的地方。 当您调用change_treat
,您正在更改Pantry
对象中的$yummy
值。 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!"
另外,重新阅读您的问题和评论,我认为您可能会误解“父母”的含义。 它适用于类定义,不适用于特定对象。 在您的代码示例中, Kitchen
是Pantry
的父类,但是您在Pantry
类中创建的对象 $kit
不是 $p
对象的“父”对象。
这有一个原因,它是如此复杂且不方便:事情并非以这种方式工作。 我了解您已经知道这样做会破坏封装等,因此我不会尝试基于这些理由将您拒之门外。 但是,除了作为“最佳实践”之外,重做该想法将更简单,并且可以在减少错误的情况下 更好地工作 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.