texthtml/vo-identity

使比较值对象更加容易和安全

dev-main 2024-09-20 13:38 UTC

This package is auto-updated.

Last update: 2024-09-20 13:39:04 UTC


README

使比较值对象以比较相等性更加容易和安全。

安装

composer req texthtml/vo-identity

用法

final readonly class UserID
{
    use TH\VOIdentity\Identity;

    private function __construct(private int $i) {}
}

$ua = UserID::of(42);
$ub = UserID::of(42);
$uc = UserID::of(24);

assert($ua === $ub); // $ua & $ub reference the same object so they can be compared with `===`
assert($ua !== $uc);

它是如何工作的?

Identity 特性实现了一个命名构造函数 ::of(),它接受与值对象构造函数相同的参数。当创建与之前相同的值对象时,它会返回值对象相同的实例。

为了识别相同的值对象,调用 $vo->identity() 并应返回一个有效的数组键。使用该键在缓存中保留对该对象的弱引用。下次创建对象时,如果存在具有该键的引用,则将返回该引用。默认的 $vo->identity() 实现使用 serialize($vo) 生成 ID。这是有效的,但可能生成大的字符串,对于大型对象来说可能耗时。为了更好的性能,建议使用专门的实现来覆盖它。例如:

final readonly class UserID
{
    use TH\VOIdentity\Identity;

    private function __construct(private int $id) {}

    protected function identity(): int
    {
        return $this->id;
    }
}

您还可以选择实现 inputIdentity() 来从构造函数参数中计算键,从而避免在不需要时构造值对象。例如:

final readonly class UserID
{
    use TH\VOIdentity\Identity;

    private function __construct(private int $id) {}

    protected function inputIdentity(int $id): int
    {
        return $id;
    }
}

垃圾回收

只保留第一个创建的实例的弱引用,这使得 PHP 能够在值对象在任何其他地方都没有引用时回收它们,避免内存泄漏。

要求

为了正常工作,必须遵守一些规则

  • 对象必须是不可变的和最终的
  • 构造函数应该是纯的(使用相同的参数调用它应该始终产生相同的结果)

注意

克隆

不允许使用 Identity 克隆值对象。这样做将违背其目的,因为它将创建一个新的具有相同内部值的值对象实例。

反序列化

PHP 的 __unserialize() 不能以返回现有对象实例而不是新实例的方式实现。因此,不允许反序列化先前序列化的值对象。当然,使用其他工具(例如,使用 Symfony Serializer 或作为自定义 Doctrine 类型)序列化和反序列化它们是可能的。

导出

可以使用 var_export() 序列化值对象,并使用 eval() 对相应的导出进行评估以获取 PHP 实例。如果对象属性与构造函数 __set_state() 不匹配,则需要覆盖它。