2014-02-21 15 views
1

我試圖使用this trick從子類訪問基類的私有變量,但我沒有完全理解閉包,所以我遇到了問題。下面的例子幾乎可以工作,除了我綁定到父類的新實例而不是子類的實例。在子類中使用Closure :: bind

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插件,它具有我想要擴展的功能。這部分插件沒有掛鉤,所以我試圖找到其他方法來擴展它。其中一個變量是私有的,我需要檢查它以確定是調用父方法還是使用我的自定義邏輯。我知道這聽起來很可疑,我會將其視爲「口香糖和膠帶」的努力。我正計劃向插件開發人員報告我嘗試過的內容,以及可以使插件更易於使用的內容。

回答

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(); 

這就是說,它最好不要在所有使用此技巧和解決Kitchen classby增加一個getter,例如getYummy()

+0

糾正這個錯誤[仍然不起作用](http://3v4l.org/MP3Ub)。但是,使用反射[確定](http://3v4l.org/U0R2X)(雖然主要觀點仍然存在:請不要這樣做) – DaveRandom

+0

這適用於如果您使用Closure :: bind($ yum,$ this- >廚房,$ this-> kitchen);.我編輯了這篇文章以反映這一點。我會看看,如果這在我的情況下工作,並接受它,如果它。 – VanillaBean

+0

@VanillaBean對,這就是我的意思,實際上是:) –

0

我知道創建一個新的廚房,如果FindFood意味着我會得到餡餅,那部分是錯誤的。我問的是,我通過什麼傳遞給closure :: 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!" 

此外,重新閱讀您的問題和意見,我想你可能會誤解「父母」的含義。它適用於類定義,而不適用於特定的對象。在你的代碼示例,KitchenPantry的父類,但是你擁有你Pantry類中創建對象$kit不是的你$p對象的‘父母’。

有一個原因是如此複雜和不方便:事情並不意味着這樣工作。我知道你已經知道這會破壞封裝等,所以我不會試圖以這些理由來談論你。但除了「最佳實踐」,重新工作這個想法將是更簡單更好地工作更少的錯誤

+0

我不清楚我需要完成什麼。我編輯了這個問題,希望能讓它更清楚。我希望改變$ p。當我綁定閉包時,我想要做的是將範圍更改爲$ p的父類。 – VanillaBean

+0

你正在這樣做。那正是你爲什麼從來沒有看到價值「派」的原因。請參閱上面的編輯。 **另外**,請參閱@ Jack的關於您的'change_treat'方法的回答(我錯過了)。 – traq

+0

我知道創建一個新的廚房,如果FindFood意味着我會得到餡餅和**這部分是錯誤的**。我問的是,我通過什麼傳遞給closure :: bind來綁定到父級Kitchen實例,而不是Pantry實例。 – VanillaBean