texthtml / vo-identity
使比较值对象更加容易和安全
Requires
- texthtml/object-reaper: dev-main
Requires (Dev)
- php: ^8.2
- phpstan/extension-installer: ^1.1
- phpstan/phpstan: ^1.12
- phpstan/phpstan-strict-rules: ^1.1
- phpunit/phpunit: ^11.3
- slevomat/coding-standard: ^8.0
- squizlabs/php_codesniffer: ^3.6
- texthtml/doctest: ^0.2.3
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()
不匹配,则需要覆盖它。