PHPackages                             immutablephp/immutable - 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. immutablephp/immutable

ActiveLibrary

immutablephp/immutable
======================

Immutable base objects, value objects, and value bag.

1.0.0(7y ago)7920.5k↓73.2%1MITPHPPHP &gt;=7.2.0

Since Feb 4Pushed 7y ago6 watchersCompare

[ Source](https://github.com/immutablephp/immutable)[ Packagist](https://packagist.org/packages/immutablephp/immutable)[ RSS](/packages/immutablephp-immutable/feed)WikiDiscussions 1.x Synced 1mo ago

READMEChangelog (1)Dependencies (2)Versions (3)Used By (0)

immutablephp/immutable
======================

[](#immutablephpimmutable)

Provides truly immutable value objects and an immutable value bag, along with base *Immutable* and *ValueObject* classes for your own objects. It helps to prevent against the oversights described by the article [Avoiding Quasi-Immutable Objects in PHP](http://paul-m-jones.com/archives/6400).

Overview
--------

[](#overview)

The base *Immutable* class protects against common oversights in PHP regarding immutables:

- It defines `final public function __set()` and `final public function __unset()`to prevent adding and mutating undefined properties.
- It defines `final public function offsetSet()` and `final public function offsetUnset()`to prevent adding and mutating values via *ArrayAccess*.
- It prevents multiple calls to `__construct()` to re-initialize the object properties.

Further, the base *ValueObject* class `with()` method checks the types of all incoming values to make sure they are themselves immutable. It does so via the static methods on the *Type* class.

The *Type* class recognizes scalars and nulls as immutable. All other non-object values (such are resources and arrays) are rejected as mutable.

When it comes to objects, the *Type* class recognizes anything descended from *Immutable* as immutable, as well as *DateTimeImmutable*. To allow *Type* to recognize other immutable classes, call `Type::register()` with a variadic list of fully-qualified class names that you want to treat as immutable.

Making Your Own Immutable Value Objects
---------------------------------------

[](#making-your-own-immutable-value-objects)

> Note:
>
> This package can only do so much to keep you from accidentally overlooking mutability. For example, The *Immutable* and *ValueObject* classes cannot prevent you from deliberately adding your own mutable behaviors. Likewise, it is not possible to prevent against using reflection to mutate an object from the outside.

To create your own immutable value object, extend *ValueObject* with your own properties.

```
use Immutable\ValueObject\ValueObject;

class Address extends ValueObject
{
    protected $street;
    protected $city;
    protected $region;
    protected $postcode;
}
```

Then add a `with*()` method to allow changing of those values on a clone of the object, using the protected `with()` method on the base ValueObject.

```
class Address extends ValueObject
{
    protected $street;
    protected $city;
    protected $region;
    protected $postcode;

    public function withChanged(
        string $street,
        string $city,
        string $region,
        string $postcode
    ) {
        return $this->with([
            'street' => $street,
            'city' => $city,
            'region' => $region,
            'postcode' => $postcode
        ]);
    }
}
```

Finally, use that method in the constructor to initialize the properties, and call `parent::__construct()` to finish initialization.

```
class Address extends ValueObject
{
    protected $street;
    protected $city;
    protected $region;
    protected $postcode;

    public function __construct(
        string $street,
        string $city,
        string $region,
        string $postcode
    ) {
        $this->withChanged($street, $city, $region, $postcode);
        parent::__construct();
    }

    public function withChanged(
        string $street,
        string $city,
        string $region,
        string $postcode
    ) : self
    {
        return $this->with([
            'street' => $street,
            'city' => $city,
            'region' => $region,
            'postcode' => $postcode
        ]);
    }
}
```

> Warning:
>
> If you do not call `parent::__construct()` then the Value Object will not know that it has been initialized, and it will be possible to call the constructor multiple time to re-initialize the object.

Now you have an immutable Value Object.

You may find it useful to add validation; do so in your `with*()` methods, either directly or by calling a validation mechanism.

```
    public function withChanged(
        string $street,
        string $city,
        string $region,
        string $postcode
    ) {

        $valid = AddressValidator::validate($street, $city, $region, $postcode);
        if (! $valid) {
            throw new \RuntimeException('address is not valid');
        }

        return $this->with([
            'street' => $street,
            'city' => $city,
            'region' => $region,
            'postcode' => $postcode
        ]);
    }
```

Provided Immutable Value Objects
--------------------------------

[](#provided-immutable-value-objects)

This package provides several Value Objects, both as examples and for common usage.

### CreditCard

[](#creditcard)

```
use Immutable\ValueObject\CreditCard;

$creditCard = new CreditCard('5555-5555-5555-4444');

// reading
$creditCard->getNumber(); // '5555555555554444'
$creditCard->getBrand(); // 'VISA'

// changing
$newCreditCard = $creditCard->withNumber('4111-1111-1111-1111');
$newCreditCard->getNumber(); // '4111111111111111'
$newCreditCard->getBrand(); // 'MASTERCARD'
```

### Email

[](#email)

```
use Immutable\ValueObject\Email;

$email = new Email('bolivar@example.com');

// reading
$email->get(); // 'bolivar@example.com'

// changing
$newEmail = $email->withAddress('boshag@example.net');
$newEmail->get(); // 'boshag@example.net'
```

### Ip

[](#ip)

```
use Immutable\ValueObject\Ip;

$ip = new Ip('127.0.0.1');

// reading
$ip->get(); // '127.0.0.1'

// changing
$newIp = $ip->withAddress('192.168.0.1');
$newIp->get(); // '192.168.0.1'
```

### Isbn

[](#isbn)

```
use Immutable\ValueObject\Isbn;

$isbn = new Isbn('960-425-059-0');

// reading
$isbn->get(); // '960-425-059-0'

// changing
$newIsbn = $ip->withAddress('0-8044-2957-X');
$newIsbn->get(); // '0-8044-2957-X'
```

### Uri\\HttpUri

[](#urihttpuri)

```
use Immutable\ValueObject\Uri\HttpUri;

$httpUri = new HttpUri(
    'http://boshag:bopass@example.com:8080/foo?bar=baz#dib'
);

// reading
$httpUri->getScheme(); // 'http'
$httpUri->getHost(); // 'example.com'
$httpUri->getPort(); // 8080
$httpUri->getUser(); // 'boshag'
$httpUri->getPass(); // 'bopass'
$httpUri->getPath(); // /'foo'
$httpUri->getQuery(); // 'bar=baz'
$httpUri->getFragment(); // 'dib'

// changing
$newHttpUri = $httpUri
    ->withScheme('https')
    ->withHost('example.net')
    ->withPort('8888')
    ->withUser('newuser')
    ->withPass('newpass')
    ->withPath('/foo2')
    ->withQuery('zim=gir')
    ->withFragment('irk');

$newHttpUri->get(); // 'https://newuser:newpass@example.net:8888/foo2/?zim=gir#irk'
```

### Uuid

[](#uuid)

```
use Immutable\ValueObject\Uuid;

$uuid = new Uuid('12345678-90ab-cdef-1234-567890123456');

// reading
$uuid->get(); // '12345678-90ab-cdef-1234-567890123456'

// changing
$newUuid = $uuid->withIdentifier('11111111-1111-1111-1111-111111111111');
$newUuid->get(); // '11111111-1111-1111-1111-111111111111'

// create a new random UUIDv4 identifier
$uuidv4 = Uuid::newVersion4();
```

Immutable Bag
-------------

[](#immutable-bag)

The *Bag* is for an arbitrary collection of immutable values, and can be useful for immutable representations of JSON data.

```
use Immutable\Bag;

$bag = new Bag(['foo' => 'bar']);
echo $bag->foo; // bar
echo $bag['foo']; // bar

$bag->foo = 'baz'; // ImmutableObjectException
$bag = $bag->with('foo', 'baz');
echo $bag->foo; // baz
echo $bag['foo']; // baz

unset($bag->foo); // ImmutableObjectException
$bag = $bag->without('foo');

$bag->dib; // Notice: $dib not defined

$bag = $bag->with('dib', ['zim', 'gir']);
foreach ($bag->dib as $key => $value) {
    echo "$key:$value,"; // 0:zim,1:gir,
}
```

###  Health Score

35

—

LowBetter than 80% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity38

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% 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.

###  Release Activity

Cadence

Every ~0 days

Total

3

Last Release

2650d ago

Major Versions

0.x-dev → 1.0.02019-02-04

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/25754?v=4)[Paul M. Jones](/maintainers/pmjones)[@pmjones](https://github.com/pmjones)

---

Top Contributors

[![pmjones](https://avatars.githubusercontent.com/u/25754?v=4)](https://github.com/pmjones "pmjones (1 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/immutablephp-immutable/health.svg)

```
[![Health](https://phpackages.com/badges/immutablephp-immutable/health.svg)](https://phpackages.com/packages/immutablephp-immutable)
```

PHPackages © 2026

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