🌐 Available in other languages: 繁體中文
An abstract base class designed for immutable objects, suitable for DTOs (Data Transfer Objects) and VOs (Value Objects) where data is initialized once and cannot be changed.
Focuses on immutability and type safety, with APIs that make it easy to construct immutable objects.
- Build objects via static constructors; ImmutableBase scans incoming keys/values and returns an instance.
- If a value’s type does not match the declared property type, an exception is thrown with detailed class/property info.
- Supports all PHP built-in types, Enums, instances, and union types.
- For properties typed as subclasses of ImmutableBase, arrays/objects matching the declared structure are automatically instantiated.
composer require reallifekip/immutable-baseuse ReallifeKip\ImmutableBase\Attributes\ArrayOf;
use ReallifeKip\ImmutableBase\Objects\DataTransferObject;
class UserDTO extends DataTransferObject
{
public readonly string $name;
public readonly int $age;
}
class UserListDTO extends DataTransferObject
{
#[ArrayOf(UserDTO::class)]
public readonly array $users;
}
$userList = UserListDTO::fromArray([
'users' => [
['name' => 'Alice', 'age' => 18],
'{"name": "Bob", "age": 19}',
UserDTO::fromArray(['name' => 'Carl', 'age' => 20]),
UserDTO::fromJson('{"name": "Dave", "age": 21}')
]
]);
print_r($userList);vendor/bin/phpunit testsvendor/bin/phpbench runuse ReallifeKip\ImmutableBase\Objects\DataTransferObject;
final class UserDTO extends DataTransferObject
{
public readonly string $name;
public readonly int $age;
}use ReallifeKip\ImmutableBase\Objects\ValueObject;
final class Money extends ValueObject
{
private readonly int $value;
}When scanning input, if a key is not a declared property of the class, it is ignored and will not exist on the resulting instance.
$user = User::fromArray([
'name' => 'Kip',
'age' => 18
]);$user = Money::fromJson('{"value": 1000}');
⚠️ This does not mutate the original object. A new instance is returned with partial updates, by design. For the underlying rationale, see Objects and references.
⚠️ Whenwith()targets a#[ArrayOf]property, the array is rebuilt.
Keys that are not declared properties are ignored and will not appear on the new instance.
// Update a scalar property
$newUser = $user->with([
'name' => 'someone'
]);
// Partial update of a nested object
$userWithNewAddress = $user->with([
'profile' => [
'address' => 'Taipei City'
]
]);// ['name' => 'Kip', 'age' => 18]
$user->toArray();
⚠️ Will be deprecated in v4.0.0. See Architecture: Inheritance for the new approach.
All properties must be public readonly. Intended for cross-layer data transport.
use ReallifeKip\ImmutableBase\DataTransferObject;
use ReallifeKip\ImmutableBase\ImmutableBase;
#[DataTransferObject]
class UserDTO extends ImmutableBase
{
public readonly string $name;
public readonly int $age;
public readonly string $email;
}
⚠️ Will be deprecated in v4.0.0. See Architecture: Inheritance.
All properties must be private. Intended for value objects in DDD.
use ReallifeKip\ImmutableBase\ValueObject;
use ReallifeKip\ImmutableBase\ImmutableBase;
#[ValueObject]
class Money extends ImmutableBase
{
private int $amount;
private string $currency;
public function getAmount(): int
{
return $this->amount;
}
public function getCurrency(): string
{
return $this->currency;
}
}
⚠️ Will be deprecated in v4.0.0. See Architecture: Inheritance.
All properties must be private. Intended for entities in DDD.
use ReallifeKip\ImmutableBase\Entity;
#[Entity]
class User extends ImmutableBase
{
private string $id;
private string $email;
private string $name;
public function getId(): string
{
return $this->id;
}
public function getEmail(): string
{
return $this->email;
}
}
⚠️ Whenwith()targets a#[ArrayOf]property, the array is rebuilt.
Marks an array property as an array of instances. Incoming data for that array will be converted into instances of the specified class.
Accepts JSON strings, arrays, or already-instantiated objects that match the required structure.
use ReallifeKip\ImmutableBase\Attributes\ArrayOf;
use ReallifeKip\ImmutableBase\Objects\DataTransferObject;
class UserListDTO extends DataTransferObject
{
#[ArrayOf(UserDTO::class)]
public readonly array $users;
}
$userList = UserListDTO::fromArray([
'users' => [
// All four forms are accepted
['name' => 'Alice', 'age' => 18],
'{"name": "Bob", "age": 19}',
UserDTO::fromArray(['name' => 'Carl', 'age' => 20]),
UserDTO::fromJson('{"name": "Dave", "age": 21}')
]
]);All properties must be public readonly. Intended for cross-layer data transport.
use ReallifeKip\ImmutableBase\Objects\DataTransferObject;
class UserDTO extends DataTransferObject
{
public readonly string $name;
public readonly int $age;
public readonly string $email;
}All properties must be private readonly. Intended for value objects in DDD.
use ReallifeKip\ImmutableBase\Objects\ValueObject;
class Money extends ValueObject
{
private int $value;
public function getValue(): int
{
return $this->value;
}
}- Property types must be explicitly declared;
mixedis not allowed. - Enums: when a property is typed as an Enum, construction validates incoming data against the Enum’s cases/values and the resulting property is the Enum instance. Use
stringtypes if you want raw text values.
This package is released under the MIT License.
Developed and maintained by Kip. Suitable for implementing immutable DTOs/VOs in Laravel, DDD, and Hexagonal Architecture.
Feedback and contributions are welcome — please open an Issue or submit a PR.