Реализация типа подсказки «родитель» в интерфейсе выдает ошибку - PullRequest
1 голос
/ 01 июля 2019

В интерфейсе можно ввести подсказку self:

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

Также возможно ввести подсказку parent в интерфейсе, что означает, что следующий код не выдает ошибку:

interface I1 {}

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

    public function instanceOfParent(parent $object);
}

Однако, когда тип подсказывает I1 в соответствии с I2::instanceOfParent, возникает ошибка:

// 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

Это приводит к следующей ошибке (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. Итак, если I1 не является родителем I2, то кто?

  2. Это известная ошибка, что можно ввести подсказку parent в интерфейсе без возможности реализовать эту функцию?

1 Ответ

2 голосов
/ 01 июля 2019

PHP 7.4 выдаст новое сообщение об ошибке устаревания , если псевдотип parent используется в местах, которые не имеют родительского типа. Интерфейс является одним из примеров этого.

Когда я выполняю ваш код, я получаю 2 ошибки в PHP 7.4 :

Не рекомендуется: нельзя использовать «родительский», если в текущей области класса нет родительского элемента в / in / tOSug в строке 8

Неустранимая ошибка: не удалось проверить совместимость между B :: instanceOfParent (объект I1 $) и I2 :: instanceOfParent (родительский объект $), поскольку родительский класс недоступен в / in / tOSug в строке 22 * ​​1013 *

Это указывает, где проблема. Вы не можете использовать parent в интерфейсе.
Вы можете исправить это, заменив parent фактическим именем интерфейса:

// 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

Достаточно интересно, что подсказка типа с помощью parent не документирована в Руководстве по PHP под Объявления типов

Объяснение, почему код не ведет себя так, как вы ожидаете.

Ключевое слово parent всегда относится к родительскому классу или, другими словами, к классу, который вы расширили. В вашем примере класс B не имеет родителя. Он реализует только интерфейс.

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

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

Этот код выдаст ошибку:

Неустранимая ошибка: необученная ошибка: невозможно использовать «родительский элемент», если в области действия текущего класса нет родительского элемента

Интерфейсы сами по себе не являются классами. Интерфейс только сообщает вам, какие методы у класса.
Этот код точно такой же, как указано выше:

interface I1 {
    public function amIanOrphan();
}

interface I2 extends I1 {
}

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

Интерфейс расширяется от другого, но сам класс все еще не имеет родителя. Приведенный выше код все равно выдаст ошибку, потому что B не имеет родителя, и та же логика применяется к подсказке типа.

...