PHP 入门教程
PHP 抽象类、接口与 Trait
本教程共 65 篇 · 第 21 篇 · 更新于 2026-07-24 · 约 5 分钟阅读
PHPPHP8抽象类接口Trait多态代码复用
21. PHP 抽象类、接口与 Trait
本节目标:掌握抽象类、接口和 Trait 三种代码复用机制。学完你能设计灵活的类结构,实现多态和代码混入。
21.1 抽象类
抽象类是不能直接实例化的类,只能被继承。它可以包含抽象方法(只有声明,没有实现):
<?php
abstract class Animal {
protected string $name;
public function __construct(string $name) {
$this->name = $name;
}
// 普通方法
public function sleep(): string {
return "{$this->name}在睡觉";
}
// 抽象方法:子类必须实现
abstract public function speak(): string;
}
class Dog extends Animal {
public function speak(): string {
return "{$this->name}说:汪汪汪";
}
}
class Cat extends Animal {
public function speak(): string {
return "{$this->name}说:喵喵喵";
}
}
// $animal = new Animal("动物"); // 错误!不能实例化抽象类
$dog = new Dog("大黄");
echo $dog->speak(); // 大黄说:汪汪汪
echo $dog->sleep(); // 大黄在睡觉
?>
Note抽象类适合”部分实现”的场景:父类提供通用代码,子类完成特定逻辑。
21.2 接口
接口只声明方法签名,不包含实现。类通过 implements 实现接口:
<?php
// 定义接口
interface LoggerInterface {
public function log(string $message): void;
public function error(string $message): void;
}
interface CacheInterface {
public function get(string $key): mixed;
public function set(string $key, mixed $value): void;
}
// 实现多个接口
class FileLogger implements LoggerInterface {
public function log(string $message): void {
file_put_contents('app.log', $message . "\n", FILE_APPEND);
}
public function error(string $message): void {
$this->log("[ERROR] $message");
}
}
class RedisCache implements CacheInterface {
public function get(string $key): mixed {
// 从 Redis 读取
return null;
}
public function set(string $key, mixed $value): void {
// 写入 Redis
}
}
?>
接口继承接口
<?php
interface Readable {
public function read(): string;
}
interface Writable {
public function write(string $data): void;
}
// 接口可以继承多个接口
interface Stream extends Readable, Writable {
public function close(): void;
}
class FileStream implements Stream {
public function read(): string { return ""; }
public function write(string $data): void {}
public function close(): void {}
}
?>
21.3 抽象类 vs 接口
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 实例化 | 不能 | 不能 |
| 包含属性 | 可以 | 只能有常量 |
| 方法实现 | 可以 | 不能(只能声明方法签名) |
| 继承/实现 | 单继承 | 多实现 |
| 构造函数 | 可以有 | 不能 |
<?php
// 选择原则:
// - "是一种"关系 + 共享代码 = 抽象类
// - "能做什么"关系 + 纯契约 = 接口
// 抽象类示例:共享代码
abstract class Controller {
protected function render(string $view): string {
return "渲染视图: $view";
}
}
// 接口示例:定义能力
interface Authenticatable {
public function login(string $user, string $pass): bool;
public function logout(): void;
}
?>
21.4 Trait
PHP 不支持多重继承,Trait 提供了一种代码复用机制:
<?php
trait Timestampable {
public ?DateTime $createdAt = null;
public ?DateTime $updatedAt = null;
public function setCreatedAt(): void {
$this->createdAt = new DateTime();
}
public function setUpdatedAt(): void {
$this->updatedAt = new DateTime();
}
}
trait Loggable {
public function log(string $action): void {
echo "[LOG] $action at " . date('Y-m-d H:i:s') . "\n";
}
}
// 在一个类中使用多个 Trait
class Article {
use Timestampable, Loggable;
public string $title;
public function publish(): void {
$this->setCreatedAt();
$this->log("发布文章: {$this->title}");
}
}
$article = new Article();
$article->title = "PHP 入门";
$article->publish();
?>
21.5 解决 Trait 冲突
当多个 Trait 有同名方法时,需要显式解决冲突:
<?php
trait TraitA {
public function sayHello(): string {
return "Hello from A";
}
public function sayGoodbye(): string {
return "Goodbye from A";
}
}
trait TraitB {
public function sayHello(): string {
return "Hello from B";
}
}
class MyClass {
use TraitA, TraitB {
// 使用 TraitA 的 sayHello,而不是 TraitB 的
TraitA::sayHello insteadof TraitB;
// 给 TraitB 的 sayHello 起别名
TraitB::sayHello as sayHelloB;
}
}
$obj = new MyClass();
echo $obj->sayHello(); // Hello from A
echo $obj->sayHelloB(); // Hello from B
echo $obj->sayGoodbye(); // Goodbye from A
?>
21.6 Trait 修改访问控制
<?php
trait Utility {
public function helper(): string {
return "帮助方法";
}
}
class Service {
use Utility {
// 将 public 改为 private
helper as private;
}
public function doWork(): string {
return $this->helper(); // 类内部可以调用
}
}
$service = new Service();
echo $service->doWork(); // 帮助方法
// echo $service->helper(); // 错误!现在是 private
?>
21.7 接口的常用用法
<?php
// 接口可以定义常量
interface Configurable {
public const VERSION = "1.0";
}
// 使用 interface 约束类型
interface LoggerInterface {
public function log(string $message): void;
}
// 用 instanceof 检查接口
if ($obj instanceof LoggerInterface) {
$obj->log("就绪");
}
// 一个类可以实现多个接口
class FileLogger implements LoggerInterface, Configurable {
public function log(string $message): void {
echo "[LOG] $message (v" . self::VERSION . ")\n";
}
}
?>
21.8 何时使用什么
| 场景 | 选择 |
|---|---|
| 共享代码 + 强制子类实现某些方法 | 抽象类 |
| 定义一组能力/契约,不关心实现 | 接口 |
| 水平复用代码(跨类层次) | Trait |
| 需要多继承效果 | 接口 + Trait |
来源:参考了 w3cschool「PHP 面向对象」、runoob「PHP 面向对象」以及 php.net 官方文档,综合改写后所得。