除了限制访问,访问方式同样决定了在有方法或属性重载的子类中,哪些方法可以被调用或者哪些属性可以被访问。函数调用与函数相应代码之间的关联,和成员访问与变量的内存位置之间的关联,这就叫做绑定。
在计算机语言中,有两种主要的绑定类型:静态绑定和动态绑定。静态绑定匹配数据结构的引用和数据结构本身。静态绑定发生在程序编译时期,所以不能使用任何运行时的信息。它将函数调用与函数体匹配,将变量与变量的内存块匹配。由于 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 是不可以的。
- <?php
- class Father
- {
- protected $salutation = "Hello there!";
- public function getSalutation()
- {
- print("$this->salutation ");
- $this->identify();
- }
- protected function identify()
- {
- print("I am Father. ");
- }
- };
- class Son extends Father
- {
- protected $salutation = "Hey!";
- protected function identify()
- {
- print("I am Son. ");
- }
- };
- $obj = new Son();
- $obj->getSalutation();
- ?>
private 成员只存在于包含它们的类里面。不像 public 和 protected 成员,PHP 对 private 成员是模拟静态绑定的方式。考虑接下来的代码。它会输出“Hello there!I am Father.”,尽管子类重写了 salutation 的值。脚本必须将 this->salutation 绑定到它直接的类 Father 中。同样的规则也适用与 private 方法 identify。
- <?php
- class Father
- {
- private $salutation = "Hello there!";
- public function getSalutation()
- {
- print("$this->salutation ");
- $this->identify();
- }
- private function identify()
- {
- print("I am Father. ");
- }
- };
- class Son extends Father
- {
- private $salutation = "Hey!";
- protected function identify()
- {
- print("I am Son. ");
- }
- };
- $obj = new Son();
- $obj->getSalutation();
- ?>
动态绑定的优势在于,它允许派生类修改它们父类的行为,同时又可以保留父类的接口和功能。看下面的代码。感谢动态绑定,在 deleteUser 里面调用的 isAuthorized 方法的版本取决于我们对象的类型。如果 this 是一个普通的 User,PHP 会调用 User::isAuthorized,这将会返回 FALSE。如果 this 是 AuthorizedUser 的一个实例,PHP 将会调用 AuthorizedUser::isAuthorized,这会允许 deleteUser 像我们期待的那样去运作。
- <?php
- class User
- {
- protected function isAuthorized()
- {
- return(FALSE);
- }
- public function getName()
- {
- return($this->name);
- }
- public function deleteUser($username)
- {
- if(!$this->isAuthorized())
- {
- print("You are not authorized. ");
- return(FALSE);
- }
- //delete the user
- print("User deleted. ");
- }
- }
- class AuthorizedUser extends User
- {
- protected function isAuthorized()
- {
- return(TRUE);
- }
- }
- $user = new User;
- $admin = new AuthorizedUser;
- //not authorized
- $user->deleteUser("Zeev");
- //authorized
- $admin->deleteUser("Zeev");
- ?>
为什么 private 的类成员要模拟静态绑定呢?为了回答这个问题,你需要回顾下一开始我们需要 private 成员的原因。也就是说,使用 private 和使用 protected 有什么区别。
只有当不想要派生类改变或者特殊化父类的行为时才会使用 private 成员。用到这种情况的时候很少。一般来说,好的对象分层应该允许大多数的功能被派生类特殊化、改善或者修改。这是面向对象编程的基础之一。有些情况是需要到 private 成员的,例如当你确定不想让派生类修改该类中某一特定的方面时。
(完)
注:英文原文来自《Core PHP Programming》第三版本,第六章的6.9小节
本文作者: chenishr
本文标题:《PHP 动态绑定》
本文地址: http://blog.chenishr.com/?p=216
©版权所有,除非注明, 永在路上文章均为原创,转载请以链接形式注明出处和作者细信息。