简体   繁体   English

静态/非静态方法问题

[英]Static / Non-Static Method Issue

I am working on a simple ORM solution and have run into a tricky situation. 我正在研究一个简单的ORM解决方案,并遇到了棘手的情况。 Ideally, I'd like to be able to use methods in both a static context, and object context depending on how it is called. 理想情况下,我希望能够在静态上下文和对象上下文中使用方法,具体取决于它的调用方式。 I am not sure if this is possible, but here is what I mean: 我不确定这是否可行,但这就是我的意思:

Say a User model wants to call where() statically, this currently works fine, for example: 假设用户模型想静态调用where(),这当前工作正常,例如:

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

Now, I also support relationships, for example a user can have messages. 现在,我也支持关系,例如用户可以拥有消息。 When this relationship is established I simply store a blank copy of a message model in the user model and set a foreign key. 建立此关系后,我只需在用户模型中存储消息模型的空白副本并设置外键。 For example: 例如:

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

Now, ideally, I would like to be able to call: 现在,理想情况下,我希望能够致电:

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

In a non-static context and make use of $this -> foreign_key when in this context so as to only pull messages where the foreign key matches the user's id. 在非静态上下文中,在此上下文中使用$ this - > foreign_key,以便仅提取外键与用户id匹配的消息。 Is this type of context-switching possible in PHP? 这种类型的上下文切换在PHP中是否可行? Any reference to $this from static context throws an error as its a static method and should not rely on $this (for obvious reasons, when being called from a static context, $this won't exist) 从静态上下文对$ this的任何引用都会抛出一个错误,因为它是一个静态方法,并且不应该依赖于$ this(出于显而易见的原因,当从静态上下文调用时,$ this将不存在)

Are there any clever ways around this? 这有什么巧妙的方法吗? I tried overloading the method to have two different prototypes, both with and without the static keyword but this threw a re-declaration error. 我尝试重载方法有两个不同的原型,有和没有静态关键字,但这引发了重新声明错误。

After quite a bit of playing around, I have found a way to make this workable without the Strict Standards error mentioned by @drew010. 经过相当多的游戏后,我找到了一种方法,可以在没有@ drew010提到的Strict Standards错误的情况下使其可行。 I don't like it, it feels horrible, but it does work so I shall post this anyway. 我不喜欢它,它感觉很糟糕,但它确实有效,所以无论如何我都会发布。

Basically the idea is to make the method you want to be accessible private and static . 基本上,我们的想法是让您想要访问的方法是privatestatic You then define the __call() and __callStatic() magic methods, so that they will call the private static method. 然后定义__call()__callStatic()魔术方法,以便它们调用私有静态方法。 Now you may think "this doesn't solve the problem, I'm still stuck in a static context" - which you are but for a minor addition, you can append $this to the arguments passed to the actual method in __call() and fetch this as the last argument to the method. 现在你可能会认为“这不能解决问题,我仍然停留在一个静态上下文中” - 你只是为了一个小的补充,你可以将$this附加到传递给__call()实际方法的参数并将其作为方法的最后一个参数获取。 So instead of referencing $this in an object context, you reference the third argument to get a reference to your own instance. 因此,不是在对象上下文中引用$this ,而是引用第三个参数来获取对自己实例的引用。

I'm probably not explaining this very well, just have a look at 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);

I can't think of any way to do this without going against PHP standards and using the language in a way it wasn't meant to be used. 如果不违反PHP标准并以不应该使用的方式使用语言,我想不出任何方法可以做到这一点。

A function is either static or not. 函数是静态的还是非静态的。 Yes PHP allows you to call it either way but this does violate strict standards and the only reason you can get away with doing this is for backwards compatibility with older PHP 4 code where static didn't exist as a keyword. 是PHP允许您以任何方式调用它,但这确实违反了严格的标准,并且您可以逃避这样做的唯一原因是向后兼容旧的PHP 4代码,其中静态不作为关键字存在。

Consider this code: 考虑以下代码:

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

The output is: 输出是:

Not static. 不是静止的。
Static. 静态的。

But with error reporting turned on, this is the actual output: 但是在启用错误报告的情况下,这是实际输出:

Not static. 不是静止的。

Strict Standards: Non-static method Test::find() should not be called statically in test.php on line 19 严格标准:非静态方法Test :: find()不应在第19行的test.php中静态调用
Static. 静态的。

If you declare the method as static, then it is static no matter which way it was called. 如果将方法声明为static,则无论调用哪种方式,它都是静态的。

So I suppose the answer is Yes, you can do it using this workaround, but I wouldn't recommend it. 所以我认为答案是肯定的,你可以使用这种解决方法,但我不推荐它。 If you want to have it both ways, I would suggest adding two methods, public function find() and public static function findStatic() . 如果你想两种方式,我建议添加两个方法, public function find()public static function findStatic()

Since your code is either going to be written as $obj->find() or Class::find() , you can just as easily use the static vs. non-static methods in my opinion rather than having one method behave statically. 由于你的代码要么写成$obj->find()Class::find() ,你可以在我看来轻松使用静态和非静态方法,而不是让一个方法静态地运行。 To adhere to DRY, I suppose one method would leverage the other one for doing the actual finding. 为了坚持DRY,我想一种方法可以利用另一种方法来进行实际发现。

Sorry for not answering your question, but I have some comments that don't fit in a... comment. 很抱歉没有回答你的问题,但我有一些评论不适合...评论。 What you're doing is a bit illogical. 你正在做的事情有点不合逻辑。

$user->messages = new Message();

You're creating a single message inside a variable called messages . 你要创建一个单一的消息中的变量称为消息
Do you mean $user->messages[] = new Message(); 你的意思是$user->messages[] = new Message(); ?
Also, protect your class variables. 另外,保护您的类变量。

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

Here you're trying to SELECT from your user messages, it's nonsense. 在这里,您尝试从用户消息中进行选择,这是无稽之谈。
What you should do is simply the same as you did for the User class: get the messages statically and then assign them to your user: 你应该做的就像你对User类所做的一样:静态获取消息,然后将它们分配给你的用户:

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

If you need to look up for messages that have a specific primary key, pass it as a parameter to the where method, which can be enhanced to take many clauses: 如果需要查找具有特定主键的消息,请将其作为参数传递给where方法,该方法可以增强以采用许多子句:

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

I'd also like to add a personal note: creating an ORM is a great way to learn, but if you're looking for something more serious , I recommend you to give Doctrine or Propel a look. 我还想添加一个个人注释:创建ORM是一种很好的学习方式,但如果你正在寻找更严肃的东西,我建议你给Doctrine或Propel看一看。

From the theoretical and practical point of view, it is not a good idea to have a class method which can be called both from the static and non-static context. 从理论和实践的角度来看,拥有一个可以从静态和非静态上下文中调用的类方法并不是一个好主意。

If you want to achieve accessibility of your class/methods throughout your application, maybe it would be a good start to read about Dependency Injection, Service Container and dependency injection oriented programming. 如果您希望在整个应用程序中实现类/方法的可访问性,那么阅读依赖注入,服务容器和依赖注入的编程可能是一个良好的开端。

By implementing DI in your application, you will most probably loose any need for what you have mentioned. 通过在您的应用程序中实现DI,您很可能不再需要您提到的内容。

I would suggest investigating trough web and you will find that static calls in context that you are working with is avoided and flagged as bad practice. 我建议调查低谷网络,你会发现你正在使用的上下文中的静态调用被避免并被标记为不好的做法。 Static/shared state in object oriented programming is something which should be avoided (as well as singleton pattern). 面向对象编程中的静态/共享状态是应该避免的(以及单例模式)。

Dont get me wrong -> static methods have their purpose and benefits, however, the way you are using it is kinda obsolete (even though some frameworks, like Laravel, promotes this bad practice - eg "Facades" and Eloquent). 不要误解我的意思 - >静态方法有其目的和好处,但是,你使用它的方式有点过时(尽管一些框架,如Laravel,促进了这种不良做法 - 例如“Facades”和Eloquent)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM