1

Is there a variable/magic Property that contains "this class" inside a class, for purposes of new?

I want:

trait foo {

    public function __construct( $x ) {
        // ... do stuff ...
    }

    static function init( $x ) {
        return new __THISCLASS__( $x ); // <--- whuzzat?
    }

)

class A {
    use foo;
}

class B {
    use foo;
}

The reason I'm doing this is that the function is actually contained in a Trait that is used by multiple classes, so I need the common code to be able to figure out what class it's running in and create the corresponding object.

A::init() should return an A object, and B::init() a B object.

Running PHP 7.3

yivi
  • 42,438
  • 18
  • 116
  • 138
Stephen R
  • 3,512
  • 1
  • 28
  • 45

2 Answers2

3

I believe the easiest and most readable way to accomplish something like this would be to use static to take advantage of late static binding.

E.g.:

trait aTrait {
    public static function build($x) {
        return new static($x);   
    }
}

class A
{
    public function __construct($x) {
        $this->x = $x;
    }
    use aTrait;
}

class B
{
    public function __construct($x) {
        $this->x = $x * 2;
    }

    use aTrait;
}


$a = A::build(2);
$b = B::build(2);
var_dump($a);
var_dump($b);

See it working here.

get_called_class() is simply a convenience for this. As the manual says:

get_called_class — The "Late Static Binding" class name

I wouldn't use __CLASS__ or self for builder methods, since these always refer to the class they were originally used on. If you later on extend the class, the method wouldn't work as you would expect.

For example, something like this:

trait dangerTrait
{
    public static function build($x) {
        return new self($x);
    }
}

class X
{
    use dangerTrait;

    public function __construct($x) {
        $this->x = $x * 3;
        echo "building class ", __CLASS__, "\n";
    }
}

class Y extends X {

    public function __construct($x) {
        $this->x = $x * 4;
        echo "building class ", __CLASS__, "\n";
    }
}

$x = X::build(2);
$y = Y::build(2);

...would output:

building class X
building class X

and both $x and $y would be instances of X.

yivi
  • 42,438
  • 18
  • 116
  • 138
2

You can get class which uses trait with get_called_class() function. For example

trait Foo
{
    public static function init()
    {
        $class = get_called_class();
        return new $class();
    }
}


class A {
    use Foo;
}

$obj = A::init();

Another way is the usage of Late Static Bindings

Maksym Fedorov
  • 6,383
  • 2
  • 11
  • 31