2012-03-05 19 views
1

我最近與另一位開發人員討論了PHP的__isset()__get()魔術方法之間的關係。它是由一個我們擁有的類通過__get()方法延遲加載其他對象(延遲加載意味着該屬性在第一次訪問之前不存在,此時對象被實例化並返回)。不過,我們對__isset()應該返回尚未加載的屬性有不同意見。該屬性在技術上不存在(它不是集合,或者至少它已設置,但當前爲NULL),但對它的調用在技術上也會成功(禁止任何例外),並返回一個非NULL值。PHP:__isset()和__get()之間的關係

所以,我的問題是,在這種情況下應該__isset()僅僅是爲相同的參數是否__get()會成功的指標(返回TRUE如果__get()會成功,返回非NULL值)。或者,如果它技術性更強,並且返回FALSE,由於數據尚不存在(即使它首次訪問時)?

一個簡單的例子:

class Foo { 
    protected $data; 

    public function __get($prop) { 
     if ($prop == 'bar') { 
      $this->data['bar'] = new Bar; 
      return $this->data['bar']; 
     } 
    } 

    public function __isset($prop) { 
     if ($prop == 'bar') { 
      // What goes here? 
      // return isset($this->data[$prop]) would mean 
      // that the first call to isset($foo->bar) below will be FALSE 
      // which means that using logic like this would always fail 
      // and __get() would never be called: 
      // isset($foo->bar) ? $foo->bar->baz : 'foo->bar not set' 
     } 
    } 
} 

class Bar {} 

$foo = new Foo; 
var_dump(isset($foo->bar)); // ??? 
$bar = $foo->bar; 
var_dump(isset($foo->bar)); // bool(true) 
+3

'__isset'應該檢查'__get'返回的值是否設置爲 – zzzzBov 2012-03-05 14:07:28

回答

4

你必須從類的用戶的角度來考慮這一點。假設$fooclass Foo,你有這樣的代碼:

if(isset($foo->bar)) { 
    var_dump($foo->bar); // #1 
} 
else { 
    // $foo->bar is "not set", right? 
    $x = $foo->bar; 
    var_dump($x); // #2 
} 

修辭的問題:你認爲#1永遠打印null?你會期望#2打印什麼除了null

當然不是。如果這樣工作,那麼class Foo的用戶將花費大部分工作時間來詛咒作者。

假設我已經說服你,這是很簡單的從期望的行爲開始,然後回到它應該如何實現,即實現__isset這樣的:

public function __isset($prop) { 
    $val = $this->$prop; 
    return isset($val); 
} 
+0

+1示例函數說明最終用戶可能檢查的內容。 – 2012-03-05 14:11:10

+0

這就是我所傾向的,除了''set'(''''''''''''方面的奇特性質。例如,如果類「Foo」具有屬性「public $ foo = NULL;」 - 當您執行像isset($ foo-> foo)這樣的操作時,會發生什麼?屬性*存在*,但它沒有任何價值,所以熟悉isset()如何工作的大多數PHP開發人員的普遍期望是它應該是'FALSE',因爲它沒有價值。以同樣的方式,如果'$ foo'是延遲加載的,我們知道屬性*存在*,但它也沒有值。這就是我對它應該返回的混淆。 – FtDRbwLXw6 2012-03-05 14:15:43

+0

@drrcknlsn:其實,屬性*是否存在*並不相關,可能只是讓你感到困惑。由於'isset'返回'false'的'null'值可能非常「存在」,所以我們不得不效仿我們是否喜歡它。如果'null'是該房產的合法價值,因此不適合作爲警衛使用,只需添加其他房產'private $ gotFoo = false;'標記你是否已經實際執行了延遲加載。 – Jon 2012-03-05 14:19:27

0

我認爲這取決於什麼一個isset()檢查意味着你的代碼。使用isset()的代碼實際上關心什麼?

它只是要求的財產是價值(如在,可以訪問)?然後檢查是否可以通過__get()訪問該值並返回結果。

問是否屬性有效而不是null?然後實際加載該值並返回結果。

它問是否已裝入該值?然後檢查是否已調用__get(),並返回結果。

但它應該反映你的班級的使用情況,而不是一些任意的規則。

+0

使用將取決於調用代碼,這是我無法控制或深入瞭解,因爲這是一個庫類。實質上,我試圖創建其他開發人員最期望的行爲,如果這樣做合理的話。 – FtDRbwLXw6 2012-03-05 14:18:07

+0

@drrcknlsn我仍然認爲考慮使用是有意義的。 ActiveRecord類與API包裝器有不同的考慮因素。 – 2012-03-05 14:32:28

+0

@drrcknlsn而且由於它是一個庫,最重要的部分是* document * isset()'的含義。它檢查值是否已加載*?如果該值已設置*(不管將其設置爲「null」)?或者如果該財產是*有效*財產? – 2012-03-05 14:34:38