Php 将接口传递给另一个接口方法

Php 将接口传递给另一个接口方法,php,oop,covariance,contravariance,php-7.4,Php,Oop,Covariance,Contravariance,Php 7.4,我需要在OOP中实现一个事件侦听器模型。现在我有了EventInterface和ListenerInterface。但我想将事件传递给侦听器,如下所示: interface ListenerInterface { public function listen(EventInterface $event); } 但是,即使我传递给侦听器的事件实现了EventInterface,我也不能这样做。那我该怎么办?我想把它抽象成简单的用法 PhpStorm的屏幕截图: 您的SendUserNotif

我需要在OOP中实现一个事件侦听器模型。现在我有了
EventInterface
ListenerInterface
。但我想将事件传递给侦听器,如下所示:

interface ListenerInterface {
  public function listen(EventInterface $event);
}
但是,即使我传递给侦听器的
事件实现了
EventInterface
,我也不能这样做。那我该怎么办?我想把它抽象成简单的用法

PhpStorm的屏幕截图:

您的
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;
}
任何实现
原始HandlerInterface
的类都需要严格遵循它

例如

如果您试图使实现在参数上使用协方差,它将失败,因为实现将不遵循接口。例如

class WrongOriginalImplementor implements OriginalHandlerInterface
{
    public function handle(ChildInterface $a) : string
    {
        return class_name($a);
    }
}
这样做的逻辑简单而合理。如果有人正在使用您创建的类,该类声明实现了
OriginalHandlerInterface
,那么该用户根本不需要查看您的实现,他们只需要查看接口

如果原始接口声明
handle()
需要一个
ParentInterface
对象,这意味着我可以将实现
ParentInterface
的类中的对象传递给它,但实现
ChildInterface
另一个ChildInterface
的类中的对象也将有效

另一方面,如果您的实现能够说
handle(ChildInterface$a)
,那么用户的期望就会被背叛,因为他们将无法从实现另一个ChildInterface的类中传递
handle()
对象,尽管根据
原始HandlerInterface
有效

另一方面,如果您有此接口:

interface AnotherHandlerInterface
{ 
    public function handle(ChildInterface $a): string;
}
您的实现可以在
handle()
参数上使用逆变。他们可以指定限制较少的参数类型。例如:

此实现,尽管不遵循点,是有效的。同样,如果您考虑一下,同样的逻辑也适用:您的类的使用者可以查看另一个HandlerInterface
,并看到
handle()
需要一个
ChildInterface
。因此,如果用户遵循原始合同,他们将永远不会被您的实现绊倒

您的实现限制较少,并且接受原始接口不会接受的对象,但这很好,因为协方差和逆变规则允许这样做

请注意,协方差和逆变支持在PHP中是非常新的


您可以看到上面的代码正在运行(包括错误)

您应该能够做到这一点。为什么你认为你不能呢?也给它一个接口名。只要您传入的对象实现了该接口,您就可以了。UserLoggeDiEvent应该实现ListenerInterface,这就是消息所说的,只要它不符合逻辑,那么您应该再次检查逻辑,因为屏幕截图中的代码与界面显示中的代码不匹配。在您显示的代码中,接口仅声明方法
listen()
,但在您的屏幕截图中,它抱怨方法
\uu construct()
的签名。请为这个例子添加适当的代码(实际的接口和实现类)作为代码,而不是屏幕截图,这样我可以给你一个正确的答案。@Alexosipov,问题已经解决了,但如果你能用正确的代码更新它,那就太好了。此外,对于以下答案的反馈,我们将不胜感激。
interface AnotherHandlerInterface
{ 
    public function handle(ChildInterface $a): string;
}
class ContravariantImplementor implements AnotherHandlerInterface
{
    public function handle(ParentInterface $a): string {
        return class_name($a);
    }
}