简体   繁体   中英

PHP return object instance from __getStatic()

PHP has a magic method __getStatic() that allows overloading of static method calls. I have a class with a fluent interface which performs integrity checks. I call it like this:-

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

However, I would like to call it like this:-

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

I thought that having this magic method in my base class would allow me to do this:-

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

But new self() produces an instance of the class that the calling code is in, and not the class which __callStatic() exists in, why is this? and how can I get around it?

I have also tried new static and this does the same thing.

I know this must be possible because Laravel's QueryBuilder has an interface like DB::table()->... and this uses method chaining, returning object instances, not static classes. I have had a look at the Laravel code, but I think they create the instances somewhere else in the application and they are stored in a class member ready to be returned.

The magic method __callStatic is only called for methods which don't exist , so in this case it will simply not run.

Consider the following simplified example:

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

If you run this ( here's an online demo ) you will see that it is the "real" bar() method that is called.

There can only be one method named bar on the class, so PHP's only other option would be to complain that bar() should be static - which it does, but non-fatally.


The reason you see an instance of the calling class is not that $instance is instantiated with the wrong class, but because when your method is called non-statically, $this "leaks" from the enclosing scope.

In the following example, $this ends up being the instance of Bar :

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

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

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

Live Demo

As @IMSoP pointed out, __getStatic() is only called if there is no method with the name called - not just if there isn't a static method with the name.

So the workaround to allow calls such as CheckClass::forActive->sites() is to give all the non static method names a prefix such as '_' and have a magic method __call() which will add the prefix in.

This means if I do CheckClass::forActive() the method forActive() doesn't exist, so __getStatic() will be called and will create an instance of the object and try to call the desired method. But the method doesn't exist because we've prefix it, so PHP will call the __call() magic method which will add the prefix and call the prefixed method.

So the 2 functions are:-

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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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