📘 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 就跟谁走!"

相关推荐
zh731412 分钟前
laravel在cli模式下输出格式漂亮一些
microsoft·php·laravel
onejason1 小时前
《PHP 爬虫实战指南:获取淘宝店铺详情》
前端·后端·php
用户3074596982074 小时前
PHP 抽象类完全指南(含 PHP 8.4 新特性)
php
卑微的小鬼6 小时前
TCP如何实现可靠传输?实现细节?
网络·tcp/ip·php
wuzuyu36510 小时前
Laravel The requested URL /hellowzy was not found on this server. 404 问题的解决
php·laravel
kebeiovo11 小时前
网络muduo库的实现(2)
服务器·网络·php
七七软件开发12 小时前
二手交易 app 系统架构分析
java·python·小程序·系统架构·php
七七软件开发1 天前
直播 app 系统架构分析
java·python·小程序·系统架构·php
linux修理工1 天前
使用 SecureCRT 连接华为 eNSP 模拟器的方法
服务器·开发语言·php