为什么我们需要检查用户是否是 UserInterface 的实例

Why do we need to check if user is instance of UserInterface

我注意到在 FOSUserBundle 控制器 (ProfileController) 中检查 $user 是否是 UserInteface

的实例
$user = $this->getUser();
if (!is_object($user) || !$user instanceof UserInterface) {
    throw new AccessDeniedException('This user does not have access to this section.');
}

只检查 if (!is_object($user)) 就够了吗?

如果我的用户实体扩展 FOS\UserBundle\Model\User,在这种情况下 $user 将不是 UserInterface 的实例?

是的,这对于新老开发者来说有点陌生。

接口允许多重继承。有人告诉我,当 classes 最好被描述为 "is a" 时,你使用继承,就像狗是动物或 SwiftMailer 是邮件程序。

接口然后可以用来插入额外的功能,它就像一个合同,说这个 class 必须实现一些方法。像树皮或邮件。我被告知这些接口应该命名为 canBark 或 Barkable 或 Mailable 等,然后这些接口将实现 bark 或 mail 等方法。

但现代开发更倾向于使用接口作为额外的抽象,因此您可以快速交换 classes。

因此,您可以绑定到用户 class 将实现的用户界面,而不是绑定到您的用户 class。

所以要回答您的实际问题,只要 FOS\UserBundle\Model\User class 或您的用户 class 实现了 UserInterface 接口,那么您就可以开始了。

是的,如果您的代码不是开源的,否则不是。

不检查对象的实例并不能确保通过方法 getUser() 编辑的对象 return 将具有您期望的所有方法(示例:getUsername())。

如果您查看 Controller.php 中的 getUser() 方法,它不一定是 return 用户对象。事实上,你可以设置 Symfony2 防火墙 getUser() return 不同实例的不同对象。

承认我们有一个定义了 getUsername().

的接口 UserInterface

在下面的代码中,我们的User对象没有实现UserInterface.

$user = $this->getUser();
if (!is_object($user)) {
    $user->getUsername();
}

此代码将引发错误,因为 getUsername() 在对象上不存在,而代码应该如下所示:

$user = $this->getUser();
if (!is_object($user) || !$user instanceof UserInterface) {
    $user->getUsername();
}

如果用户对象没有实现正确的接口,那么代码不会出错,因为它不会被执行。

避免检查如下对象

$user = $this->getUser();
if (!is_object($user) || !$user instanceof User) {
    $user->getRoles();
}

如果有人扩展了 User 对象,则 if 语句将不再执行,因为 $user 将不是 User 的实例,而是说 ExtendedUser,即使它具有所有你需要的方法。

使用接口的另一个优点是您可以在一个对象上实现多个接口。

class A implements C {}

class B extends A implements C, D {}

interface C {}

interface D {}

$nA = new A();
$nB = new B();

$nA instanceof A; // true - instance of A
$nA instanceof B; // false - pretty obvious, no relationship with B
$nA instanceof C; // true - A implements C
$nA instanceof D; // false - A does not implement D

$nB instanceof A; // false - B is not an instance of A
$nB instanceof B; // true - instance of B
$nB instanceof C; // true - A implements C, that's the key:
                  //        both A and B implements C but B is not an
                  //        instance of A.
$nB instanceof D; // true - A implements D

TLDR;界面是设定期望和避免重大麻烦的好方法。

通读代码后,您可以快速识别传递的对象类型。如果有人更改代码,它要么显示有意义的错误,要么正常降级(在这种情况下,用户将被拒绝访问)。

If my user entity extends FOS\UserBundle\Model\User, in which case $user will not be instance of UserInterface?

这不是真的,因为 FOS\UserBundle\Model\User 实现 FOS\UserBundle\Model\UserInterface,它扩展(接口扩展其他接口)Symfony\Component\Security\Core\User\AdvancedUserInterface,它扩展 Symfony\Component\Security\Core\User\UserInterface。所以 $user instanceof UserInterface 将是真实的。

接口是面向对象世界中的契约。使用 is_object($user),你知道 $user 是一个对象,但你不知道对象有哪些 public 方法,等等。没有什么能阻止 $this->getUser() 返回一个完全不同的对象,破坏你的代码。当您检查实例时,您有一个承诺:接口中的方法对您可用。通常,我建议您永远不要调用您没有明确输入提示或检查使用 instanceof.

的方法

Wouter J 是对的:FOS\UserBundle\Model\User 实现了 FOS\UserBundle\Model\User 接口,但如果您不在文件中附加 "use FOS\UserBundle\Model\UserInterface;",那么 instanceof 测试将不会通过。别忘了。