【问题标题】:Why do we need to check if user is instance of UserInterface为什么我们需要检查用户是否是 UserInterface 的实例
【发布时间】:2015-05-14 18:45:46
【问题描述】:

我注意到在 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 的实例?

【问题讨论】:

    标签: php symfony fosuserbundle


    【解决方案1】:

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

    不检查对象的实例并不能确保方法 getUser() 返回的对象将具有您期望的所有方法(例如:getUsername())。

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

    承认我们有一个定义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;接口是设定期望和避免重大问题的好方法。

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

    【讨论】:

      【解决方案2】:

      如果我的用户实体扩展FOS\UserBundle\Model\User,在这种情况下$user 将不是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 是一个对象,但您不知道该对象具有哪些公共方法,等等。没有什么可以阻止$this->getUser() 返回一个完全不同的对象,从而破坏您的代码。当您检查实例时,您有一个承诺:接口中的方法可供您使用。作为一项规则,我建议您永远不要调用未明确键入提示或检查是否使用 instanceof 的方法。

      【讨论】:

        【解决方案3】:

        是的,这对新老开发者来说有点奇怪。

        接口允许多重继承。有人告诉我,当类最好被描述为“is a”时,您可以使用继承,例如 dog 是动物或 SwiftMailer 是 Mailer。

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

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

        因此,您可以绑定到 User 类将实现的 UserInterface,而不是绑定到您的用户类。

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

        【讨论】:

        • 这不是为了快速交换类,而是确保这些类必须需要实现,需要的方法,以便现有代码可以在需要时调用这些,并且您不必破解他们的来源以满足您自己的班级
        • 接口不允许多重继承,php中没有多重继承。
        【解决方案4】:

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

        【讨论】:

          猜你喜欢
          • 2011-02-21
          • 1970-01-01
          • 1970-01-01
          • 2020-03-31
          • 1970-01-01
          • 2019-02-17
          • 1970-01-01
          • 1970-01-01
          • 2020-01-16
          相关资源
          最近更新 更多