PHP 后期静态绑定 ( Late Static Bindings ) 的使用

Xavier

后缀

后期静态绑定也就是 static:: 在类中的用法,首先需要明确一点,static:: 后可跟 类常量静态属性(类变量)静态方法普通方法(跟 类常量/静态属性/静态方法 时 static 代表一个类,跟 普通方法时 static 代表一个对象),不可以跟普通属性(对象的成员变量),因为语义上无法辨别是普通属性还是静态属性(语法都是 static::$xx)。

引入目的

引入后期静态绑定的目的是为了打破 self:: 关键词的限制,类中 self 的指向在编译时期便确定在了代码上下文中 self 所在的类。而 static:: 则是在运行时确定其指向。在继承关系中,便可看出 selfstatic 的区别。

使用场景

static:: 跟类常量的场景

class A {
    public const FOO = 'a';

    public function test() {
        echo self::FOO; // self 明确指向代码上下文中所在类 A,即使被子类调用,self 也是指向 A
    }
}

class B extends A {
    public const FOO = 'b';
}

(new B)->test(); // 输出 a
class A {
    public const FOO = 'a';

    public function test() {
        echo static::FOO; // static 在编译阶段不确定指向,运行时才确定指向,指向调用者,故称为 `后期绑定`。此处 static 后跟类常量,故 static 代表调用类,也就是 B 类。
    }
}

class B extends A {
    public const FOO = 'b';
}

(new B)->test(); // 输出 b

static:: 跟静态属性的场景(同理)

class A {
    public static $foo = 'a';

    public static function test() {
        echo self::$foo;
    }
}

class B extends A {
    public static $foo = 'b';
}

B::test(); // 输出 a
class A {
    public static $foo = 'a';

    public static function test() {
        echo static::$foo;
    }
}

class B extends A {
    public static $foo = 'b';
}

B::test(); // 输出 b

static:: 跟静态方法的场景

class A {
    public static function who() {
        echo __CLASS__; // __CLASS__ 编译时期绑定了 A 类名
    }
    public static function test() {
        self::who(); // self 在编译时期就指向了 A 类
    }
}

class B extends A {
    public static function who() {
        echo __CLASS__; // __CLASS__ 编译时期绑定了 B 类名
    }
}

B::test(); // A
class A {
    public static function who() {
        echo __CLASS__;
    }
    public static function test() {
        static::who(); // 后期绑定
    }
}

class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}

B::test(); // B

static:: 跟普通方法的场景

除了代替 self::static:: 在一些场景中也可代替 $this->
有种场景是这样的:当父类中的方法被子类调用时,方法中的 $this-> 会首先在其所在范围(代码上下文,即父类)里寻找私有方法或属性,如果寻找不到才会调用其子类的方法或属性。而 static 属于后期绑定,可绕过这一步,直接指向方法的调用者。

class A {
    private function foo() {
        echo "a\n";
    }

    public function test() {
        $this->foo(); // 所在范围中存在私有方法 foo,所以并不会调用 B 中的 foo 方法
    }
}

class B extends A {
    public function foo() {
        echo "b\n";
    }
}

(new B)->test(); // 输出 a
class A {
    private function foo() {
        echo "a\n";
    }

    public function test() {
        static::foo(); // 绕过检查,直接调用调用者的该方法
    }
}

class B extends A {
    public function foo() {
        echo "b\n";
    }
}

(new B)->test(); // 输出 b
阅读 693

Xavier 的技术博客
最近的关注重心: 1. 云原生 (Docker、Kubernetes) 2. 微服务 (网关 Kong、服务通讯 gRPC、通讯格式 Pro...

最近的关注重心:

407 声望
25 粉丝
0 条评论
你知道吗?

最近的关注重心:

407 声望
25 粉丝
文章目录
宣传栏