PHP 完全指南 / 第 11 章 — OOP 进阶
第 11 章 — OOP 进阶:继承、接口、Trait、抽象类与命名空间
11.1 继承(Inheritance)
<?php
declare(strict_types=1);
// 基类
class Animal
{
public function __construct(
protected string $name,
protected int $age,
) {}
public function eat(): string
{
return "{$this->name} is eating.";
}
public function sleep(): string
{
return "{$this->name} is sleeping.";
}
public function info(): string
{
return "{$this->name}, {$this->age} years old";
}
}
// 子类继承
class Dog extends Animal
{
public function __construct(
string $name,
int $age,
private string $breed,
) {
parent::__construct($name, $age); // 调用父类构造器
}
// 重写方法
public function info(): string
{
return parent::info() . ", breed: {$this->breed}";
}
// 新增方法
public function bark(): string
{
return "{$this->name} says: Woof!";
}
}
$dog = new Dog('Buddy', 3, 'Golden Retriever');
echo $dog->eat(); // Buddy is eating.
echo $dog->bark(); // Buddy says: Woof!
echo $dog->info(); // Buddy, 3 years old, breed: Golden Retriever
// 类型检查
var_dump($dog instanceof Dog); // true
var_dump($dog instanceof Animal); // true
final 关键字
<?php
// final 方法 — 子类不能重写
class BaseModel
{
final public function save(): bool
{
// 保存逻辑
return true;
}
}
// final 类 — 不能被继承
final class SecurityToken
{
public function __construct(
private readonly string $token,
) {}
}
// class ExtendedToken extends SecurityToken {} // Error!
11.2 抽象类(Abstract Class)
<?php
abstract class Shape
{
public function __construct(
protected string $color = 'black',
) {}
// 抽象方法 — 子类必须实现
abstract public function area(): float;
abstract public function perimeter(): float;
// 具体方法 — 子类可直接使用
public function describe(): string
{
return sprintf(
"%s: area=%.2f, perimeter=%.2f",
static::class,
$this->area(),
$this->perimeter()
);
}
}
class Circle extends Shape
{
public function __construct(
string $color,
private readonly float $radius,
) {
parent::__construct($color);
}
public function area(): float
{
return M_PI * $this->radius ** 2;
}
public function perimeter(): float
{
return 2 * M_PI * $this->radius;
}
}
class Rectangle extends Shape
{
public function __construct(
string $color,
private readonly float $width,
private readonly float $height,
) {
parent::__construct($color);
}
public function area(): float
{
return $this->width * $this->height;
}
public function perimeter(): float
{
return 2 * ($this->width + $this->height);
}
}
// 使用多态
$shapes = [
new Circle('red', 5),
new Rectangle('blue', 4, 6),
];
foreach ($shapes as $shape) {
echo $shape->describe() . "\n";
}
// Circle: area=78.54, perimeter=31.42
// Rectangle: area=24.00, perimeter=20.00
11.3 接口(Interface)
<?php
interface Serializable2
{
public function serialize(): string;
public static function deserialize(string $data): self;
}
interface Loggable
{
public function toLogContext(): array;
}
interface Cacheable
{
public function getCacheKey(): string;
public function getCacheTTL(): int;
}
// 实现多个接口
class User implements Serializable2, Loggable, Cacheable
{
public function __construct(
private string $name,
private string $email,
) {}
public function serialize(): string
{
return json_encode(['name' => $this->name, 'email' => $this->email]);
}
public static function deserialize(string $data): self
{
$decoded = json_decode($data, true);
return new self($decoded['name'], $decoded['email']);
}
public function toLogContext(): array
{
return ['user' => $this->name, 'email' => $this->email];
}
public function getCacheKey(): string
{
return 'user:' . md5($this->email);
}
public function getCacheTTL(): int
{
return 3600;
}
}
接口继承
<?php
interface Repository
{
public function find(int $id): ?array;
public function findAll(): array;
public function save(array $data): bool;
public function delete(int $id): bool;
}
// 接口可以继承接口
interface UserRepository extends Repository
{
public function findByEmail(string $email): ?array;
public function findActive(): array;
}
// 接口常量
interface HttpStatus
{
const OK = 200;
const NOT_FOUND = 404;
const SERVER_ERROR = 500;
}
11.4 Trait
Trait 是一种代码复用机制,解决了 PHP 单继承的限制。
<?php
declare(strict_types=1);
trait Timestamps
{
private ?DateTimeImmutable $createdAt = null;
private ?DateTimeImmutable $updatedAt = null;
public function getCreatedAt(): DateTimeImmutable
{
return $this->createdAt ??= new DateTimeImmutable();
}
public function getUpdatedAt(): DateTimeImmutable
{
return $this->updatedAt ??= new DateTimeImmutable();
}
public function touch(): void
{
$this->updatedAt = new DateTimeImmutable();
}
}
trait SoftDelete
{
private ?DateTimeImmutable $deletedAt = null;
public function delete(): void
{
$this->deletedAt = new DateTimeImmutable();
}
public function restore(): void
{
$this->deletedAt = null;
}
public function isDeleted(): bool
{
return $this->deletedAt !== null;
}
}
trait HasValidation
{
private array $errors = [];
public function validate(): bool
{
$this->errors = [];
$this->validateRules();
return empty($this->errors);
}
public function getErrors(): array
{
return $this->errors;
}
abstract protected function validateRules(): void;
protected function addError(string $field, string $message): void
{
$this->errors[$field][] = $message;
}
}
// 使用 Trait
class Article
{
use Timestamps;
use SoftDelete;
use HasValidation;
public function __construct(
private string $title = '',
private string $content = '',
) {
$this->createdAt = new DateTimeImmutable();
}
protected function validateRules(): void
{
if ($this->title === '') {
$this->addError('title', 'Title is required');
}
if (mb_strlen($this->content) < 10) {
$this->addError('content', 'Content too short');
}
}
}
$article = new Article('', 'Short');
var_dump($article->validate()); // false
print_r($article->getErrors());
Trait 冲突解决
<?php
trait A
{
public function hello(): string { return 'Hello from A'; }
public function onlyA(): string { return 'Only in A'; }
}
trait B
{
public function hello(): string { return 'Hello from B'; }
public function onlyB(): string { return 'Only in B'; }
}
class MyClass
{
// 解决冲突:选择一个
use A, B {
A::hello insteadof B; // 使用 A 的 hello
B::hello as helloFromB; // B 的 hello 别名为 helloFromB
}
// 为 trait 方法设置别名
use A {
onlyA as public exposedA;
}
}
$obj = new MyClass();
echo $obj->hello(); // Hello from A
echo $obj->helloFromB(); // Hello from B
echo $obj->exposedA(); // Only in A
11.5 命名空间(Namespaces)
11.5.1 基本用法
<?php
// 文件: src/Models/User.php
namespace App\Models;
class User
{
public function __construct(
public string $name,
) {}
}
<?php
// 文件: src/Services/AuthService.php
namespace App\Services;
use App\Models\User; // 导入类
use App\Repositories\UserRepository as UserRepo; // 别名
use function App\Helpers\formatDate; // 导入函数
use const App\Config\MAX_RETRIES; // 导入常量
class AuthService
{
public function __construct(
private UserRepo $userRepo,
) {}
public function login(string $email): ?User
{
$user = $this->userRepo->findByEmail($email);
if ($user) {
formatDate(new \DateTime()); // 使用完全限定名调用全局类
}
return $user;
}
}
11.5.2 命名空间层级
App\
├── Controllers\
│ ├── HomeController.php → App\Controllers\HomeController
│ └── Api\
│ └── UserController.php → App\Controllers\Api\UserController
├── Models\
│ ├── User.php → App\Models\User
│ └── Post.php → App\Models\Post
├── Services\
│ └── AuthService.php → App\Services\AuthService
├── Repositories\
│ └── UserRepository.php → App\Repositories\UserRepository
└── Helpers\
└── DateHelper.php → App\Helpers\DateHelper
11.5.3 自动加载
// composer.json
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
}
}
# 重新生成自动加载映射
composer dump-autoload
11.6 对象克隆与序列化
<?php
class Config
{
public function __construct(
public array $data = [],
) {}
// 浅克隆
public function __clone(): void
{
// 嵌套对象仍指向同一个引用
}
}
$a = new Config(['key' => 'value']);
$b = clone $a;
$b->data['key'] = 'changed';
echo $a->data['key']; // 'value'(独立副本)
// 深克隆
class DeepCopyConfig
{
public function __clone(): void
{
foreach ($this->data as $key => $value) {
if (is_object($value)) {
$this->data[$key] = clone $value;
}
}
}
}
// 序列化
$serialized = serialize($a);
$restored = unserialize($serialized);
// JSON 序列化(实现 JsonSerializable 接口)
class Money implements \JsonSerializable
{
public function __construct(
private int $cents,
private string $currency,
) {}
public function jsonSerialize(): array
{
return ['amount' => $this->cents / 100, 'currency' => $this->currency];
}
}
11.7 PHP 8.x 类新特性
11.7.1 readonly 类(PHP 8.2+)
<?php
readonly class UserDTO
{
public function __construct(
public string $name,
public int $age,
) {}
}
$user = new UserDTO('Alice', 30);
// $user->name = 'Bob'; // Error: Cannot modify readonly property
11.7.2 #[\Override] 属性(PHP 8.3+)
<?php
class Base
{
public function process(): void {}
}
class Child extends Base
{
#[\Override] // 如果方法名拼写错误或父类删除了该方法,会报错
public function process(): void
{
parent::process();
}
}
11.8 业务场景:仓储模式
<?php
declare(strict_types=1);
namespace App\Repositories;
use App\Models\User;
use PDO;
interface UserRepositoryInterface
{
public function findById(int $id): ?User;
public function findByEmail(string $email): ?User;
public function findAll(int $limit = 100, int $offset = 0): array;
public function save(User $user): bool;
public function delete(int $id): bool;
}
class UserRepository implements UserRepositoryInterface
{
public function __construct(
private readonly PDO $db,
) {}
public function findById(int $id): ?User
{
$stmt = $this->db->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([$id]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
return $row ? User::fromArray($row) : null;
}
public function findByEmail(string $email): ?User
{
$stmt = $this->db->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$email]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
return $row ? User::fromArray($row) : null;
}
public function findAll(int $limit = 100, int $offset = 0): array
{
$stmt = $this->db->prepare(
'SELECT * FROM users ORDER BY id DESC LIMIT ? OFFSET ?'
);
$stmt->execute([$limit, $offset]);
return array_map(
fn(array $row) => User::fromArray($row),
$stmt->fetchAll(PDO::FETCH_ASSOC)
);
}
public function save(User $user): bool
{
if ($user->getId() === null) {
return $this->insert($user);
}
return $this->update($user);
}
public function delete(int $id): bool
{
$stmt = $this->db->prepare('DELETE FROM users WHERE id = ?');
return $stmt->execute([$id]);
}
private function insert(User $user): bool
{
$stmt = $this->db->prepare(
'INSERT INTO users (name, email) VALUES (?, ?)'
);
return $stmt->execute([$user->getName(), $user->getEmail()]);
}
private function update(User $user): bool
{
$stmt = $this->db->prepare(
'UPDATE users SET name = ?, email = ? WHERE id = ?'
);
return $stmt->execute([$user->getName(), $user->getEmail(), $user->getId()]);
}
}
11.9 扩展阅读
上一章:第 10 章 — OOP 基础 下一章:第 12 章 — 异常处理