0

0

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

下次还敢

下次还敢

发布时间:2025-09-15 13:15:01

|

383人浏览过

|

来源于php中文网

原创

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
类上,无法实现子类对静态方法的“多态”覆盖。

Nanonets
Nanonets

基于AI的自学习OCR文档处理,自动捕获文档数据

下载

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

static::
关键字。有了
static::
,上面的
log
方法就可以这样写:

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

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1567

2023.10.24

go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

58

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

63

2025.11.27

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

27

2025.11.27

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

27

2025.11.27

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

27

2025.11.27

scripterror怎么解决
scripterror怎么解决

scripterror的解决办法有检查语法、文件路径、检查网络连接、浏览器兼容性、使用try-catch语句、使用开发者工具进行调试、更新浏览器和JavaScript库或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

492

2023.10.18

500error怎么解决
500error怎么解决

500error的解决办法有检查服务器日志、检查代码、检查服务器配置、更新软件版本、重新启动服务、调试代码和寻求帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

382

2023.10.25

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

37

2026.03.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP课程
PHP课程

共137课时 | 13.4万人学习

JavaScript ES5基础线上课程教学
JavaScript ES5基础线上课程教学

共6课时 | 11.3万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号