Ваш SendUserNotificationListener
не соответствует договору, заключенному ListenerInterface
.
Ошибка услужливо говорит вам об этом. Если вы попытаетесь запустить этот код, вы получите фатальную ошибку, и ваш скрипт рухнет.
Если у вас есть пара таких интерфейсов:
interface ParentInterface {
public function foo();
}
interface ChildInterface extends ParentInterface {
public function bar();
}
interface AnotherChildInterface extends ParentInterface {
public function baz();
}
interface OriginalHandlerInterface
{
public function handle(ParentInterface $a): string;
}
Любой класс, реализующий OriginalHandlerInterfce
, должен точно следовать ему.
E.g.
class OriginalImplementor implements OriginalHandlerInterface
{
public function handle(ParentInterface $a) : string
{
return class_name($a);
}
}
Если вы попытаетесь заставить реализацию использовать ковариацию для параметра, это не удастся, так как реализация не будет следовать интерфейсу. Например.
class WrongOriginalImplementor implements OriginalHandlerInterface
{
public function handle(ChildInterface $a) : string
{
return class_name($a);
}
}
Логика этого проста и здрава. Если кто-то использует созданный вами класс, который объявляет, что реализует OriginalHandlerInterface
, этому пользователю вообще не нужно смотреть на вашу реализацию, ему нужно только смотреть на интерфейсы.
Если исходный интерфейс объявляет, что handle()
ожидает объект ParentInterface
, это означает, что я могу передать ему объекты из классов, реализующих ParentInterface
, но объекты из классов, реализующих ChildInterface
или AnotherChildInterface
, также будут действительными.
С другой стороны, если бы ваша реализация могла сказать handle(ChildInterface $a)
, то ожидания пользователя были бы обмануты, поскольку он не смог бы передать handle()
объект из класса, реализующего AnotherChildInterface
, несмотря на то, что он действителен в соответствии с OriginalHandlerInterface
.
С другой стороны, если бы у вас был этот интерфейс:
interface AnotherHandlerInterface
{
public function handle(ChildInterface $a): string;
}
В ваших реализациях может использоваться контравариантность для параметра handle()
. Они могут указать менее строгий тип параметра. Например.:
class ContravariantImplementor implements AnotherHandlerInterface
{
public function handle(ParentInterface $a): string {
return class_name($a);
}
}
Эта реализация, несмотря на то, что она не соответствует действительности, действительна. Опять же, если подумать, применима та же логика: потребитель вашего класса может посмотреть на AnotherHandlerInterface
и увидеть, что handle()
ожидает ChildInterface
. Таким образом, они никогда не будут сбиты с толку вашей реализацией, если они будут следовать исходному контракту.
Ваша реализация менее ограничительна и принимает объекты, которых не допускал исходный интерфейс, но это нормально, поскольку это разрешено правилами ковариантности и контравариантности.
Обратите внимание, что поддержка ковариантности и контравариантности появилась в PHP впервые, и была добавлена только в PHP 7.4.
Вы можете увидеть работающий приведенный выше код здесь (включая ошибку)
person
yivi
schedule
13.02.2020
listen()
, но на вашем снимке экрана он жалуется на сигнатуру метода__construct()
. Пожалуйста, добавьте соответствующий код для этого примера (фактический интерфейс и реализующий класс) в виде кода, а не скриншота, чтобы я мог дать вам правильный ответ. - person yivi   schedule 13.02.2020