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

问题描述 投票:0回答:8

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

php late-binding late-static-binding
8个回答
219
投票

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

基本上,归结为

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

后期静态绑定引入了

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

这就是它背后的两个基本概念。

self
parent
static
static
运行时的操作方式可能很微妙,因此我强烈建议您学习手册页示例,而不是深入了解更多细节。一旦您了解了每个关键字的基础知识,这些示例对于了解您将获得什么样的结果是非常必要的。


86
投票

来自 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::
介绍其作用域。


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 类中删除类名函数的声明,我们将得到 'alphaalpha' 作为结果。


14
投票

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

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

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

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


解释 Late Static Binding 最清晰的方法是用一个实际的例子。我在模板方法模式中使用它。见下文。

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();

注意方法

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

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

var_dump
someFunction
的输出将是:

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

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


12
投票

显示差异的最简单示例。
注意,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

7
投票

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

对于

self
,上下文是您最初定义方法的地方。使用
static
,它就是您从中调用它的人。


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();

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;        
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.