【问题标题】:PHP return object instance from __getStatic()PHP 从 __getStatic() 返回对象实例
【发布时间】:2023-04-07 22:35:01
【问题描述】:

PHP 有一个magic 方法__getStatic(),它允许重载静态方法调用。我有一个具有流畅接口的类,可以执行完整性检查。我这样称呼它:-

$check = new CheckSomeCondition();
$check->forActive()->sites(array(1,2,3))->check();

但是,我想这样称呼它:-

CheckSomeCondition::forActive()->sites(array(1,2,3))->check();

我认为在我的基类中使用这个神奇的方法可以让我这样做:-

public static function __callStatic($method, $args)
{
    $instance = new self();
    return call_user_func_array(array($instance, $method), $args);
}

但是new self() 生成了调用代码所在类的实例,而不是__callStatic() 所在的类,这是为什么呢?我该如何解决?

我也尝试过new static,这也是同样的事情。

我知道这一定是可能的,因为 Laravel 的 QueryBuilder 有一个类似 DB::table()->... 的接口,它使用方法链接,返回对象实例,而不是静态类。我查看了 Laravel 代码,但我认为它们会在应用程序的其他位置创建实例,并将它们存储在准备返回的类成员中。

【问题讨论】:

  • "这使用了静态方法无法完成的方法链接" --- 它只返回一个对象。 “方法链接”不是魔术,它只是调用对象的方法。无论如何,你的想法很糟糕——不要那样做
  • @zerkms 方法链接在方法中返回对象的当前实例,以便可以直接调用另一个方法。这不能用静态方法完成,因为在 PHP 中你不能返回 self - 静态类
  • php.net/manual/en/language.oop5.late-static-bindings.php 可以,但这是个糟糕的主意
  • @zerkms 为什么这么可怕?
  • @Petah:因为静态处理起来更复杂。应尽可能避免静态本身(总是

标签: php singleton static-members


【解决方案1】:

魔术方法__callStatic 仅对不存在的方法调用,因此在这种情况下它根本不会运行。

考虑以下简化示例:

class Foo
{
    public function bar()
    {
        echo "Running instance method bar()";
    }

    public static function __callStatic($method, $args)
    {
        echo "__callStatic called for non-existent method $method";
    }
}

Foo::bar();

如果你运行这个 (here's an online demo),你会看到它是被调用的“真正的”bar() 方法。

类上只能有一个名为 bar 的方法,因此 PHP 唯一的其他选择是抱怨 bar() 应该是 static - 它确实如此,但不是致命的。


您看到调用类实例的原因不是 $instance 使用错误的类实例化,而是因为当您的方法被非静态调用时,$this 从封闭范围“泄漏”。

在以下示例中,$this 最终成为 Bar 的实例:

class Foo
{
    public function doSomething()
    {
        echo get_class($this);
    }
}

class Bar
{
    public function doSomethingElse()
    {
        Foo::doSomething();
    }
}

$bar = new Bar();
$bar->doSomethingElse();

Live Demo

【讨论】:

  • 对!这就说得通了。所以你认为我唯一的选择是有一个返回实例的静态方法get_instance(),然后做TheClass::get_instance()->method()...
  • 我查看了 laravel 代码,但我认为它们在应用程序的其他地方创建了实例,并且它们存储在类成员中
  • @rgvcorley 你的get_instance() 想法就是所谓的“工厂模式”,当然是一种值得尊敬的方法。
  • 想通了——只需要同时使用__call()__callStatic 并为我所有的方法名称加上前缀,这样我调用的方法名称就不存在了。然后所有呼叫都通过__call()__callStatic()
【解决方案2】:

正如@IMSoP 指出的那样,__getStatic() 仅在 没有方法 的名称被调用时才被调用 - 而不仅仅是在没有名称为静态方法的情况下。

因此,允许诸如CheckClass::forActive->sites() 之类的调用的解决方法是为所有非静态方法名称提供诸如“_”之类的前缀,并使用一个魔术方法__call() 来添加前缀。

这意味着如果我执行CheckClass::forActive() 方法forActive() 不存在,所以__getStatic() 将被调用,并将创建对象的一个​​实例并尝试调用所需的方法。但是这个方法不存在,因为我们给它加了前缀,所以 PHP 会调用 __call() 魔术方法,它会添加前缀并调用前缀方法。

所以这两个功能是:-

public static function __callStatic($method, $args)
{
    $instance = new self;
    return call_user_func_array(array($instance, $method), $args);
}

public static function __call($method, $args)
{
    $method = 'prefix_' . $method;
    return call_user_func_array(array($instance, $method), $args);
}

// Then all our method names need to be prefixed, like so:-
public static function prefix_SomeMethod($method, $args)
{
    // Do something
    return $this;
}

【讨论】:

  • 请注意,只有当 any 方法可用于“启动”链时才真正有意义 - 即工厂/构造函数不需要基本上下文。跨度>
  • 这很好——在我的情况下,任何公共方法都可以启动链,所以没问题。非常感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-28
相关资源
最近更新 更多