我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

本文聚焦于php类中的静态方法的调用方式,涉及的内容有::->的使用,以及static::self::parent::的区别。同时涉及了一个“后期静态绑定”的概念。如何理解这个“后期静态绑定”的概念呢?请阅读本文的内容。

苏南大叔:php教程,类静态方法可以使用什么调用方式?有何不同? - 静态类方法调用方式
php教程,类静态方法可以使用什么调用方式?有何不同?(图1-1)

苏南大叔的“程序如此灵动”博客,记录苏南大叔的编程经验总结。本文测试环境:win10nginx@1.15.11php@8.2.10-nts

基本概念

本文涉及到了__CLASS__以及get_class()等概念,但是它们并不是本文的主角,而是用于验证本文关于 “static::self::parent::的区别”的结论的。

名称内容
CLASS当前的代码所在的实际的类
get_class()当前的代码所在的实际的类
get_called_class()获得后期静态绑定("Late Static Binding")类的名称。
get_parent_class()获得对象或者类的父类名字

如果您不是很清楚相关概念的话,可以参考下面这篇文章:

类外部,class::method() 和 class->method()

这个实际上说的是外部调用方法的形式。
在本文中,
在类定义外部,使用::或者->都可以合理的调用静态方法,并不会产生任何结果上的不同。但是推荐使用::这种表述方式。
对于非静态方法,是不可以使用::表述的,直接报错。报错信息类似如下:

Deprecated: Non-static method S::test() should not be called statically
调用方式说明
class::method()method()必须是静态方法
class->method()method()可能是静态方法,也可能是非静态方法

所以,对于定义在类里面的静态方法,实际上有三种调用方式。比如:

classs S {
  public static function test(){}
}

调用方式可以是:

S::test();
$s = new S();
$s->test();
$s::test();

方法内部,static::self::parent::

对于static::self::parent::这三种表达来说,都是::的表述,没有->的表述方式。而且也都是用于方法定义内部的,而不是类定义的外部。

名称内容
static::method()后期静态绑定,实际上就是考虑继承,继承类的method()可以覆盖父类的同名方法
self::method()不考虑继承,就是执行当前类定义中的method(),也就是说子类的同名方法覆盖失效【?】
parent::method()自身及子类的同名方法都失效,执着的执行父类中的对应方法【?】
class::method()直接指明类名,不考虑任何继承或者覆盖之类的概念
$this::method()伪命题,静态方法没有上下文,所以也没有$this的表述

可见:static::method()就是加了个前缀,然后起了一个新名字(后期静态绑定),实际上的概念还是大家以及理解的原始样子。就是包装了一个新的概念而已,糊弄人的。

根据百度百科的说法:
后期静态绑定的功能,始于PHP5.3开始,用于在继承范围内引用静态调用的类。该功能从语言内部角度考虑被命名为”后期静态绑定“。”后期绑定“的意思是说,static::不再被解析为定义当前方法所在的类,而是在实际运行时计算的。也可以称之为”静态绑定“,因为它可以用于(但不限于)静态方法的调用。

如果parent::或者self::method()代码是static::的,则本来被parent或者self缩小的范围,则继续被扩大寻找,到最外层寻找对应的method(),同时考虑继承关系,__CLASS__输出则考虑实际的代码位置。可以理解为类可选范围:缩小=》放大=》缩小的一个过程。【很烧脑】

测试代码一

class S
{
    public static function who()
    {
        echo "__CLASS__:",__CLASS__, PHP_EOL;
    }
}
class N extends S
{
    public static function who()
    {
        echo "__CLASS__:",__CLASS__, PHP_EOL;
    }
    public static function test()
    {
        static::who();  // N
        self::who();    // N => S
        N::who();       // N
        if(get_parent_class()){
            parent::who();  // S => 空
        }
        else{
            echo "\r\n";
        }
        S::who();       // S
    }
}
$n = new N;
$n::test();

子类N中覆盖了who方法。所以,static::后期静态绑定的是子类N

输出值:

__CLASS__:N
__CLASS__:N
__CLASS__:N
__CLASS__:S
__CLASS__:S

测试代码二

相同的代码,把test()方法移动到父类里面,如下所示。self::parent::的运算结果发生了变化。

class S
{
    public static function who()
    {
        echo "__CLASS__:",__CLASS__, PHP_EOL;
    }
    public static function test()
    {
        static::who();  // N
        self::who();    // N => S
        N::who();       // N
        if(get_parent_class()){
            parent::who();  // S => 空
        }
        else{
            echo "\r\n";
        }
        S::who();       // S
    }
}
class N extends S
{
    public static function who()
    {
        echo "__CLASS__:",__CLASS__, PHP_EOL;
    }
}
$n = new N;
$n::test();

输出:


__CLASS__:N
__CLASS__:S
__CLASS__:N

__CLASS__:S

测试代码三

这里用了三层继承,代码如下:

<?php
class S
{
    public static function who()
    {
        echo "__CLASS__S:",__CLASS__, PHP_EOL;
    }
}
class N extends S
{
    public static function who()
    {
        echo "__CLASS__N:",__CLASS__, PHP_EOL;
    }
    public static function test()
    {
        static::who();  // X=>N
        self::who();    // N
        if(get_parent_class()){
            parent::who();  // S
        }
    }
}

class X extends N
{
    public static function who()
    {
        echo "__CLASS__X:",__CLASS__, PHP_EOL;
    }
}
class X2 extends N {}
X::test();
X2::test();

输出:

__CLASS__X:X
__CLASS__N:N
__CLASS__S:S

__CLASS__N:N
__CLASS__N:N
__CLASS__S:S

可见:static::method()是随着调用者是否实际定义method()的变化而变化的。

思考题

下面这个parent::self::的执行结果,可能和想象的不同。虽然指向不同,但是内部都是static::这个需要考虑后期静态调用的...

class S
{
    public static function check()
    {
        static::who();  // 这个要考虑后期静态绑定
    }
    public static function who()
    {
        echo "<S>:", __CLASS__, PHP_EOL;
    }
}
class N extends S
{
    public static function test()
    {
        parent::check();
        self::check();
    }
    public static function who()
    {
        echo "<N>:", __CLASS__, PHP_EOL;
    }
}
class X_1 extends N
{
    public static function who()
    {
        echo "<X>:", __CLASS__, PHP_EOL;
    }
}
class X_2 extends N { }
X_1::test();  // X_1  X_1
X_2::test();  // N  N

相关文章

结束语

static::被称之为后期静态绑定,苏南大叔觉得就是给大家已知的概念加了个新的命名而已。考虑继承关系加方法覆盖关系得到的__CLASS__就是“后期静态绑定”。

更多来自苏南大叔的php经验文章解读,请参考:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   php