Implement type hinting 'parent' in interface throws an error

Viewed 2434

It is possible to type hint self inside an interface:

interface I {
    public function instanceOfSelf(self $object);
}

class A implements I {
    private function printBool(bool $b) {
        echo ($b ? 'true' : 'false') . "\n";
    }

    public function instanceOfSelf(I $object) {
        $this->printBool($object instanceof I);
    }                        
}

$a = new A;
$a->instanceOfSelf($a); // true

It is also possible to type hint parent inside an interface, which means the following code produces no error:

interface I1 {}

interface I2 extends I1 {
    public function instanceOfSelf(self $object);

    public function instanceOfParent(parent $object);
}

However when type hinting I1 according to I2::instanceOfParent an error is thrown:

// Duplication for completion...
interface I1 {}
interface I2 extends I1 {
    public function instanceOfSelf(self $object);

    public function instanceOfParent(parent $object);
}

class A implements I1 {}

class B implements I2 {
    private function printBool(bool $b) {
        echo ($b ? 'true' : 'false') . "\n";
    }

    public function instanceOfSelf(I2 $object) {
        $this->printBool($object instanceof I2);
    }

    public function instanceOfParent(I1 $object) {
        $this->printBool($object instanceof I1);
    }                        
}

$a = new A;
$b = new B;

$b->instanceOfSelf($b);   // true
$b->instanceOfParent($a); // true
$b->instanceOfParent($b); // true

This produces the following error (PHP 5.6.30, 7.3.3, 7.4.0):

Fatal error: Declaration of B::instanceOfParent(I1 $object) must be compatible with I2::instanceOfParent(parent $object)

  1. So if I1 is not the parent of I2, who is?

  2. Is this a known bug, that it is possible to type hint parent in an interface without the ability to implement this feature?

1 Answers

PHP 7.4 will thrown new deprecation error message if the parent pseudo-type is used in places that do not have a parent type. An interface is one example of it.

When I execute your code I get 2 errors in PHP 7.4:

Deprecated: Cannot use "parent" when current class scope has no parent in /in/tOSug on line 8

Fatal error: Could not check compatibility between B::instanceOfParent(I1 $object) and I2::instanceOfParent(parent $object), because class parent is not available in /in/tOSug on line 22

This indicates where the problem is. You can't use parent in the interface.
You can fix this by replacing parent with the actual name of the interface:

// Duplication for completion...
interface I1 {}
interface I2 extends I1 {
    public function instanceOfSelf(self $object);

    public function instanceOfParent(I1 $object);
}

class A implements I1 {}

class B implements I2 {
    private function printBool(bool $b) {
        echo ($b ? 'true' : 'false') . "\n";
    }

    public function instanceOfSelf(I2 $object) {
        $this->printBool($object instanceof I2);
    }

    public function instanceOfParent(I1 $object) {
        $this->printBool($object instanceof I1);
    }                        
}

$a = new A;
$b = new B;

$b->instanceOfSelf($b);   // true
$b->instanceOfParent($a); // true
$b->instanceOfParent($b); // true

Interstingly enough type hinting with parent is not documented in PHP Manual under Type declarations

Explanation why the code doesn't behave as you would expect it.

The keyword parent always refers to the parent class, or in other words the class you have extended from. In your example the B class has no parent. It only implements an interface.

class B  {
    public function amIanOrphan() {
        var_dump(parent::class);
    }             
}

$b = new B; 
$b->amIanOrphan();

This code will throw error:

Fatal error: Uncaught Error: Cannot use "parent" when current class scope has no parent

Interfaces are not classes in themselves. Interface only tells you which methods the class has.
This code is exactly the same as above:

interface I1 {
    public function amIanOrphan();
}

interface I2 extends I1 {
}

class B implements I2 {
    public function amIanOrphan() {
        var_dump(parent::class);
    }
}

The interface extends from another one, but the class itself still has got no parent. The code above would still throw an error, because B has no parent, and the same logic applies to type hinting.

Related