php static:: 和 self:: 有什么区别 php静态绑定中static与self的差异

self::指向定义时的类,static::指向运行时调用类。例如Base类中test()方法用self::who()始终调用Base的who(),而static::who()在Child类调用时会调用Child的who(),实现静态多态性。

php static:: 和 self:: 有什么区别 php静态绑定中static与self的差异

self::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

static::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

在 PHP 中最核心的区别在于它们引用的“当前类”的定义:

self::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

总是指向定义当前方法或属性的那个类,而

static::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

则指向运行时实际调用方法的那个类。这个差异正是 PHP “后期静态绑定”(Late Static Binding)机制的关键,它赋予了静态方法和属性在继承体系中更强的多态性。

解决方案

要理解

self::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

static::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

的差异,我们需要深入到 PHP 的继承和静态调用的机制中。当我们在一个类中使用

self::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

引用静态成员(方法或属性)时,这个引用是“硬编码”的,它在编译时就已经确定了,指向的就是

self::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

所在代码块所属的那个类。它不会因为子类继承并调用了这个方法而改变。

static::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

则不同,它引入了“后期静态绑定”的概念。这意味着

static::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

的引用是在运行时动态解析的,它会指向最开始发起调用的那个类。如果一个子类继承了父类的一个方法,并且这个方法内部使用了

static::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

,那么当子类调用这个方法时,

static::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

就会指向子类本身,而不是父类。

我们来看一个经典的例子,这能最直观地展现它们的行为差异:

立即学习PHP免费学习笔记(深入)”;

class Base {
    public static function who() {
        echo __CLASS__; // __CLASS__ 总是返回当前代码所在的类名
    }

    public static function test() {
        echo "self::who() output: ";
        self::who(); // 总是调用 Base::who()
        echo "/n";

        echo "static::who() output: ";
        static::who(); // 调用运行时实际发起调用的类的 who() 方法
        echo "/n";
    }
}

class Child extends Base {
    public static function who() {
        echo __CLASS__; // 覆盖了父类的 who() 方法
    }
}

echo "--- Calling from BaseClass ---/n";
Base::test();
// 预期输出:
// self::who() output: Base
// static::who() output: Base

echo "/n--- Calling from ChildClass ---/n";
Child::test();
// 预期输出:
// self::who() output: Base
// static::who() output: Child
登录后复制

从上面的输出你可以清楚地看到:

  • Base::test()
    登录后复制

    被调用时,

    self::who()
    登录后复制
    登录后复制

    static::who()
    登录后复制
    登录后复制

    都指向

    Base
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制

    类,因为

    Base
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制

    是原始调用者,且

    self::
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制

    所在的

    test
    登录后复制
    登录后复制

    方法就在

    Base
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制

    中定义。

  • Child::test()
    登录后复制

    被调用时,

    self::who()
    登录后复制
    登录后复制

    依然指向

    Base
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制

    类,因为

    test
    登录后复制
    登录后复制

    方法是在

    Base
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制

    类中定义的,

    self::
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制

    牢牢绑定在那里。但

    static::who()
    登录后复制
    登录后复制

    则指向了

    Child
    登录后复制
    登录后复制
    登录后复制

    类,因为它追踪到了最初发起调用的类是

    Child
    登录后复制
    登录后复制
    登录后复制

    ,并且

    Child
    登录后复制
    登录后复制
    登录后复制

    覆盖了

    who()
    登录后复制

    方法。这就是后期静态绑定的魔力。

为什么PHP需要后期静态绑定(Late Static Binding)?

在 PHP 5.3 之前,我们只有

self::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

这样的引用方式。这在处理继承链中的静态方法时,常常会遇到一个令人头疼的问题,即所谓的“静态方法多态性缺失”。简单来说,如果父类有一个静态方法

A
登录后复制
登录后复制

,它内部又调用了另一个静态方法

B
登录后复制
登录后复制
登录后复制
登录后复制

(使用

self::B()
登录后复制
登录后复制

),而子类覆盖了

B
登录后复制
登录后复制
登录后复制
登录后复制

方法。那么当子类调用

A
登录后复制
登录后复制

方法时,期望的是调用子类覆盖后的

B
登录后复制
登录后复制
登录后复制
登录后复制

方法,但实际上,

self::B()
登录后复制
登录后复制

仍然会调用父类的

B
登录后复制
登录后复制
登录后复制
登录后复制

方法。这显然违背了面向对象的多态原则,让静态方法在继承体系中显得非常僵硬。

举个例子,假设我们有一个基类

Logger
登录后复制

,它有一个

log()
登录后复制
登录后复制

方法,内部调用

self::getPrefix()
登录后复制

来获取日志前缀。

class OldLogger {
    protected static function getPrefix() {
        return "LOG: ";
    }

    public static function log($message) {
        echo self::getPrefix() . $message . "/n";
    }
}

class ErrorLogger extends OldLogger {
    protected static function getPrefix() {
        return "ERROR: "; // 期望这个前缀被使用
    }
}

echo "OldLogger::log('Message');/n";
OldLogger::log('Message'); // 输出: LOG: Message

echo "ErrorLogger::log('Error Message');/n";
ErrorLogger::log('Error Message'); // 输出: LOG: Error Message (问题所在!)
登录后复制

你看,

ErrorLogger::log()
登录后复制

竟然输出了 “LOG: Error Message”,而不是我们期望的 “ERROR: Error Message”。这就是

self::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

的局限性,它使得

getPrefix()
登录后复制
登录后复制

的调用始终绑定在

OldLogger
登录后复制

类上,无法实现子类对静态方法的“多态”覆盖。

为了解决这个问题,PHP 5.3 引入了后期静态绑定,并提供了

static::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

关键字。有了

static::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

,上面的

log
登录后复制

方法就可以这样写:

造梦阁AI

造梦阁AI

AI小说推文一键成片,你的故事值得被看见

造梦阁AI139


查看详情
造梦阁AI

class NewLogger {
    protected static function getPrefix() {
        return "LOG: ";
    }

    public static function log($message) {
        echo static::getPrefix() . $message . "/n"; // 使用 static::
    }
}

class NewErrorLogger extends NewLogger {
    protected static function getPrefix() {
        return "ERROR: ";
    }
}

echo "NewLogger::log('Message');/n";
NewLogger::log('Message'); // 输出: LOG: Message

echo "NewErrorLogger::log('Error Message');/n";
NewErrorLogger::log('Error Message'); // 输出: ERROR: Error Message (这正是我们想要的!)
登录后复制

通过

static::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

log()
登录后复制
登录后复制

方法现在能够正确地识别出

NewErrorLogger
登录后复制
登录后复制

是原始调用者,并调用

NewErrorLogger
登录后复制
登录后复制

中覆盖的

getPrefix()
登录后复制
登录后复制

方法,从而实现了静态方法的多态性,让继承体系中的代码更加灵活和符合直觉。

在实际项目中,何时优先使用

self::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

,何时使用

static::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

在日常开发中,选择

self::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

还是

static::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

并非随意,它取决于你想要实现的行为以及对代码未来可扩展性的考量。我个人在实践中,通常会根据以下原则进行权衡:

优先使用

self::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

的场景:

  1. 明确指向当前定义类: 当你无论如何都希望引用到当前方法定义所在的那个类的成员时,

    self::
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制

    是最明确的选择。它就像一个硬锚点,确保无论哪个子类调用,它都指向同一个地方。

  2. 访问常量: 在 PHP 8.2 之前,类常量并不受后期静态绑定影响,

    self::CONSTANT_NAME
    登录后复制

    是访问常量的标准方式。即便在 PHP 8.2 之后

    static::CONSTANT_NAME
    登录后复制

    也支持 LSB,但如果你希望常量引用是固定不变的,不随子类调用而改变,那么

    self::
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制

    仍然是更安全、更明确的选择。

  3. 调用父类特定实现: 如果你在一个子类中,需要明确调用父类中某个静态方法的原始实现,而不是子类可能覆盖的版本,那么

    parent::method()
    登录后复制

    是首选,但如果是在父类内部,需要调用同级方法且不希望被子类影响,

    self::
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制

    也可以达到类似效果。

优先使用

static::
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

的场景:

  1. 实现静态多态性: 这是

    static::
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制

    最主要的设计目的。当你希望子类能够覆盖父类的静态方法或属性,并且通过父类的方法调用时,能动态地解析到子类的实现,那么

    static::
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制

    是不可或缺的。

  2. 工厂方法(Factory Methods):

    return new static();
    登录后复制

    是一个非常强大的模式。它允许你在一个父类中定义一个静态的工厂方法,该方法能够创建并返回实际调用这个工厂方法的那个类的实例。这在构建可扩展的类库时非常有用。

    class Car {
        public static function create() {
            return new static(); // 返回 Car 或其子类的实例
        }
    }
    
    class Sedan extends Car {}
    class SUV extends Car {}
    
    $sedan = Sedan::create(); // $sedan 是 Sedan 的实例
    $suv = SUV::create();     // $suv 是 SUV 的实例
    登录后复制
  3. 动态配置或状态管理: 当你希望静态属性(例如配置项、状态标志)能够在继承链中被子类“覆盖”时,使用

    static::$property
    登录后复制

    可以确保你总是访问到最具体(即调用者)的类定义的那个属性值。

    class Settings {
        protected static $theme = 'default';
    
        public static function getTheme() {
            return static::$theme; // 允许子类覆盖
        }
    }
    
    class AdminSettings extends Settings {
        protected static $theme = 'admin';
    }
    
    echo Settings::getTheme() . "/n";      // default
    登录后复制

以上就是php static:: 和 self:: 有什么区别 php静态绑定中static与self的差异的详细内容,更多请关注php中文网其它相关文章!

https://www.php.cn/faq/1521243.html

发表回复

Your email address will not be published. Required fields are marked *