143

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

4

8 回答 8

212

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

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

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

这是它背后的两个基本概念。播放时的方式self和操作可能很微妙,因此我强烈建议您学习手册页示例,而不是更详细地介绍parent。一旦您了解了每个关键字的基础知识,这些示例对于了解您将获得什么样的结果是非常必要的。staticstatic

于 2009-12-16T07:57:51.410 回答
85

来自PHP:后期静态绑定 - 手册

自 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::引入其范围。

于 2012-12-22T07:10:34.543 回答
27

没有很明显的行为:

以下代码生成“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' 作为结果。

于 2013-11-19T14:06:02.957 回答
13

我引用这本书:“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();

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

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

var_dumpat的输出someFunction将是:

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

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

于 2014-06-18T12:29:29.910 回答
11

显示差异的最简单示例。
注意,自我::$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
于 2016-02-23T12:00:43.797 回答
7

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

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

于 2015-08-13T12:27:57.110 回答
5

例如:

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();
于 2010-09-09T06:13:22.977 回答
2

另外,请注意是否更新子类中的静态变量。我发现了这个(有点)意外的结果,孩子 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;        
    }
}
于 2019-05-03T16:26:54.407 回答