【问题标题】:What exactly are late static bindings in PHP?PHP 中的后期静态绑定到底是什么?
【发布时间】:2010-12-27 03:23:28
【问题描述】:

什么是 PHP 中的后期静态绑定?

【问题讨论】:

    标签: php late-binding late-static-binding


    【解决方案1】:

    您肯定需要阅读 PHP 手册中的Late Static Bindings。不过,我会尽量给你一个简短的总结。

    基本上,归结为self 关键字不遵循相同的继承规则。 self 始终解析为使用它的类。这意味着如果您在父类中创建一个方法并从子类中调用它,self 将不会像您预期的那样引用子类。

    后期静态绑定引入了static 关键字的新用途,它解决了这个特殊的缺点。当你使用static 时,它代表你第一次使用它的类,即。它“绑定”到运行时类。

    这是它背后的两个基本概念。当static 起作用时selfparentstatic 的操作方式可能很微妙,因此我强烈建议您研究手册页示例,而不是更详细地介绍。一旦您了解了每个关键字的基础知识,就非常有必要通过示例来了解您将获得什么样的结果。

    【讨论】:

    【解决方案2】:

    例如:

    abstract class Builder {
        public static function build() {
            return new static;
        }
    }
    
    class Member extends Builder {
        public function who_am_i() {
             echo 'Member';
        }
    }
    
    Member::build()->who_am_i();
    

    【讨论】:

      【解决方案3】:

      来自PHP: Late Static Bindings - Manual

      从 PHP 5.3.0 开始,PHP 实现了一种称为后期静态绑定的功能,可用于在静态继承的上下文中引用被调用的类。

      后期静态绑定试图通过引入一个关键字来解决这个限制,该关键字引用最初在运行时调用的类。 ...决定不引入新的关键字,而是使用已经保留的static

      我们来看一个例子:

      <?php
          class Car
          {
              public static function run()
              {
                  return static::getName();
              }
      
              private static function getName()
              {
                  return 'Car';
              }
          }
      
          class Toyota extends Car
          {
              public static function getName()
              {
                  return 'Toyota';
              }
          }
      
          echo Car::run(); // Output: Car
          echo Toyota::run(); // Output: Toyota
      ?>
      

      后期静态绑定通过存储在最后一个“非转发调用”中命名的类来工作。在静态方法调用的情况下,这是明确命名的类(通常是:: 运算符左侧的类);在非静态方法调用的情况下,它是对象的类。 “转发呼叫”是由self::parent::static:: 引入的静态呼叫,或者,如果在类层次结构中上升,forward_static_call()。函数get_called_class() 可用于检索带有被调用类名称的字符串,static:: 引入了它的作用域。

      【讨论】:

      • 这篇文章是 php.net article 的约 80% 的逐字副本,没有引用标记。
      【解决方案4】:

      没有很明显的行为:

      以下代码生成“alphabeta”。

      class alpha {
      
          function classname(){
              return __CLASS__;
          }
      
          function selfname(){
              return self::classname();
          }
      
          function staticname(){
              return static::classname();
          }
      }
      
      class beta extends alpha {
      
          function classname(){
              return __CLASS__;
          }
      }
      
      $beta = new beta();
      echo $beta->selfname(); // Output: alpha
      echo $beta->staticname(); // Output: beta
      

      但是,如果我们从 beta 类中删除 classname 函数的声明,我们会得到 'alphaalpha' 作为结果。

      【讨论】:

      【解决方案5】:

      我引用的书是:《PHP 高手写前沿代码》。

      后期静态绑定是 php 5.3 引入的一个特性。它允许 我们从父类继承静态方法,并引用 被调用的子类。

      这意味着你可以拥有一个带有静态方法的抽象类,并且 通过使用引用子类的具体实现 static::method() 表示法而不是 self::method()。

      您也可以查看官方 php 文档: http://php.net/manual/en/language.oop5.late-static-bindings.php


      解释后期静态绑定的最清晰方法是使用一个实际示例。我在模板方法模式中使用它。见下文。

      abstract class AbstractTemplate {
          
          public const AWESOME_LIST = [''];
          
          public function someFunction(): void {
              $awesomeList = $this->getAwesomeList();
      
              // OUTPUT: ['harry','henk','john'];
              var_dump($awesomeList); 
          }
      
      
          /**
           * This function gets static constants from CHILD classes
           */
          public function getAwesomeList(): array
          {
              return static::AWESOME_LIST;
          }
      }
      
      class ConcreteTemplate extends AbstractTemplate {
          
          public const AWESOME_LIST = ['harry','henk','john'];
          
          public function someFunction(): void {
              parent::someFunction();
          }
      }
      
      $concreteTemplate = new ConcreteTemplate();
      $concreteTemplate->someFunction();
      

      注意方法getAwesomeList 中的static 关键字。 现在让我们稍微改变一下:

      public function getAwesomeList(): array
      {
          return self::AWESOME_LIST;
      }
      

      var_dumpsomeFunction 的输出将是:

      array (size=1)
        0 => string '' (length=0)
      

      static 关键字用于单例设计模式。 见链接:https://refactoring.guru/design-patterns/singleton/php/example

      【讨论】:

        【解决方案6】:

        从“我为什么要使用它?”的角度来看它。从角度来看,它基本上是一种更改解释/运行静态方法的上下文的方法。

        使用self,上下文是您最初定义方法的上下文。使用static,它就是您调用它的来源。

        【讨论】:

          【解决方案7】:

          显示差异的最简单示例。
          注意,self::$c

          class A
          {
              static $c = 7;
          
              public static function getVal()
              {
                  return self::$c;
              }
          }
          
          class B extends A
          {
              static $c = 8;
          }
          
          B::getVal(); // 7
          

          后期静态绑定,注意static::$c

          class A
          {
              static $c = 7;
          
              public static function getVal()
              {
                  return static::$c;
              }
          }
          
          class B extends A
          {
              static $c = 8;
          }
          
          B::getVal(); // 8
          

          【讨论】:

            【解决方案8】:

            另外,请注意您是否更新子类中的静态变量。我发现了这个(有点)意外的结果,孩子 B 更新了孩子 C:

            class A{
                protected static $things;
            }
            
            class B extends A {
                public static function things(){
                    static::$things[1] = 'Thing B';
                    return static::$things; 
                }
            }
            
            class C extends A{
                public static function things(){
                    static::$things[2] = 'Thing C';
                    return static::$things;        
                }
            }
            
            print_r(C::things());
            // Array (
            //   [2] => Thing C
            // )
            
            B::things();
            
            print_r(C::things()); 
            // Array (
            //    [2] => Thing C
            //    [1] => Thing B
            // )
            

            您可以通过在每个子类中声明相同的变量来修复它,例如:

            class C extends A{
                protected static $things; // add this and B will not interfere!
            
                public static function things(){
                    static::$things[2] = 'Thing C';
                    return static::$things;        
                }
            }
            

            【讨论】:

              猜你喜欢
              • 2018-11-26
              • 2012-04-21
              • 1970-01-01
              • 1970-01-01
              • 2019-01-16
              • 1970-01-01
              • 1970-01-01
              • 2014-06-07
              • 2013-08-04
              相关资源
              最近更新 更多