0

PHP 有一个神奇的方法__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 代码,但我认为它们在应用程序的其他位置创建实例,并将它们存储在准备返回的类成员中。

4

2 回答 2

2

魔术方法__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();

如果你运行它(这里是一个在线演示),你会看到它是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();

现场演示

于 2013-11-03T22:42:15.343 回答
1

正如@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;
}
于 2013-11-03T23:21:43.453 回答