简体   繁体   中英

PHP use static function to create new instance w/ Inheritance

So I got this idea from Laravel. With Laravel you can do something like the following.

$user = new User;
$user->where('name', 'george')->get();

// which is the same as...

User::where('name', 'george')->get();

So I'm guessing the User class has a __callStatic setup so it makes a new instance as a fallback. I was able to replicate that using the following code.

class Driver
{
  protected static $instance = null;

  public static function __callStatic($name, $args) {
    $classname = get_called_class();
    if (empty(static::$instance)) static::$instance = new $classname;
    return static::$instance->$name(...$args);
  }
}

But a problem occurs when I try to inherit the class more than once. I want all classes to be able to inherit the __callStatic and be able to call any of their ancestor's public functions statically.

class A extends Driver
{

  public function hello() {
    echo "hello";
    return $this;
  }

  public function world() {
    echo " world";
    return $this;
  }

}

class B extends A 
{
  public function name() {
    echo "\nMy Name is George";
    return $this;
  }
}

class C extends B 
{
  // class can be empty
}

C::hello()->world()->name();

// returns: hello world
// My name is George

The problem you face is due to the $instance property being static, and declared on the parent class. There is only one instance property in your project, it's a class property of Driver.

There are several ways around this. You could define $instance on the subclasses, make Driver::$instance a map (array) as [className => instance], or better yet, get rid of that piece of global state entirely ;)

The whole concept of having a "singleton-like" $instance structure is probably going to cause more problems later on, anyway. Consider the following example:

class Foo extends Driver
{
  private $name;
  public function named(string $name) : self
  {
    $this->name = $name;
    return $this;
  }
  public function name() : string
  {
    return $this->name;
  }
}

$alice = Foo::named('Alice');
$bob = Foo::named('Bob');

echo $alice->name(); // Outputs "Bob"!

$alice still points to the original instance, but since Foo::named('bob') assigns the name bob to that very same instance, you accidentally renamed Alice into Bob.

What you're probably looking for is more like:

abstract class Driver
{
  public static function __callStatic(string $name, array $arguments)
  {
    $instance = new static;
    $instance->$name(...$arguments);
    return $instance;
  }
}

That way, you'll create new instances every time.

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