10

This PHP question is related to this question, but is a bit different. I have a static factory method called create() which instantiates a class instance. I want the method to dynamically instantiate an instance of the (sub)class on which it is called. So, the class it instantiates must be determined at runtime. But I want to do this without having to redefine the static factory method in the subclass (and this would be perfectly valid in my example since the subclass has no new data members to initialize). Is this at all possible?

class Foo {

  private $name;

  public static function create($name) {

    //HERE INSTED OF:
    return new Foo($name);
    //I WANT SOMETHING LIKE:
    //return new get_class($this)($name);//doesn't work
    //return self($this);//doesn't work either

  }

  private function __construct($name) {

    $this->name = $name;

  }

  public function getName() {

    return $this->name;

  }

}

// the following class has no private data, just extra methods:

class SubFoo extends Foo {

  public function getHelloName() {

    echo "Hello, ", $this->getName(), ".\n";

  }

}


$foo = Foo::create("Joe");
echo $foo->getName(), "\n"; // MUST OUTPUT: Joe

$subFoo = SubFoo::create("Joe");
echo $subFoo->getHelloName(), "\n"; // MUST OUTPUT: Hello, Joe.
Community
  • 1
  • 1
John Sonderson
  • 3,238
  • 6
  • 31
  • 45

3 Answers3

11

You must create object using Late Static Binding - method get_called_class() is useful. Second option is use static keyword.

Example:

class Foo 
{
    private $name;

    public static function create($name) 
    {
        $object = get_called_class();
        return new $object($name);
    }

    private function __construct($name) 
    {
        $this->name = $name;
    }

    public function getName() 
    {
        return $this->name;
    }
}

class SubFoo extends Foo 
{
    public function getHelloName() 
    {
        return "Hello, ". $this->getName();
    }
}

$foo = Foo::create("Joe");
echo $foo->getName(), "\n";

$subFoo = SubFoo::create("Joe");
echo $subFoo->getHelloName(), "\n";

And output:

Joe
Hello, Joe
Piotr Olaszewski
  • 6,017
  • 5
  • 38
  • 65
  • 1
    Because you must create object `SubFoo`, read about late state binding... And there is a point of question. `static` is also good. – Piotr Olaszewski Feb 15 '15 at 17:50
  • 1
    well sorry I mean `static` not `self` – Kamil Karkus Feb 15 '15 at 17:51
  • What downvote? I didn't downvote. If I accidentally downvoted and then upvoted a tenth of a second later perhaps that showed up somewhere, not sure whether the notification would be deleted by the SE code in this case. Anyways, if anyone downvoted it wasn't me. Regards. – John Sonderson Feb 15 '15 at 18:06
  • Anyways, what's the advantage of using `get_called_class()` instead of `static`? Thanks. – John Sonderson Feb 15 '15 at 18:10
  • `get_called_class()` can be used to retrieve a string with the name of the called class, but `static` creating object. – Piotr Olaszewski Feb 15 '15 at 18:21
7

return new static();

there is already reserved keyword static

Kamil Karkus
  • 1,283
  • 1
  • 11
  • 29
  • Well, that doesn't work for me. The first call goes fine, but when I call `$subFoo->getHelloName()` I get the fatal error `Call to undefined method Foo::getHelloName()`. It looks like `self` is binding at compile-time and not at run-time as I need it to bind. I've updated my code to show this. – John Sonderson Feb 15 '15 at 17:48
  • 1
    oh sorry my bad, it is `static` keyword – Kamil Karkus Feb 15 '15 at 17:50
  • OK, `return new static($name)` works for me. Please update your answer so that I can accept it. Thanks. – John Sonderson Feb 15 '15 at 17:52
-3

Yes, It is possible with late static binding feature of php.

Instead of

$foo = Foo::create("Joe");
echo $foo->getName(), "\n"; // MUST OUTPUT: Joe

$subFoo = SubFoo::create("Joe");
echo $foo->getHelloName(), "\n"; // MUST OUTPUT: Hello, Joe.

try this

echo parent::getName();
echo self::getHelloName();
  • Well, if I have a chain of subclasses `parent` wouldn't work anyways. I think using `static($parameterList)` is the best answer to what I was looking for. – John Sonderson Feb 15 '15 at 18:09