Return type "self" in abstract PHP class

self will always refer to the class its in, never children classes. So when Parent says to return :self, it's expecting you to create a function that returns an instance of Parent. What you need to do is have your child class declare that it's doing that directly. Instead of declaring the child's setName will return :self (which is an instance of Child), we declare we're going to return our Parent class directly. PHP sees this as perfectly valid (Child is an instance of Parent after all)

We cannot use :static here because static is a reserved word in the class definitions

abstract class ParentTest {

    public static function fromDB(string $name = '') {
        $instance = new static();
        if (!empty($name)) {
            $instance->setName($name)->read();
        }
        return $instance;
    }

    public abstract function read();

    public abstract function setName(string $name): self;

}

class Child extends ParentTest {
    public function read() {

    }

    public function setName(string $name) :ParentTest {
        return $this;
    }
}

$child = new Child();

The above code runs without error


Your code sample looks fine in PhpStorm 2017.2 (currently in EAP stage) but shows warning in 2017.1.4.

enter image description here

Quite possibly it was fixed by WI-33991 or one of the related tickets.


You may get & try the 2017.2 EAP build at any time from here: http://confluence.jetbrains.com/display/PhpStorm/PhpStorm+Early+Access+Program

It comes with own 30-days license and can run in parallel to your current version (IDE-wide settings are stored in separate folders).


IMHO, there is an intelligent and SOLID approach to use interfaces. It is even required for libraries supposed to be used by 3rd-parties.

So, we just set the interface name as a type instead of 'self' in all parent classes and continue using 'self' for children.

<?php
interface EntityInterface 
{
    public function setName(string $name): EntityInterface;
}

abstract class ParentTest implements EntityInterface 
{

    public abstract function read();

    public abstract function setName(string $name): EntityInterface;

}

class Child extends ParentTest 
{
    
    private string $name = '';
    
    public function read(): string 
    {
        return $this->name;
    }

    public function setName(string $name): self 
    {
        $this->name = $name;
        return $this;
    }
}

$child = new Child();
echo $child->setName('hello')->read();