PHP延迟静态绑定的深入讲解

这篇文章主要给大家介绍了关于PHP延迟静态绑定的相关资料,这是最近工作中遇到的一个功能,通过查找相关的资料整理了这篇文章,分享出来供大家参考学习,需要的朋友们下面随着小编来一起学习学习吧。

前言

所谓延迟静态绑定,顾名思义,静态调用时::符号左侧的部分的的绑定是延迟,也就是说不再被解析为定义当前方法所在的类,而是在实际运行时计算的,本文主要介绍了关于PHP延迟静态绑定的相关内容,下面话不多说了,来一起看看详细的介绍吧。

嗅到了坏的味道

这段时间看项目后台的PHP代码,看到了类似于以下的一段代码,我把它抽出来:

  1. <?php
  2. class DBHandler {
  3. function get() {}
  4. }
  5. class MySQLHandler extends DBHandler {
  6. // 这里一个create
  7. public static function create() {
  8. echo "MySQL";
  9. return new self();
  10. }
  11. public function get() {
  12. echo "MySQL get()";
  13. }
  14. }
  15. class MemcachedHandler extends DBHandler {
  16. // 这里又有一个create
  17. public static function create() {
  18. echo "Memcached";
  19. return new self();
  20. }
  21. public function get() {
  22. echo "Memcached get";
  23. }
  24. }
  25. function get(DBHandler $handler) {
  26. $handler->get();
  27. }
  28. $dbHandler = MySQLHandler::create();
  29. get($dbHandler);
  30. ?>

有没有嗅到坏代码的味道?可以看到,在MySQLHandler和MemcachedHandler类中,都有一个create函数,除掉我的输出语句,发现它们一模一样,这就是代码冗余。是的,需要进行代码重构。

进行简单的重构

代码重构无处不在,只要你想,你觉的有改进,就需要敲起键盘开始干活。来吧,对上面的代码进行重构,如下:

  1. <?php
  2. class DBHandler {
  3. public static function create() {
  4. echo "create";
  5. return new self();
  6. }
  7. function get() {}
  8. }
  9. class MySQLHandler extends DBHandler {
  10. public function get() {
  11. echo "MySQL get()";
  12. }
  13. }
  14. class MemcachedHandler extends DBHandler {
  15. public function get() {
  16. echo "Memcached get";
  17. }
  18. }
  19. function get(DBHandler $handler) {
  20. $handler->get();
  21. }
  22. $dbHandler = MySQLHandler::create();
  23. get($dbHandler);
  24. ?>

将create函数移到DBHandler类中,看起来还不错,至少少了一坨那糟糕的代码。

貌似是错的

运行一下,却发现,并没有打印出我们期望的 MySQL get() 。什么情况?这说明,并没有调用MySQLHandler的get函数,但是代码明明调用了啊,这说明, new self() 这句代码有问题。这有什么问题?这就需要说到今天总结的重点了————延迟静态绑定。

延迟静态绑定

在PHP5.3以后引入了延迟静态绑定。再看下面这段代码:

  1. <?php
  2. class A {
  3. public static function who() {
  4. echo __CLASS__;
  5. }
  6. public static function test() {
  7. self::who();
  8. }
  9. }
  10. class B extends A {
  11. public static function who() {
  12. echo __CLASS__;
  13. }
  14. }
  15. B::test();
  16. ?>

上面的代码输出了A,但是我希望它输出B,这就是问题的所在。这也是 self 和 __CLASS__ 的限制。使用 self:: 或者 __CLASS__ 对当前类的静态引用,取决于定义当前方法所在的类。所以,这就很好的解释了为什么上面的代码输出了A。但是,如果我们需要输出B呢?可以这么干:

  1. <?php
  2. class A {
  3. public static function who() {
  4. echo __CLASS__;
  5. }
  6. public static function test() {
  7. static::who(); // 这里有变化,后期静态绑定从这里开始
  8. }
  9. }
  10. class B extends A {
  11. public static function who() {
  12. echo __CLASS__;
  13. }
  14. }
  15. B::test();
  16. ?>

后期静态绑定本想通过引入一个新的关键字表示运行时最初调用的类来绕过限制。简单地说,这个关键字能够让你在上述例子中调用 test() 时引用的类是 B 而不是 A。最终决定不引入新的关键字,而是使用已经预留的 static 关键字。

这就是后期静态绑定的根本————static关键字的另类用法。对于文章一开始的例子,可以这么改:

return new static(); // 改变这里,后期静态绑定

这种使用后期静态绑定,在使用PHP实现23中设计模式的时候,你会感到很轻松的。