2012-05-09 17 views
4

我有一個問題,一直在破壞我想要做很長一段時間的事情的方式。這與使用magic get和set in PHP並試圖對對象進行預增量有關。我有一個PHP類這樣的:用PHP預先增加魔法獲取和設置定義

class Foo { 
    public $object; 

    function __construct() { 
      $this->object = array("bar" => 1); 
    } 

    function & __get($name) { 
      return $this->object[$name]; 
    } 

    function __set($name, $value) { 
      echo "Old value: ". $this->object[$name] ." - New value: ". $value ."\n"; 
      $this->object[$name] = $value; 
    } 
} 

注意&__get方法。現在我運行此代碼:

$o = new Foo(); 

echo "First test\n"; 
$o->bar = 2; 

echo "Second test\n"; 
$o->bar = $o->bar + 1; 

echo "Third test\n"; 
$o->bar += 1; 

echo "Fourth test\n"; 
++$o->bar; 

,輸出是:

First test 
Old value: 1 - New value: 2 
Second test 
Old value: 2 - New value: 3 
Third test 
Old value: 4 - New value: 4 
Fourth test 
Old value: 5 - New value: 5 

第三和第四測試有意想不到的(由我)的行爲。看起來像$ this-> object ['bar']返回要設置的值,而不是我期望的舊值。在實際設置之前,價值是如何設定的?

如果我從__get方法中刪除&,這將起作用,所以我想這與PHP所做的引用管理有關。但我期望第三次測試的行爲與第二次測試的行爲相同,但事實並非如此。

我真的不明白這一點。任何幫助將非常感激。

回答

6

結果事實上(仔細考慮之後)我所期望的。


首先測試

代碼:$o->bar = 2;
輸出:Old value: 1 - New value: 2

的操作,依次是:

  • 分配一個新的價值$bar(調用__set()

很明顯,這只是放棄舊的價值,並將新的價值放在它的位置上。沒什麼複雜。


二測

代碼:$o->bar = $o->bar + 1;
輸出:Old value: 2 - New value: 3

的操作,依次是:

  • 獲取的$bar一個副本(呼叫__get()
  • 添加一個到它
  • 分配新值,以$bar(呼叫__set()

表達式的右側進行評價,並將結果指定給左側。實例變量$bar的值在評估右側時未觸及,因此您在__set()調用的開始處看到它是舊值。


第三次測試

代碼:$o->bar += 1;
輸出:Old value: 4 - New value: 4

的操作,依次是:

  • 得到一個參考$bar(調用__get()
  • 添加一個到它
  • 分配一個新的價值$bar(調用__set()

這是有趣的 - 乍一看我,你看到__set()與生成的輸出有點驚訝這個操作。由於您正在增加原始zval中保存的值,因此不會像指定前兩個值那樣分配全新的值 - 因此也不會分配全新的zval - 看起來__set()調用是多餘的。

然而,當__set()叫,你看到輸出是你所期望的,因爲參考價值已經增加,__set()被稱爲前。傳遞給__set()的值是增量操作的結果,因此不會導致另一個被添加到它,它只是將$foo->bar的值分配給$foo->bar - 因此您會看到舊值和新值相同。


第四次試驗

代碼:++$o->bar;
輸出:Old value: 5 - New value: 5

...語義上的第三次試驗。完全相同的操作完全相同的操作導致完全相同的輸出。再次,有點奇怪,有任何輸出,但你去。


第五試驗我發明

代碼:$o->bar++;
輸出:Old value: 5 - New value: 6

...語義上的第二次測試。這又是一個有趣且有點意外的事情,但它也是有道理的,因爲$i++返回舊值$i


在猜測,__set()被不必要地稱爲第三和第四測試的原因是:

  • (不可能的,但有可能),因爲它會更耗費計算摸出是否__get()返回一個引用或一個副本,而不是簡單地調用__set()--這似乎不大可能,因爲函數調用通常非常昂貴。
  • (更可能),因爲__set()函數可能包含與實際賦值操作無關的代碼,並且如果代碼在值更改時沒有運行,這可能會非常不方便。例如,如果要實現某種事件驅動的體系結構,當某些值被更改時觸發事件,如果只用於直接分配,則會很煩人。
+0

我一直認爲第三個測試會被翻譯成類似於第二個的東西。這種行爲給了我一些嚴重的問題。 – makmonty

+0

@Portnoy解決方法是養成總是使用'$ var ++'的習慣,除非您需要將新值作爲較大操作的一部分 - 只有在那時您才使用'++ $ var'。 – DaveRandom

+0

是的,但是我正在處理的系統中必須支持預增量。我遇到這個問題的真正的類將以不同的私有屬性存儲在對該對象所做的更改中,以便它只將那些已更改的字段推送到數據庫。有了這種行爲,我無法分辨新值是否與前一個值實際不同,所以我現在的做法是在保存對象時更新每個字段,因爲預先增量會導致條件失敗。 – makmonty