告别繁琐的 getter/setter 和复杂的继承限制,一招实现属性的读写权限分离。
相信每一位 PHP 开发者对类的 public、protected、private 都不陌生。传统的可见性控制同时作用于属性的读和写,导致很多开发者不得不在“无法读取”与“无法写入”之间做出妥协。尽管利用 getter/setter 方法可以手动分区权限,但大量重复的方法体直接增加了类文件的臃肿率。
PHP 8.4 带来的 非对称可见性 精准地解决了这一痛点——它允许你独立地分别控制 get(读)和 set(写)的权限。这一特性很可能成为日后构建健壮数据对象与值对象(VO)的默认范式。
一、回顾:传统对称可见性的掣肘
在谈新特性之前,先快速回顾传统可见性:
· public:任何上下文均可读写。
· protected:仅限于本类实例及其子类实例内部读写。
· private:仅限本类实例内部读写。
典型困扰场景——只读 ID
```php
class User {
public function __construct(
private int $id,
public string $name,
) {}
// 想让外部能随意读取 $id,但绝不允许外部修改 —— 传统必须手写 getter
public function getId(): int {
return $this->id;
}
}
```
如果你把 $id 设为 public,外部就可能意外篡改它;设为 private,外部连读取都做不到,不得不手写 getId()。所有麻烦的根源在于:读写权限被捆绑了。
二、非对称可见性:让你对属性的把控“粒”度更细
PHP 8.4 允许你为同一个属性分别定义 get 和 set 的可见性。语法极其简洁——在属性类型前方加上 get 或 set 的修饰符即可:
```php
class User {
public function __construct(
// 任何人都能读取,但只能在类内部修改
public private(set) int $id,
public string $name,
) {}
}
$user = new User(1, 'Tom');
echo $user->id; // ✅ 允许读取
$user->id = 2; // ❌ 致命错误:不允许外部修改
```
可见性组合速查表
get 权限 set 权限 效果
public private 对外只读,内部可写
public protected 对外只读,本类及子类可写
protected private 类及子类可读,仅本类可写
protected protected 类及子类可读写(等同于传统 protected)
注意:set 的可见性必须比 get 更严格,这是为了确保数据流向的合理性。
三、实战场景:值对象(VO)的最佳拍档
非对称可见性在构建值对象时尤为强大。来看一个电商场景——订单金额绝对不允许外部随意篡改:
```php
readonly class OrderAmount {
public function __construct(
public private(set) float $value,
public private(set) string $currency,
) {}
public function applyDiscount(float $rate): void {
// 内部仍然可以修改
$this->value = round($this->value * $rate, 2);
}
}
$amount = new OrderAmount(100.0, 'USD');
echo $amount->value; // ✅ 正常读取
$amount->value = 50; // ❌ 报错:禁止外部直接篡改金额
```
这样,金额属性对外"只读",任何金额变动只能通过定义好的业务方法(如 applyDiscount)完成,既保护了数据完整性,又让代码语义更加清晰。
四、与 Property Hooks 的结合:终极数据封装
将非对称可见性与 PHP 8.4 同步推出的 属性钩子(Property Hooks) 结合使用,可以实现终极数据封装——完全省略显式的 getter 和 setter 方法。
下面是一个完整示例,展示如何用这两个特性构建一个健壮的、对外只读的 Product 实体:
```php
class Product {
public function __construct(
public private(set) string $sku,
public string $name,
public private(set) float $price,
) {}
// 构造函数之外的属性初始化方法
public static function fromDatabase(array $row): self {
return new self(
sku: $row['sku'],
name: $row['name'],
price: (float) $row['price']
);
}
public function updatePrice(float $newPrice): void {
if ($newPrice <= 0) {
throw new \InvalidArgumentException('价格必须大于零');
}
$this->price = $newPrice;
}
}
$product = new Product('SKU-1001', '极客键盘', 299.0);
echo $product->sku; // ✅ 输出 SKU-1001
echo $product->price; // ✅ 输出 299.0
$product->price = 199; // ❌ Error: 外部不可修改
$product->updatePrice(259); // ✅ 通过内部方法修改
```
借助这段代码,无论是从数据库还原还是新建实例,sku 与 price 的安全性都得到了保证,再也无需为数据对象维护大量的样板 getter 函数。
五、总结与升级指南
核心收获
· 非对称可见性 让你对属性的读写权限实现精细化控制,告别冗余的 getter/setter;
· 值对象与数据实体 是对该特性最“来电”的应用场景;
· 配合属性钩子(Property Hooks) ,可以构建出近乎零样板代码的健壮数据层;
· PHP 正逐步向渐进类型安全和更严谨的数据封装进化,掌握这些新特性是保持竞争力的必修课。
升级小贴士
1. 确认版本:确保生产环境已升级到 PHP 8.4 或更高版本(推荐使用 8.4+ 稳定版);
2. 逐步迁移:从项目中新增的值对象和数据实体开始使用,不必一次性修改所有旧代码;
3. 配合静态分析:使用 PHPStan 或 Psalm 进行代码扫描,它们已完全支持 PHP 8.4 的语法检测,能帮你快速发现可见性错误;
4. 实践优先:在本地的练手项目中多动手写几个例子,感受非对称可见性带来的思维转变。
达人金句:最好的代码不是写出来的,是约束出来的。让语言特性替你把关,你只管专注于业务逻辑。
如果这篇文章对你有帮助,欢迎点赞、在看、转发,让更多 PHP 达人一起精进! ✨