静态/非静态方法问题

时间:2012-06-26 21:16:19

标签: php oop

我正在研究一个简单的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 个答案:

答案 0 :(得分:5)

经过相当多的游戏后,我找到了一种方法,可以在没有@ 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);

答案 1 :(得分: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();

输出结果为:

  

不是静止的   静态的。

但是在启用错误报告的情况下,这是实际输出:

  

不是静止的。

     

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

如果将方法声明为静态,则无论调用哪种方式,它都是静态的。

所以我认为答案是肯定的,你可以使用这种解决方法,但我不推荐它。如果您想两种方式,我建议添加两种方法,public function find()public static function findStatic()

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

答案 2 :(得分: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一看。

答案 3 :(得分:0)

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

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

通过在您的应用程序中实施DI,您很可能不再需要您提到的内容。

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

别误会我的意思 - &gt;静态方法有其目的和好处,但是,你使用它的方式有点过时(尽管一些框架,如Laravel,促进了这种不良做法 - 例如&#34; Facades&#34;和Eloquent)。