2012-06-26 216 views
5

我正在研究一個簡單的ORM解決方案,並遇到棘手的情況。理想情況下,我希望能夠在靜態上下文和對象上下文中使用方法,具體取決於它如何被調用。我不知道這是可能的,但這裏是我的意思是:靜態/非靜態方法問題

假設一個用戶模式要調用其中()靜態,這目前工作正常,例如:

$user = User::where('id = ?', 3); 

現在,我也支持關係,例如用戶可以擁有消息。當建立這種關係時,我只需在用戶模型中存儲消息模型的空白副本並設置一個外鍵。例如:

$user -> messages = new Message(); 
$user -> messages -> foreign_key = 'user_id'; 

現在,理想情況下,我想能夠調用:

$user -> messages -> where('unread = ?', 1); 

在非靜態上下文,並利用$如此 - > foreign_key時在這方面如此至於只拉外鍵與用戶的id相匹配的消息。這種類型的上下文切換可能在PHP中?任何從靜態上下文引用$ this都會拋出一個錯誤作爲其靜態方法,並且不應該依賴$ this(由於顯而易見的原因,當從靜態上下文中調用時,$ this將不存在)

是否有任何聰明的方法呢?我嘗試了重載方法來獲得兩個不同的原型,它們都帶有和不帶static關鍵字,但是這會引發重新聲明錯誤。

+0

$ user = User :: find();如果它沒有指定外鍵,你希望這個返回什麼? – craig1231

+0

看起來在頂層用法中,'find()'是'User'類中的一個(靜態)函數,但在底層使用中,find()是Message類中的一個方法。哪一個?都? – jedwards

+0

也許這是一個不清楚的例子,我忽略了這些參數,因爲它們看起來與問題無關,但我會用更準確的表示法來清除它,以確定它是如何工作的。 –

回答

4

相當多的玩耍後,我已經找到了一種方法,使不通過@ drew010提到Strict Standards錯誤這是可行的。我不喜歡它,這感覺很糟糕,但它確實有用,所以我會發布這個。

基本上這個想法是讓你想訪問privatestatic的方法。然後定義__call()__callStatic()魔術方法,以便他們將調用私有靜態方法。現在你可能會認爲「這不能解決問題,我仍然陷於靜態環境中」 - 你只是一個小小的增加,你可以附加$this給傳遞給__call()中實際方法的參數並獲取它作爲該方法的最後一個參數。因此,不要在對象上下文中引用$this,而是引用第三個參數來獲取對您自己的實例的引用。

我可能不會解釋這個非常好,只是看看this code

<?php 

class test_class { 

    private $instanceProperty = 'value'; 

    private static function where ($arg1, $arg2, $obj = NULL) { 
     if (isset($obj)) { 
      echo "I'm in an object context ($arg1, $arg2): I can access the instance variable: $obj->instanceProperty<br>\n"; 
     } else { 
      echo "I'm in a static context ($arg1, $arg2)<br>\n"; 
     } 
    } 

    public function __call ($method, $args) { 
     $method = "self::$method"; 
     if (is_callable($method)) { 
      $args[] = $this; 
      return call_user_func_array($method, $args); 
     } 
    } 

    public static function __callStatic ($method, $args) { 
     $method = "self::$method"; 
     if (is_callable($method)) { 
      return call_user_func_array($method, $args); 
     } 
    } 

} 

test_class::where('unread = ?', 1); 

$obj = new test_class(); 
$obj->where('unread = ?', 2); 
+0

我其實很喜歡這個解決方案,這是巧妙的解決方法。我沒有必要像規避這種語言的使用方式那樣,但這會爲整個方案增加一些簡單性。除了規避自然語言意圖之外,我認爲這樣使用它會帶來性能上的衝擊,但可能值得,因爲它們在引擎蓋下是一樣的,這對最終用戶是不可見的。是否有其他原因會導致您不使用這種重載? –

+1

我不會這樣做的最重要的原因是因爲當我在兩年內回到它時,我不知道這段代碼會做什麼,我將不得不花費10分鐘來完成它。如果你這樣做,請評論它!就性能而言,你可能會談論微秒,但是你必須明白,上面的代碼允許訪問對象/類的每一種方法,公開或其他方式 - 所以你可能想要檢查'$方法'針對允許像這樣訪問的方法的白名單。 – DaveRandom

+0

您可能還需要針對提供的參數數量執行一些額外的驗證,特別是在對象上下文中。這是因爲PHP不會抱怨傳遞的參數數量不正確,因爲它沒有固定的定義來檢查它,並且由於它通過將'$ this'附加到參數中的方式的性質,如果您提供了錯誤對象將傳遞給錯誤參數的參數數量。 – DaveRandom

4

我想不出有什麼辦法可以做到這一點,而不違反PHP標準,並且以不打算使用的方式使用該語言。

函數是靜態的或不是。是的,PHP允許您以任何方式調用它,但這確實違反了嚴格的標準,而您可以避免這樣做的唯一原因是爲了向後兼容較舊的PHP 4代碼,其中靜態不存在作爲關鍵字。

考慮以下代碼:

<?php 

class Test { 
    protected $_userId; 

    public function find() 
    { 
     if (isset($this)) { 
      echo "Not static.<br />\n"; 
     } else { 
      echo "Static.<br />\n"; 
     } 
    } 
} 

$t = new Test(); 
$t->find(); 

Test::find(); 

輸出是:

不是一成不變的。
靜態。

但隨着錯誤報告開啓,這是實際的輸出:

不是一成不變的。

嚴格的標準:非靜態方法測試:: find()方法不應該叫 靜態地test.php的第19行
靜態。

如果將方法聲明爲靜態方法,則無論調用哪種方式,它都是靜態的。

所以我想答案是肯定的,你可以使用這種解決方法,但我不會推薦它。如果你想兩種方式,我會建議添加兩種方法,public function find()public static function findStatic()

由於您的代碼要麼寫成$obj->find()Class::find(),您可以在我看來簡單地使用靜態方法和非靜態方法,而不是讓一個方法靜態運行。堅持DRY,我想有一種方法會利用另一種方法來做實際的發現。

+0

感謝您的回覆,我得到了這個結論以及我在測試中遇到了同樣嚴格的標準錯誤。希望能夠使用相同的名稱,因爲我可以調用6-7種不同的方法,這些方法都需要一個名爲counter的靜態對象。但是這確實是最合乎邏輯的意義,只是希望我能夠繞着它而破解;) –

4

對不起,不回答你的問題,但我不適合在...評論一些評論。 你在做什麼有點不合邏輯。

$user->messages = new Message(); 

你會創建一個名爲消息變量裏面消息。
你的意思是$user->messages[] = new Message();
另外,保護你的類變量。

$user->messages->where('unread = ?', 1); 

這裏你想從你的用戶消息,以選擇,這是無稽之談。
你應該做的僅僅是像你一樣的User類相同的:靜態得到消息,然後將它們分配給您的用戶:

$user->messages = Message::where('unread = ?', 1); 

如果需要查找該有一個特定的初級信息鍵,把它作爲參數傳遞給where方法,它可以提高到採取許多條款:

$messages = Message::where(array(
    array('unread = ?', 1), 
    array('id = ?',  $message->getID()), 
)); 

我也想加入個人備註:創建一個ORM是一個學習的好方法,但如果你正在尋找更多嚴重,我建議你給主義或推動看看。

+0

事實上,我這樣做主要是爲了學習。爲了解決在$ user - > messages中存儲新Message()的奇怪現象,我這樣做了,因爲我本質上是在尋找一種方法來模擬對象上下文中User :: where()的行爲。像做'$ user - > messages = Message; $ user - > messages :: find()'不起作用,存儲對象的空白副本並使用非靜態方法是我能想到的最接近的方法。但是你的意思是有道理的,我只是希望有一種更聰明的方式來利用它們之間的關係,而不必明確地傳遞外鍵。 –

+0

您可以將Message對象傳遞給'User :: addMessage()',並且在其中只接收消息的id('$ message-> getId()')並在需要時將其存儲。 - 當你從數據庫中獲取用戶時,你可以自動獲取他的消息(*急切加載*),或者當用戶請求時收到消息(* lazy loading *)。確保你第二次打電話時不要問他們。 –

+0

啊,是的,這似乎也是一個非常乾淨的解決方案。今晚我肯定會玩弄這些概念,嘗試找到最佳的解決方案,謝謝你的迴應! –

0

但從理論和實踐來看,它是不是有一類方法,它可以是一個好主意可以從靜態和非靜態上下文中調用。

如果你想實現在應用程序的類/方法的可訪問性,也許這將是一個良好的開端閱讀有關依賴注入,服務容器和依賴注入面向對象編程。

通過在您的應用程序中實現DI,您很可能會放棄您提到的任何需求。

我會建議調查低谷網站,你會發現在你正在使用的上下文中的靜態調用被避免並被標記爲不好的做法。面向對象編程中的靜態/共享狀態應該避免(以及單例模式)。不過,你使用它的方式已經過時了(即使像Laravel這樣的一些框架促成了這種不好的做法 - 例如「Facades」和Eloquent) 。