PHP 动态绑定

除了限制访问,访问方式同样决定了在有方法或属性重载的子类中,哪些方法可以被调用或者哪些属性可以被访问。函数调用与函数相应代码之间的关联,和成员访问与变量的内存位置之间的关联,这就叫做绑定。

在计算机语言中,有两种主要的绑定类型:静态绑定和动态绑定。静态绑定匹配数据结构的引用和数据结构本身。静态绑定发生在程序编译时期,所以不能使用任何运行时的信息。它将函数调用与函数体匹配,将变量与变量的内存块匹配。由于 PHP 是动态语言,它不使用静态绑定。然而,PHP 的有些地方会模拟静态绑定。

动态绑定使用只有运行时才可用的信息在运行时匹配访问请求。在面向对象的代码中,动态绑定意味着基于类的 this 指针来决定调用哪个函数或访问哪个属性,而不是基于访问的范围。

public 的和 protected 的成员的行为和上一个版本的 PHP 中的成员的行为相似,都是使用动态绑定。这意味着,如果访问一个在子类中重写过的方法,并且 this 指针是子类的一个实例的,那么访问到的将会是子类的方法。

思考下面的代码:这些代码将会输出“Hey!I am Son.”。因为 PHP 执行到 getSalutation 方法时, this 指针是重写过 salutation 的类 Son 的一个实例。如果 salutation 是 public 的,PHP 会产生同样的结果。方法重写的运作也一样,调用 identify 绑定的是子类 Son 中的方法。

即使访问类型在派生类中减弱——从 protected 变成 public,动态绑定也会发生。根据访问类型的访问规则,不可能增加一个类成员的访问限制。也就是说,将一个访问类型从 public 改变成 protected 是不可以的。

  1. <?php  
  2. class Father  
  3. {  
  4.     protected   $salutation = "Hello there!";  
  5.   
  6.     public function getSalutation()  
  7.     {  
  8.         print("$this->salutation ");  
  9.         $this->identify();  
  10.     }  
  11.   
  12.     protected function identify()  
  13.     {  
  14.         print("I am Father. ");  
  15.     }  
  16. };  
  17.   
  18. class Son extends Father  
  19. {  
  20.     protected   $salutation = "Hey!";  
  21.   
  22.     protected function identify()  
  23.     {  
  24.         print("I am Son. ");  
  25.     }  
  26. };  
  27.   
  28. $obj = new Son();  
  29. $obj->getSalutation();  
  30. ?>  

private 成员只存在于包含它们的类里面。不像 public 和 protected 成员,PHP 对 private 成员是模拟静态绑定的方式。考虑接下来的代码。它会输出“Hello there!I am Father.”,尽管子类重写了 salutation 的值。脚本必须将 this->salutation 绑定到它直接的类 Father 中。同样的规则也适用与 private 方法 identify。

  1. <?php  
  2. class Father  
  3. {  
  4.     private $salutation = "Hello there!";  
  5.   
  6.     public function getSalutation()  
  7.     {  
  8.         print("$this->salutation ");  
  9.         $this->identify();  
  10.     }  
  11.   
  12.     private function identify()  
  13.     {  
  14.         print("I am Father. ");  
  15.     }  
  16. };  
  17.   
  18. class Son extends Father  
  19. {  
  20.     private $salutation = "Hey!";  
  21.   
  22.     protected function identify()  
  23.     {  
  24.         print("I am Son. ");  
  25.     }  
  26. };  
  27.   
  28. $obj = new Son();  
  29. $obj->getSalutation();  
  30. ?>  

动态绑定的优势在于,它允许派生类修改它们父类的行为,同时又可以保留父类的接口和功能。看下面的代码。感谢动态绑定,在 deleteUser 里面调用的 isAuthorized 方法的版本取决于我们对象的类型。如果 this 是一个普通的 User,PHP 会调用 User::isAuthorized,这将会返回 FALSE。如果 this 是 AuthorizedUser 的一个实例,PHP 将会调用 AuthorizedUser::isAuthorized,这会允许 deleteUser 像我们期待的那样去运作。

  1. <?php  
  2.    class User  
  3.    {  
  4.        protected function isAuthorized()  
  5.        {  
  6.            return(FALSE);  
  7.        }  
  8.   
  9.        public function getName()  
  10.        {  
  11.            return($this->name);  
  12.        }  
  13.   
  14.        public function deleteUser($username)  
  15.        {  
  16.            if(!$this->isAuthorized())  
  17.            {  
  18.                print("You are not authorized. ");  
  19.                return(FALSE);  
  20.            }  
  21.   
  22.            //delete the user  
  23.            print("User deleted. ");  
  24.        }  
  25.    }  
  26.   
  27.    class AuthorizedUser extends User  
  28.    {  
  29.        protected function isAuthorized()  
  30.        {  
  31.            return(TRUE);  
  32.        }  
  33.    }  
  34.   
  35.    $user = new User;  
  36.    $admin = new AuthorizedUser;  
  37.   
  38.    //not authorized  
  39.    $user->deleteUser("Zeev");  
  40.   
  41.    //authorized  
  42.    $admin->deleteUser("Zeev");  
  43. ?>  

为什么 private 的类成员要模拟静态绑定呢?为了回答这个问题,你需要回顾下一开始我们需要 private 成员的原因。也就是说,使用 private 和使用 protected 有什么区别。

只有当不想要派生类改变或者特殊化父类的行为时才会使用 private 成员。用到这种情况的时候很少。一般来说,好的对象分层应该允许大多数的功能被派生类特殊化、改善或者修改。这是面向对象编程的基础之一。有些情况是需要到 private 成员的,例如当你确定不想让派生类修改该类中某一特定的方面时。

(完)

注:英文原文来自《Core PHP Programming》第三版本,第六章的6.9小节

本文作者: chenishr

本文标题:《PHP 动态绑定》

本文地址: http://blog.chenishr.com/?p=216

©版权所有,除非注明, 永在路上文章均为原创,转载请以链接形式注明出处和作者细信息。

发表评论

电子邮件地址不会被公开。 必填项已用*标注