2012-06-26 187 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) 。