Static Mutations
Overview
Section titled “Overview”The Mutator class provides static methods to create modified copies of any object or array. Use it when working with third-party classes, stdClass, or when you need a standalone mutation utility.
Static API
Section titled “Static API”mutate()
Section titled “mutate()”The simplest way to create a modified copy:
<?php
use Cline\Immutable\Mutator;
// Mutate an array$original = ['name' => 'John', 'age' => 30];$updated = Mutator::mutate($original, ['age' => 31]);
// Original unchanged$original; // ['name' => 'John', 'age' => 30]$updated; // ['name' => 'John', 'age' => 31]
// Mutate an object$user = new UserData('John', 'john@example.com', 30);$updated = Mutator::mutate($user, ['age' => 31]);Fluent API
Section titled “Fluent API”For more complex mutations, use the fluent interface:
Start a fluent chain:
$mutator = Mutator::for($original);with()
Section titled “with()”Add multiple properties to modify:
$updated = Mutator::for($user) ->with(['name' => 'Jane', 'age' => 25]) ->get();Add a single property:
$updated = Mutator::for($user) ->set('name', 'Jane') ->get();Chaining
Section titled “Chaining”Chain multiple calls together:
$updated = Mutator::for($user) ->set('name', 'Jane') ->with(['email' => 'jane@example.com']) ->set('age', 25) ->get();Later mutations override earlier ones:
$updated = Mutator::for(['name' => 'John']) ->set('name', 'Jane') ->set('name', 'Bob') ->get();
$updated['name']; // 'Bob'Execute the mutation and return the result:
$result = Mutator::for($data)->with($changes)->get();Array Mutations
Section titled “Array Mutations”Replace Values
Section titled “Replace Values”$original = ['a' => 1, 'b' => 2];$updated = Mutator::mutate($original, ['a' => 10]);// ['a' => 10, 'b' => 2]Add New Keys
Section titled “Add New Keys”$original = ['a' => 1, 'b' => 2];$updated = Mutator::mutate($original, ['c' => 3]);// ['a' => 1, 'b' => 2, 'c' => 3]Numeric Keys
Section titled “Numeric Keys”$original = [1, 2, 3];$updated = Mutator::mutate($original, [0 => 10]);// [10, 2, 3]Nested Arrays
Section titled “Nested Arrays”Replace entire nested structures:
$original = ['user' => ['name' => 'John', 'age' => 30]];$updated = Mutator::mutate($original, [ 'user' => ['name' => 'Jane', 'age' => 25]]);// ['user' => ['name' => 'Jane', 'age' => 25]]Object Mutations
Section titled “Object Mutations”Objects with Immutable Trait
Section titled “Objects with Immutable Trait”The Mutator automatically delegates to the object’s mutate() method:
$user = new UserData('John', 'john@example.com', 30);$updated = Mutator::mutate($user, ['age' => 31]);
// Equivalent to:$updated = $user->mutate(['age' => 31]);Generic Objects
Section titled “Generic Objects”Works with any object using reflection:
class GenericObject{ public function __construct( public string $name, public int $value, ) {}}
$obj = new GenericObject('test', 42);$updated = Mutator::mutate($obj, ['value' => 100]);
$obj->value; // 42$updated->value; // 100stdClass Objects
Section titled “stdClass Objects”Full support for dynamic objects:
$obj = new stdClass();$obj->name = 'John';$obj->age = 30;
$updated = Mutator::mutate($obj, ['age' => 31]);$updated->age; // 31
// Can add new properties$updated = Mutator::mutate($obj, ['email' => 'john@example.com']);$updated->email; // 'john@example.com'Type Validation
Section titled “Type Validation”For objects with type hints, the Mutator validates values:
use Cline\Immutable\Exceptions\InvalidTypeException;
$obj = new GenericObject('test', 42);
// Throws InvalidTypeExceptionMutator::mutate($obj, ['value' => 'not an int']);Property Validation
Section titled “Property Validation”use Cline\Immutable\Exceptions\PropertyDoesNotExistException;
$obj = new GenericObject('test', 42);
// Throws PropertyDoesNotExistExceptionMutator::mutate($obj, ['nonexistent' => 'value']);Note: stdClass allows any property name without validation.
Complex Example
Section titled “Complex Example”Real-world usage with operation state transitions:
$operation = new OperationData( id: 'op-123', function: 'process', version: '1.0.0', status: OperationStatus::Running, progress: 50, result: null, errors: [], startedAt: $now, completedAt: null, cancelledAt: null, metadata: ['key' => 'value'],);
// Without immutable mutation (verbose):$cancelledOperation = new OperationData( id: $operation->id, function: $operation->function, version: $operation->version, status: OperationStatus::Cancelled, progress: $operation->progress, result: $operation->result, errors: $operation->errors, startedAt: $operation->startedAt, completedAt: $operation->completedAt, cancelledAt: new DateTimeImmutable(), metadata: $operation->metadata,);
// With Mutator (clean):$cancelledOperation = Mutator::mutate($operation, [ 'status' => OperationStatus::Cancelled, 'cancelledAt' => new DateTimeImmutable(),]);Laravel Facade
Section titled “Laravel Facade”For Laravel applications, use the facade:
<?php
use Cline\Immutable\Facades\Mutator;
$updated = Mutator::mutate($user, ['age' => 31]);
$updated = Mutator::for($user) ->with(['age' => 31]) ->get();