📘 PHP 继承与静态机制深度解析

📘 PHP 继承与静态机制深度解析

------ 从 extendsstatic 的完整知识体系(适合深度复习)

本文涵盖 PHP 7.4 ~ PHP 8.4 的继承规则、可见性、后期静态绑定、只读属性、内部类兼容性等核心机制,结合设计原则与实际案例,助你构建完整的 OOP 认知框架。


一、继承的本质:代码复用与契约承诺

✅ 什么是继承?

使用 extends 关键字,子类可以继承父类的 public 和 protected 成员(方法、属性、常量),实现功能复用。

scala 复制代码
class Animal {
    public function breathe() { echo "呼吸空气\n"; }
}
class Dog extends Animal {
    public function bark() { echo "汪汪叫\n"; }
}
(new Dog())->breathe(); // ✅ 继承自 Animal

🔑 继承不是"复制粘贴",而是一种 "is-a"关系Dog is an Animal


二、继承了什么?哪些不能继承?

成员类型 是否继承 说明
public 成员 ✅ 是 外部可访问
protected 成员 ✅ 是 子类内部可访问
private 成员 ❌ 否 仅父类内部可用,但可通过 parent:: 间接调用

🧠 重要补充:private 成员的"类级封装"

同一个类的不同实例,可以互访 private 成员!

php 复制代码
class BankAccount {
    private $balance = 0;
    public function transfer(BankAccount $other, $amount) {
        $this->balance -= $amount;
        $other->balance += $amount; // ✅ 合法!类内部可访问同类实例的 private 属性
    }
}

$a = new BankAccount();
$b = new BankAccount();
$b->transfer($a,1000);

✅ 原因:private类级别的封装,不是实例级别的。


三、可见性规则:只能"放宽",不能"收紧"

✅ 正确:放宽可见性(允许)

scala 复制代码
class Parent {
    protected function foo() {}
}
class Child extends Parent {
    public function foo() {} // ✅ OK:protected → public
}

❌ 错误:收紧可见性(违反 LSP)

scala 复制代码
class Parent {
    public function bar() {}
}
class Child extends Parent {
    protected function bar() {} // ❌ Fatal Error!
}

🔥 原因:违反 里氏替换原则(LSP)

"子类对象应能替换父类对象而不破坏程序行为。"

如果 Childbar() 变成 protected,外部代码调用时会失败。


✅ 特例:构造方法可以"收紧"可见性!

这是 PHP 中唯一的例外

scala 复制代码
class Singleton {
    public function __construct() {} // 父类 public
}
class Restricted extends Singleton {
    private function __construct() {} // ✅ 允许!用于单例模式
}

💡 用途:实现单例、工厂模式,控制对象创建。


四、只读属性(readonly)的继承规则

❌ 不允许互相覆盖

scala 复制代码
class A {
    public int $prop = 1;
}
class B extends A {
    public readonly int $prop = 2; // ❌ Fatal Error!
}
scala 复制代码
class C {
    public readonly int $prop = 1;
}
class D extends C {
    public int $prop = 2; // ❌ Fatal Error!
}

✅ 正确做法

  • 子类可显式声明同名 readonly 属性,也就是继承,只是把代码又写一了一遍:

    scala 复制代码
    class E extends C {
        public readonly int $prop; // ✅ 允许
    }
  • 子类可在构造函数中为 readonly 属性赋值。

🔑 原因:readonly 是一种"不可变"契约,不能被破坏。


五、方法重写:签名必须兼容(PHP 8.0+ 更严格)

PHP 8.0 开始,方法重写必须满足:

  1. 参数兼容(支持协变/逆变)
  2. 返回类型兼容
  3. 可见性不能收紧
  4. 不能删除返回类型声明

否则会触发 Fatal Error(PHP 7.x 是警告)。


六、继承内部类:返回类型兼容性(PHP 8.1+)

📌 背景

PHP 8.1 为大多数内部方法"暂定添加返回类型",你必须遵守:

php 复制代码
class MyData implements JsonSerializable {
    public function jsonSerialize(): mixed { // ✅ 必须写 : mixed
        return ['name' => 'Alice'];
    }
}

❌ 如果不写:

csharp 复制代码
public function jsonSerialize() { ... }

⚠️ 触发弃用通知:

scala 复制代码
深色版本
Deprecated: Return type should be compatible with ...

✅ 解决方案:#[ReturnTypeWillChange]

用于兼容 PHP < 8.1 的库开发:

php 复制代码
use ReturnTypeWillChange;

class MyData implements JsonSerializable {
    #[ReturnTypeWillChange]
    public function jsonSerialize() {
        return ['name' => 'Alice'];
    }
}

⚠️ 注意:这是临时方案,长期应加上正确返回类型。


七、:: 操作符:静态世界的"遥控器"

:: 的完整用法

用法 示例 说明
静态属性 ClassName::$prop 必须带 $
静态方法 ClassName::method() 常见调用方式
常量 ClassName::CONSTANT 不带 $
父类调用 parent::method() 在子类中调用父类方法
自身类 self::method() 指向定义它的类
实际调用者 static::method() 后期静态绑定(重点!)

八、self vs static:早期绑定 vs 后期静态绑定

🔍 核心区别

关键字 绑定时机 含义 特点
self 编译时(早期绑定) "写这个方法的类" 死板,不随调用者变
static 运行时(后期绑定) "实际调用这个方法的类" 灵活,智能识别上下文

🍕 生活化比喻:总公司与分公司

php 复制代码
class 总公司 {
    protected static $logo = '蓝色地球';

    public static function 显示Logo_A() {
        echo self::$logo . "\n";   // ❌ 固定用总公司的
    }

    public static function 显示Logo_B() {
        echo static::$logo . "\n"; // ✅ 谁调用,就用谁的
    }
}

class 北京分公司 extends 总公司 {
    protected static $logo = '北京天坛';
}

北京分公司::显示Logo_A(); // 输出:蓝色地球 ❌
北京分公司::显示Logo_B(); // 输出:北京天坛 ✅

static 实现了"一套制度,多地执行"。


九、后期静态绑定(Late Static Binding)的应用场景

✅ 场景 1:通用模型 + 不同配置

scala 复制代码
class Model {
    protected static $table;

    public static function find($id) {
        return "SELECT * FROM " . static::$table . " WHERE id = $id";
    }
}

class User extends Model { protected static $table = 'users'; }
class Post extends Model { protected static $table = 'posts'; }

User::find(1);  // ✅ SELECT * FROM users ...
Post::find(2);  // ✅ SELECT * FROM posts ...

✅ 场景 2:静态工厂模式

scala 复制代码
class Animal {
    public static function create() {
        return new static(); // 返回调用者的实例
    }
}

class Dog extends Animal {}
class Cat extends Animal {}

$dog = Dog::create(); // ✅ 返回 Dog 实例
$cat = Cat::create(); // ✅ 返回 Cat 实例

new static() 是关键!


十、最佳实践与陷阱总结

建议 说明
✅ 优先使用 static 而不是 self 在静态方法中,尤其是父类方法
✅ 静态属性用 protectedprivate 避免外部随意修改
✅ 文档说明静态方法行为 特别是涉及后期绑定时
❌ 避免在非静态方法中滥用 static:: 容易混淆,优先用 $this->
✅ 使用 final class 防止继承 当你不希望类被扩展时

✅ 总结图谱

php 复制代码
text
深色版本
PHP 继承与静态机制全景图
│
├── 继承:extends
│   ├── public / protected 成员可继承
│   ├── private 成员不继承(但同类实例可互访)
│   ├── 可见性只能放宽(LSP 原则)
│   └── 构造方法是唯一可收紧的例外
│
├── 只读属性:readonly
│   ├── 不能与普通属性互相覆盖
│   └── 子类可显式声明 readonly
│
├── 方法重写
│   ├── PHP 8.0+ 严格签名兼容
│   └── PHP 8.1+ 内部类返回类型必须匹配
│       └── 可用 #[ReturnTypeWillChange] 兼容旧版本
│
└── 静态机制
    ├── :: 操作符:访问静态成员
    ├── self:早期绑定(定义类)
    └── static:后期静态绑定(调用类)
        └── 实现"一套逻辑,多种配置"

📚 一句话口诀(终极复习)

"extends 是父子关系,:: 是遥控器,
self 是亲爹,static 是干爹;
谁调用,static 就跟谁走!"

相关推荐
BingoGo19 小时前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack19 小时前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack3 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo3 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack4 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理4 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
QQ5110082854 天前
python+springboot+django/flask的校园资料分享系统
spring boot·python·django·flask·node.js·php
WeiXin_DZbishe4 天前
基于django在线音乐数据采集的设计与实现-计算机毕设 附源码 22647
javascript·spring boot·mysql·django·node.js·php·html5