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

—— 从 extends 到 static 的完整知识体系(适合深度复习)

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


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

✅ 什么是继承?

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


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 成员!


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 是 类级别的封装,不是实例级别的。


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

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

class Parent { protected function foo() {} } class Child extends Parent { public function foo() {} // ✅ OK:protected → public }

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

class Parent { public function bar() {} } class Child extends Parent { protected function bar() {} // ❌ Fatal Error! }

🔥 原因:违反 里氏替换原则(LSP)
“子类对象应能替换父类对象而不破坏程序行为。”
如果 Child 的 bar() 变成 protected,外部代码调用时会失败。


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

这是 PHP 中唯一的例外


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

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


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

❌ 不允许互相覆盖

class A { public int $prop = 1; } class B extends A { public readonly int $prop = 2; // ❌ Fatal Error! }


class C { public readonly int $prop = 1; } class D extends C { public int $prop = 2; // ❌ Fatal Error! }

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

    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 为大多数内部方法“暂定添加返回类型”,你必须遵守:


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

❌ 如果不写:

public function jsonSerialize() { ... }

⚠️ 触发弃用通知:

深色版本 Deprecated: Return type should be compatible with ... 

✅ 解决方案:#[ReturnTypeWillChange]

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


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 运行时(后期绑定) “实际调用这个方法的类” 灵活,智能识别上下文

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

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:通用模型 + 不同配置

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:静态工厂模式

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

✅ 总结图谱


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


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

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

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐