5

我正在研究一个简单的 ORM 解决方案并且遇到了一个棘手的情况。理想情况下,我希望能够在静态上下文和对象上下文中使用方法,具体取决于它的调用方式。我不确定这是否可能,但这就是我的意思:

假设一个用户模型想要静态调用 where(),这目前工作正常,例如:

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

现在,我也支持关系,例如用户可以有消息。建立这种关系后,我只需将消息模型的空白副本存储在用户模型中并设置外键。例如:

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

现在,理想情况下,我希望能够调用:

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

在非静态上下文中并在此上下文中使用 $this -> foreign_key 以便仅提取外键与用户 ID 匹配的消息。这种类型的上下文切换在 PHP 中是可能的吗?从静态上下文中对 $this 的任何引用都会引发错误,因为它是一个静态方法,并且不应依赖于 $this(出于显而易见的原因,当从静态上下文中调用时,$this 将不存在)

有什么聪明的方法可以解决这个问题吗?我尝试重载该方法以拥有两个不同的原型,有和没有 static 关键字,但这引发了重新声明错误。

4

4 回答 4

5

在玩了很多之后,我找到了一种方法来使这个可行,而不会Strict Standards出现@drew010 提到的错误。我不喜欢它,感觉很糟糕,但它确实有效,所以我还是会发布这个。

基本上,这个想法是使您想要的方法易于访问private,并且static. 然后定义__call()__callStatic()魔术方法,以便它们调用私有静态方法。现在您可能会认为“这并不能解决问题,我仍然被困在静态上下文中”-您就是这样,但要进行少量添加,您可以附加$this到传递给实际方法的参数中,__call()并将其作为方法的最后一个参数。因此,您不是$this在对象上下文中引用,而是引用第三个参数来获取对您自己的实例的引用。

我可能没有很好地解释这一点,请看一下这段代码

<?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);
于 2012-06-26T21:54:46.977 回答
4

如果不违反 PHP 标准并以不应该使用的方式使用该语言,我想不出任何方法来做到这一点。

函数要么是静态的,要么不是。是的,PHP 允许您以任何一种方式调用它,但这确实违反了严格的标准,并且您可以逃脱这样做的唯一原因是为了向后兼容旧的 PHP 4 代码,其中 static 不作为关键字存在。

考虑这段代码:

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

输出是:

不是静态的。
静止的。

但是打开错误报告后,这是实际输出:

不是静态的。

严格标准:非静态方法 Test::find() 不应在第 19 行静态的 test.php 中静态调用

如果您将方法声明为静态,那么无论以何种方式调用它,它都是静态的。

所以我想答案是肯定的,你可以使用这个解决方法来做到这一点,但我不推荐它。如果你想同时拥有它,我建议添加两种方法,public function find()并且public static function findStatic().

由于您的代码将被编写为$obj->find()or Class::find(),因此在我看来,您可以轻松地使用静态与非静态方法,而不是让一种方法静态运行。为了坚持 DRY,我想一种方法会利用另一种方法来进行实际发现。

于 2012-06-26T21:27:45.487 回答
4

很抱歉没有回答您的问题,但我有一些评论不适合...评论。你在做什么有点不合逻辑。

$user->messages = new Message();

您正在一个名为messages的变量中创建一条消息。 你的意思是? 另外,保护你的类变量。
$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 是一种很好的学习方式,但如果你正在寻找更严肃的东西,我建议你看看 Doctrine 或 Propel。

于 2012-06-26T21:36:40.637 回答
0

从理论和实践的角度来看,拥有一个可以从静态和非静态上下文调用的类方法并不是一个好主意。

如果您想在整个应用程序中实现类/方法的可访问性,那么阅读有关依赖注入、服务容器和面向依赖注入的编程可能是一个很好的开始。

通过在您的应用程序中实现 DI,您很可能会放弃对您所提到的内容的任何需求。

我建议调查 trough web,您会发现您正在使用的上下文中的静态调用被避免并被标记为不好的做法。面向对象编程中的静态/共享状态是应该避免的(以及单例模式)。

不要误会我的意思-> 静态方法有它们的目的和好处,但是,您使用它的方式有点过时了(尽管某些框架,如 Laravel,提倡这种不好的做法 - 例如“Facades”和 Eloquent)。

于 2014-06-23T08:14:03.993 回答