Skip to content

Exceptions

All exceptions extend from ImmutableException, which itself extends PHP’s RuntimeException:

RuntimeException
└── ImmutableException (abstract)
├── PropertyDoesNotExistException
├── ReadOnlyPropertyException
├── InvalidTypeException
└── UnsupportedTypeException

The abstract base class for all package exceptions. Use this for catch-all handling:

use Cline\Immutable\Exceptions\ImmutableException;
try {
$updated = $user->mutate(['invalid' => 'data']);
} catch (ImmutableException $e) {
// Handles any mutation error
log_error($e->getMessage());
}

Thrown when attempting to mutate a property that doesn’t exist on the object.

PropertyDoesNotExistException::forProperty(
string $property,
string $class
): self
use Cline\Immutable\Exceptions\PropertyDoesNotExistException;
$user = new UserData('John', 'john@example.com', 30);
try {
$user->mutate(['nonexistent' => 'value']);
} catch (PropertyDoesNotExistException $e) {
$e->getMessage();
// 'Property "nonexistent" does not exist on class "UserData".'
}
  • Passing a property name that doesn’t exist on the object
  • Typos in property names
  • Attempting to set properties from a parent class that don’t exist

Thrown when attempting to mutate a property that is marked as read-only and cannot be changed.

ReadOnlyPropertyException::forProperty(
string $property,
string $class
): self
use Cline\Immutable\Exceptions\ReadOnlyPropertyException;
try {
$user->mutate(['id' => 'new-id']);
} catch (ReadOnlyPropertyException $e) {
$e->getMessage();
// 'Property "id" on class "UserData" is read-only and cannot be mutated.'
}
  • Attempting to mutate a property that has been explicitly marked as immutable
  • Trying to change properties that should never change after construction

Thrown when the value type doesn’t match the property’s declared type.

InvalidTypeException::mismatch(
string $property,
string $expectedType,
mixed $actualValue
): self
use Cline\Immutable\Exceptions\InvalidTypeException;
$user = new UserData('John', 'john@example.com', 30);
try {
$user->mutate(['age' => 'thirty']);
} catch (InvalidTypeException $e) {
$e->getMessage();
// 'Property "age" expects type "int", got "string".'
}
  • Passing a string where int is expected
  • Passing wrong class type for object properties
  • Passing wrong enum case type
  • Null for non-nullable properties
Property TypeValid Values
intIntegers only
floatFloats and integers
stringStrings only
boolBooleans only
arrayArrays only
objectAny object
mixedAny value
?typeType or null
Class nameInstance of that class

Union and intersection types skip validation.

Thrown when attempting to mutate an unsupported value type.

UnsupportedTypeException::forValue(mixed $value): self
use Cline\Immutable\Exceptions\UnsupportedTypeException;
use Cline\Immutable\Mutator;
try {
// This would fail if passed a non-object/non-array
Mutator::mutate('string', ['key' => 'value']);
} catch (UnsupportedTypeException $e) {
$e->getMessage();
// 'Cannot mutate value of type "string". Supported types: object, array.'
}

The Mutator supports:

  • Arrays
  • Objects (any type)
  • stdClass

It does not support scalar types (string, int, float, bool) or resources.

use Cline\Immutable\Exceptions\PropertyDoesNotExistException;
use Cline\Immutable\Exceptions\InvalidTypeException;
try {
$updated = $user->mutate($changes);
} catch (PropertyDoesNotExistException $e) {
// Handle unknown property
throw new ValidationException("Unknown field: " . $e->getMessage());
} catch (InvalidTypeException $e) {
// Handle type error
throw new ValidationException("Invalid type: " . $e->getMessage());
}

For user input, validate before mutating:

$validated = $request->validate([
'name' => 'string|max:255',
'age' => 'integer|min:0',
]);
// Now safe to mutate
$updated = $user->mutate($validated);
use Cline\Immutable\Exceptions\ImmutableException;
try {
$updated = $user->mutate($data);
} catch (ImmutableException $e) {
Log::warning('Mutation failed', [
'class' => get_class($user),
'data' => $data,
'error' => $e->getMessage(),
]);
throw $e;
}