0

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
1
  • And the problem is? Commented Jan 27, 2018 at 19:47

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

PS: Laravel, or I should say Eloquent, is an Active Record ORM. IMHO, that's a Bad Thing and ought to be avoided as much as possible. Just deserialise your objects with something decent, like a Data Mapper ORM :D

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.