ОБНОВЛЕНИЕ : PHP 7.4 теперь поддерживает ковариацию и контравариантность, что решает основную проблему, поднятую в этом вопросе.
У меня возникла проблема с использованием подсказки типа возвращаемого значения в PHP 7. Насколько я понимаю, подсказка : self
означает, что вы намереваетесь, чтобы реализующий класс возвращал себя. Поэтому я использовал : self
в своих интерфейсах, чтобы указать это, но когда я попытался фактически реализовать интерфейс, я получил ошибки совместимости.
Ниже приводится простая демонстрация проблемы, с которой я столкнулся:
interface iFoo
{
public function bar (string $baz) : self;
}
class Foo implements iFoo
{
public function bar (string $baz) : self
{
echo $baz . PHP_EOL;
return $this;
}
}
(new Foo ()) -> bar ("Fred")
-> bar ("Wilma")
-> bar ("Barney")
-> bar ("Betty");
Ожидаемый результат был:
Фред Вильма Барни Бетти
На самом деле я получаю:
Неустранимая ошибка PHP: Объявление Foo :: bar (int $ baz): Foo должно быть совместимо с iFoo :: bar (int $ baz): iFoo в test.php в строке 7
Дело в том, что Foo - это реализация iFoo, поэтому, насколько я могу судить, реализация должна быть полностью совместима с данным интерфейсом. По-видимому, я мог бы исправить эту проблему, изменив либо интерфейс, либо реализующий класс (или оба), чтобы возвращать подсказку интерфейса по имени вместо использования self
, но я понимаю, что семантически self
означает «вернуть экземпляр класса, который вы только что вызвали метод на ". Поэтому изменение его на интерфейс теоретически означало бы, что я мог бы вернуть любой экземпляр чего-то, что реализует интерфейс, когда мое намерение - это то, что будет возвращено для вызываемого экземпляра.
Это недосмотр в PHP или это сознательное дизайнерское решение? Если первое, то есть ли шанс увидеть его исправленным в PHP 7.1? Если нет, то каков правильный способ возврата, намек на то, что ваш интерфейс ожидает, что вы вернете экземпляр, который вы только что вызвали для связывания метода?
источник
self
должен работать возвращаемый тип?self
обозначения «Вернуть экземпляр, на котором вы его вызывали, а не какой-то другой экземпляр, реализующий тот же интерфейс». Кажется, я помню, что у Java был аналогичный тип возвращаемого значения (хотя я давно не занимался программированием на Java)Ответы:
self
не относится к экземпляру, это относится к текущему классу. Интерфейс не может указать, что должен быть возвращен один и тот же экземпляр - использованиеself
таким образом, как вы пытаетесь, только принудит, чтобы возвращаемый экземпляр был того же класса.Тем не менее, объявления возвращаемого типа в PHP должны быть инвариантными, в то время как то, что вы пытаетесь сделать, является ковариантным.
Ваше использование
self
эквивалентно:interface iFoo { public function bar (string $baz) : iFoo; } class Foo implements iFoo { public function bar (string $baz) : Foo {...} }
что не допускается.
Возвращаемый тип декларации RFC имеет это сказать :
На данный момент лучшее, что вы можете сделать, это:
interface iFoo { public function bar (string $baz) : iFoo; } class Foo implements iFoo { public function bar (string $baz) : iFoo {...} }
источник
static
сработает, но он даже не распознаетсяstatic
добавляется в PHP 8.Это также может быть решением, в котором вы не определяете явно тип возвращаемого значения в интерфейсе, только в PHPDoc, а затем вы можете определить определенный тип возвращаемого значения в реализациях:
interface iFoo { public function bar (string $baz); } class Foo implements iFoo { public function bar (string $baz) : Foo {...} }
источник
Foo
просто использоватьself
.В случае, если вы хотите принудительно использовать интерфейс, этот метод вернет объект, но тип объекта будет не типом интерфейса, а самим классом, тогда вы можете написать это так:
interface iFoo { public function bar (string $baz) : object; } class Foo implements iFoo { public function bar (string $baz) : self {...} }
Он работает с PHP 7.4.
источник
PHP 8 добавит «статический тип возврата», который решит вашу проблему.
Ознакомьтесь с этим RFC: https://wiki.php.net/rfc/static_return_type
источник
Мне это кажется ожидаемым поведением.
Просто измените свой
Foo::bar
метод, чтобы вернутьiFoo
вместоself
и сделать с ней.Пояснение:
self
как используется в интерфейсе, означает «объект типаiFoo
».self
как используется в реализации означает "объект типаFoo
».Следовательно, типы возвращаемых данных в интерфейсе и реализации явно не совпадают.
В одном из комментариев упоминается Java и будет ли у вас эта проблема. Ответ - да, у вас была бы такая же проблема, если бы Java позволяла писать такой код, а это не так. Поскольку Java требует, чтобы вы использовали имя типа вместо
self
ярлыка PHP , вы никогда этого не увидите. (См. Здесь обсуждение аналогичной проблемы в Java.)источник
self
похоже на декларированиеMyClass::class
?