PHP 完全指南 / 第 10 章 — OOP 基础
第 10 章 — OOP 基础:类、属性、方法、构造器与枚举
10.1 类的定义
<?php
declare(strict_types=1);
class User
{
// 属性声明(PHP 7.4+ 类型化属性)
public string $name;
public int $age;
public string $email;
protected string $password;
private array $roles = [];
// 构造器
public function __construct(string $name, int $age, string $email)
{
$this->name = $name;
$this->age = $age;
$this->email = $email;
$this->password = '';
}
// 方法
public function greet(): string
{
return "你好,我是 {$this->name},今年 {$this->age} 岁。";
}
// 访问私有属性
public function addRole(string $role): void
{
$this->roles[] = $role;
}
public function getRoles(): array
{
return $this->roles;
}
}
$user = new User('Alice', 30, 'alice@example.com');
echo $user->greet(); // 你好,我是 Alice,今年 30 岁。
10.2 构造器提升(Constructor Promotion)— PHP 8.0+
<?php
// 传统写法
class UserOld
{
public string $name;
public int $age;
public function __construct(string $name, int $age)
{
$this->name = $name;
$this->age = $age;
}
}
// PHP 8.0+ 构造器提升(一行搞定)
class User
{
public function __construct(
public string $name,
public int $age,
protected string $password = '',
private array $roles = [],
) {}
}
$user = new User('Alice', 30);
echo $user->name; // Alice
10.3 属性(Properties)
10.3.1 访问控制
| 修饰符 | 类内 | 子类 | 外部 |
|---|---|---|---|
public | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ❌ |
private | ✅ | ❌ | ❌ |
10.3.2 readonly 属性(PHP 8.1+)
<?php
class Product
{
public function __construct(
public readonly string $id,
public readonly string $name,
public readonly float $price,
) {}
}
$product = new Product('P001', 'iPhone', 999.99);
echo $product->name; // iPhone
// $product->name = 'iPad'; // Error: Cannot modify readonly property
10.3.3 Property Hooks(PHP 8.4+)
<?php
class User
{
public string $name {
set(string $value) {
$this->name = trim($value);
}
}
public string $email {
set(string $value) {
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException("Invalid email: $value");
}
$this->email = strtolower($value);
}
}
// 计算属性(只读钩子)
public string $displayName {
get => $this->name . ' <' . $this->email . '>';
}
public function __construct(string $name, string $email)
{
$this->name = $name;
$this->email = $email;
}
}
$user = new User('Alice', 'ALICE@EXAMPLE.COM');
echo $user->name; // Alice
echo $user->email; // alice@example.com
echo $user->displayName; // Alice <alice@example.com>
10.3.4 非对称可见性(PHP 8.4+)
<?php
class User
{
// 外部可读,但只能在类内设置
public private(set) string $email;
public protected(set) int $loginCount = 0;
public function __construct(string $email)
{
$this->email = $email; // ✅ 类内可写
}
public function incrementLoginCount(): void
{
$this->loginCount++; // ✅ 类内可写
}
}
$user = new User('alice@example.com');
echo $user->email; // ✅ 可读
// $user->email = '...'; // ❌ 外部不可写
10.4 方法(Methods)
<?php
class Calculator
{
private float $result = 0;
// 实例方法
public function add(float $value): static
{
$this->result += $value;
return $this; // 链式调用
}
public function multiply(float $value): static
{
$this->result *= $value;
return $this;
}
public function getResult(): float
{
return $this->result;
}
// 静态方法
public static function create(float $initial = 0): static
{
$calc = new static();
$calc->result = $initial;
return $calc;
}
}
// 链式调用
$result = Calculator::create(10)
->add(5)
->multiply(3)
->getResult();
echo $result; // 45
10.5 静态属性与方法
<?php
class AppConfig
{
private static array $config = [];
private static ?self $instance = null;
// 单例模式
private function __construct() {}
public static function getInstance(): self
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public static function set(string $key, mixed $value): void
{
self::$config[$key] = $value;
}
public static function get(string $key, mixed $default = null): mixed
{
return self::$config[$key] ?? $default;
}
}
AppConfig::set('app.name', 'MyApp');
AppConfig::set('app.debug', true);
echo AppConfig::get('app.name'); // MyApp
10.6 抽象属性与接口常量
<?php
// 接口常量
interface Status
{
const ACTIVE = 1;
const INACTIVE = 0;
const DELETED = -1;
public function getStatus(): int;
}
// 使用接口常量
class User implements Status
{
public function getStatus(): int
{
return Status::ACTIVE;
}
}
echo User::ACTIVE; // 1
10.7 枚举(Enums)— PHP 8.1+
10.7.1 基础枚举
<?php
enum Color
{
case Red;
case Green;
case Blue;
}
// 使用
$color = Color::Red;
echo $color->name; // "Red"
// 比较(严格)
var_dump($color === Color::Red); // true
var_dump($color === Color::Green); // false
// 从名称创建
$color = Color::from('Green'); // 不存在时抛 ValueError
$color = Color::tryFrom('Unknown'); // 不存在时返回 null
// 遍历所有 case
foreach (Color::cases() as $case) {
echo $case->name . "\n";
}
10.7.2 标量枚举(Backed Enum)
<?php
enum OrderStatus: string
{
case Pending = 'pending';
case Processing = 'processing';
case Shipped = 'shipped';
case Delivered = 'delivered';
case Cancelled = 'cancelled';
}
// 获取标量值
echo OrderStatus::Pending->value; // "pending"
// 从标量值创建
$status = OrderStatus::from('shipped');
echo $status->name; // "Shipped"
// 在 switch/match 中使用
$message = match ($status) {
OrderStatus::Pending => '订单待处理',
OrderStatus::Processing => '正在处理中',
OrderStatus::Shipped => '已发货',
OrderStatus::Delivered => '已送达',
OrderStatus::Cancelled => '已取消',
};
10.7.3 枚举方法
<?php
enum Suit: string
{
case Hearts = 'hearts';
case Diamonds = 'diamonds';
case Clubs = 'clubs';
case Spades = 'spades';
// 枚举可以有方法
public function color(): string
{
return match ($this) {
self::Hearts, self::Diamonds => 'red',
self::Clubs, self::Spades => 'black',
};
}
public function symbol(): string
{
return match ($this) {
self::Hearts => '♥',
self::Diamonds => '♦',
self::Clubs => '♣',
self::Spades => '♠',
};
}
public function label(): string
{
return match ($this) {
self::Hearts => '红心',
self::Diamonds => '方块',
self::Clubs => '梅花',
self::Spades => '黑桃',
};
}
}
$card = Suit::Hearts;
echo $card->symbol(); // ♥
echo $card->color(); // red
echo $card->label(); // 红心
10.7.4 枚举实现接口
<?php
interface HasLabel
{
public function label(): string;
}
enum Permission: int implements HasLabel
{
case Read = 1;
case Write = 2;
case Execute = 4;
case Admin = 7;
public function label(): string
{
return match ($this) {
self::Read => '读取',
self::Write => '写入',
self::Execute => '执行',
self::Admin => '管理员',
};
}
// 位运算权限检查
public function includes(self $permission): bool
{
return ($this->value & $permission->value) === $permission->value;
}
}
$perms = Permission::Admin;
echo $perms->includes(Permission::Read); // true
echo $perms->includes(Permission::Write); // true
10.8 对象比较
<?php
class Point
{
public function __construct(
public float $x,
public float $y,
) {}
}
$p1 = new Point(1.0, 2.0);
$p2 = new Point(1.0, 2.0);
$p3 = $p1;
// 同一对象(===)
var_dump($p1 === $p3); // true(同一个实例)
var_dump($p1 === $p2); // false(不同实例)
// 属性值相等(==)
var_dump($p1 == $p2); // true(属性值相同)
10.9 业务场景:值对象(Value Object)
<?php
declare(strict_types=1);
class Money
{
public function __construct(
private readonly int $amount, // 以分为单位
private readonly string $currency,
) {
if ($amount < 0) {
throw new InvalidArgumentException('Amount must be non-negative');
}
if (!preg_match('/^[A-Z]{3}$/', $currency)) {
throw new InvalidArgumentException('Invalid currency code');
}
}
public static function fromDecimal(float $amount, string $currency): self
{
return new self((int)round($amount * 100), $currency);
}
public function getAmount(): int
{
return $this->amount;
}
public function getDecimal(): float
{
return $this->amount / 100;
}
public function getCurrency(): string
{
return $this->currency;
}
public function add(self $other): self
{
$this->assertSameCurrency($other);
return new self($this->amount + $other->amount, $this->currency);
}
public function subtract(self $other): self
{
$this->assertSameCurrency($other);
$result = $this->amount - $other->amount;
if ($result < 0) {
throw new RuntimeException('Insufficient amount');
}
return new self($result, $this->currency);
}
public function multiply(int $factor): self
{
return new self($this->amount * $factor, $this->currency);
}
public function equals(self $other): bool
{
return $this->amount === $other->amount
&& $this->currency === $other->currency;
}
public function format(): string
{
return match ($this->currency) {
'CNY' => '¥' . number_format($this->getDecimal(), 2),
'USD' => '$' . number_format($this->getDecimal(), 2),
'EUR' => '€' . number_format($this->getDecimal(), 2),
default => $this->currency . ' ' . number_format($this->getDecimal(), 2),
};
}
public function __toString(): string
{
return $this->format();
}
private function assertSameCurrency(self $other): void
{
if ($this->currency !== $other->currency) {
throw new RuntimeException('Currency mismatch');
}
}
}
// 使用示例
$price = Money::fromDecimal(99.99, 'CNY');
$tax = Money::fromDecimal(12.99, 'CNY');
$total = $price->add($tax);
echo $total->format(); // ¥112.98
echo $total->getAmount(); // 11298(分)
10.10 扩展阅读
上一章:第 9 章 — 字符串 下一章:第 11 章 — OOP 进阶