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

发布于:2025-08-08 ⋅ 阅读:(14) ⋅ 点赞:(0)

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

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

本文涵盖 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)
“子类对象应能替换父类对象而不破坏程序行为。”
如果 Childbar() 变成 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 在静态方法中,尤其是父类方法
✅ 静态属性用 protectedprivate 避免外部随意修改
✅ 文档说明静态方法行为 特别是涉及后期绑定时
❌ 避免在非静态方法中滥用 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 就跟谁走!”


网站公告

今日签到

点亮在社区的每一天
去签到