PHPackages                             texthtml/vo-identity - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. texthtml/vo-identity

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

texthtml/vo-identity
====================

Make comparing Value Objects easier &amp; safer

00PHPCI passing

Since Feb 6Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/texthtml/vo-identity)[ Packagist](https://packagist.org/packages/texthtml/vo-identity)[ RSS](/packages/texthtml-vo-identity/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependenciesVersions (2)Used By (0)

VO Identity
===========

[](#vo-identity)

Make comparing Value Objects for equality easier &amp; safer.

Installation
============

[](#installation)

```
composer req texthtml/vo-identity
```

Usage
=====

[](#usage)

```
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);
```

How does it work?
=================

[](#how-does-it-work)

The `Identity` trait implements a named constructor `::of()` that takes the same arguments as the Value Object constructor. And return the same instance of the Value Object when an identical Value Object than a previous one would have been created.

To identify identical Value Object, `$vo->identity()` is called and should return a valid array key. A weak reference to the object will be kept in a cache using that key. The next time an object would be created, if a reference with that ley exists it will be returned. The default implementation of `$vo->identity()` is using `serialize($vo)` to generate the the ID. This is valid but can generate large string and be time consuming for large objects. For better performance, it is recommended to override it with a specialized implementation. e.g.:

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

    private function __construct(private int $id) {}

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

You can also opt-in to implemented `inputIdentity()` to compute the key from the constructor arguments, and thus avoid having to construct the Value Object if not needed. e.g.:

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

    private function __construct(private int $id) {}

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

Garbage collection
------------------

[](#garbage-collection)

By keeping only a weak reference to the first created instance, this let PHP able to garbage collect the Value Objects when they are not referenced anywhere else and avoid memory leaks.

Requirements
============

[](#requirements)

To work properly a few rules must be respected:

- The object must be immutable and final
- The constructor should be pure (calling it with the same argument should always produce the same result)

Notes
=====

[](#notes)

Cloning
-------

[](#cloning)

It's not allowed to clone a Value Object using `Identity`. Doing so would defeat the purpose by creating a new instance of a Value Object having the same inner value.

Unserializing
-------------

[](#unserializing)

PHP `__unserialize()` can't be implemented in a way that an already existing instance of an object is returned instead of a new instance. Because of that it's not allowed to `unserialize()` a previously `serialize()`'d Value Objects. Of course, serializing and unserializing them with other tools is possible (e.g. with the Symfony Serializer, or as a custom Doctrine type.

Exporting
---------

[](#exporting)

It is possible to serialize a Value Object with `var_export()` and `eval()` the corresponding export to get a PHP instance. If the object properties doesn't match the constructor `__set_state()` will need to be overriden.

###  Health Score

19

—

LowBetter than 10% of packages

Maintenance54

Moderate activity, may be stable

Popularity0

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity14

Early-stage or recently created project

 Bus Factor1

Top contributor holds 68.2% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/3943b5c35797d1ede53aaed3d6812e4cf5b2dba7619a00dc7a381480a7b3d330?d=identicon)[mathroc](/maintainers/mathroc)

---

Top Contributors

[![text-html-renovate[bot]](https://avatars.githubusercontent.com/u/6465918?v=4)](https://github.com/text-html-renovate[bot] "text-html-renovate[bot] (15 commits)")[![mathroc](https://avatars.githubusercontent.com/u/291531?v=4)](https://github.com/mathroc "mathroc (7 commits)")

### Embed Badge

![Health badge](/badges/texthtml-vo-identity/health.svg)

```
[![Health](https://phpackages.com/badges/texthtml-vo-identity/health.svg)](https://phpackages.com/packages/texthtml-vo-identity)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
