PHP 8.4 属性钩子实战:重写你的商品类,消灭 getter/setter

2026-08-28 0 498

如果你还在为每个字段写 getXxx()/setXxx(),那么 PHP 8.4 新增的 property hooks 会改变你的建模方式。这篇文章用商品类作为案例,从零搭建一个带价格校验、折扣计算和名称清洗的实体,你会发现很多样板代码可以直接删掉。

环境要求

本文所用语法来自 PHP 8.4 或更高版本。请先确认你的运行环境支持这个特性。

第一个钩子

property hooks 允许你在声明属性时直接定义读取和写入逻辑。最简单的 get 钩子长这样:

class Product
{
    private string $_name;

    public string $name {
        get => $this->_name;
        set {
            $this->_name = $value;
        }
    }
}

get 块用 => 返回一个值,set 块通过 $value 接收即将写入的数据。

完整商品类

下面是一个用 property hooks 实现的产品实体,包含:名称清洗、价格校验、折扣约束、动态计算折后价和描述。

class Product
{
    private float $_price;
    private float $_discount = 0;
    private string $_name;

    public function __construct(
        string $name,
        float $price,
        float $discount = 0,
    ) {
        $this->name = $name;
        $this->price = $price;
        $this->discount = $discount;
    }

    public string $name {
        get => $this->_name;
        set {
            $value = trim($value);
            if (mb_strlen($value) < 2) {
                throw new InvalidArgumentException('商品名至少需要2个字符');
            }
            $this->_name = $value;
        }
    }

    public float $price {
        get => $this->_price;
        set {
            if ($value < 0) {
                throw new InvalidArgumentException('价格不能为负数');
            }
            $this->_price = round($value, 2);
        }
    }

    public float $discount {
        get => $this->_discount;
        set {
            if ($value < 0 || $value > 1) {
                throw new InvalidArgumentException('折扣必须在0到1之间');
            }
            $this->_discount = $value;
        }
    }

    public float $finalPrice {
        get => round($this->_price * (1 - $this->_discount), 2);
    }

    public string $description {
        get => sprintf('%s(原价:%.2f,折后价:%.2f)', $this->name, $this->price, $this->finalPrice);
    }
}

注意 $finalPrice$description 是只读的,它们没有 set 钩子,一旦类外访问就通过 get 钩子动态计算。你可以直接像读取普通属性一样读取:

$product = new Product('Apple Magic Mouse', 699);
echo $product->name;        // Apple Magic Mouse
echo $product->price;       // 699
echo $product->finalPrice;  // 699

$product->discount = 0.2;
echo $product->finalPrice;  // 559.2

$product->price = -1;       // 抛出 InvalidArgumentException

传统写法有多啰嗦

如果不使用钩子,你大概会这样写:

class Product
{
    private string $name;
    private float $price;
    private float $discount = 0;

    public function __construct(string $name, float $price, float $discount = 0)
    {
        $this->setName($name);
        $this->setPrice($price);
        $this->setDiscount($discount);
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function setName(string $value): void
    {
        $value = trim($value);
        if (mb_strlen($value) < 2) {
            throw new InvalidArgumentException('商品名至少需要2个字符');
        }
        $this->name = $value;
    }

    // ... 其余 getter/setter 和计算逻辑
}

同样的逻辑,传统写法至少要为每个属性写两个方法,还要在构造器里一一手动调用。property hooks 直接让属性本身拥有行为,调用方不需要记住方法名。

注意事项

1. 避免递归

在 get 钩子内部访问同一个属性会再次调用 get 钩子,导致无限递归。通常需要一个私有后备字段,如本文中的 $_name,公开属性只作为接口。

2. 钩子不支持静态属性

PHP 8.4 规定 property hooks 只能用于实例属性,static 属性不能定义钩子。

3. 序列化时的表现

带钩子的公开属性如果是虚拟的(没有后备存储),默认不会被序列化。私有后备字段会被序列化。若需要定制,请实现 __serialize()/__unserialize()

4. 反射检测

ReflectionProperty 新增了 isVirtual() 等方法,用于判断当前属性是否带钩子或是否为虚拟属性。框架和库可以据此调整行为。

更深一层:业务规则

钩子不仅能做格式化,还能直接内联状态规则。比如一个订单类,可以阻止某些状态跳转:

class Order
{
    private int $_state = 0;

    public int $state {
        get => $this->_state;
        set {
            if ($this->_state === 2 && $value !== 3) {
                throw new LogicException('已完成订单只能进入退款流程');
            }
            $this->_state = $value;
        }
    }
}

当状态为2且新值不是3时,抛出异常。这比在业务方法里手写 if 判断更直观,因为状态字段本身就带有规则。

结论

Property hooks 让 PHP 的属性表达能力接近 C# 或 Kotlin。它适合那些带有校验、格式化和计算逻辑的实体类,你可以把散落各处的 getter/setter 收拢到属性声明中。只要注意递归和序列化细节,它能显著减少样板代码。值得在 PHP 8.4 项目中尝试。

PHP 8.4 属性钩子实战:重写你的商品类,消灭 getter/setter
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

版权声明:
本站资源有的来自互联网收集整理,本站纯免费分享提供学习使用,如果侵犯了您的合法权益,请发送邮件1506151422@qq.com联系,将会及时下架删除。
本站资源仅供研究、学习交流之用,免费开源项目不代表完全可商用,若商业用途请先咨询开发企业能否商用,否则产生的一切后果将由下载用户自行承担。
原创板块未经允许不得转载,否则将追究法律责任。

淘吗网 php PHP 8.4 属性钩子实战:重写你的商品类,消灭 getter/setter https://www.taomawang.com/server/php/2655.html

下一篇:

已经没有下一篇了!

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务