Skip to content

Casting Attributes

Reference for type casting attributes and 20+ built-in cast classes.

SimpleDtos apply casts in the following order:

  1. Explicit #[Cast] attributes (highest priority)
  2. Attribute-based casts (#[DataCollectionOf], #[ConvertEmptyToNull])
  3. Nested DTOs (always auto-casted, unless #[NoCasts] is used)
  4. #[AutoCast] for native types (lowest priority, opt-in)
TypeWithout #[AutoCast]With #[AutoCast]With #[NoCasts]
Nested DTOs✅ Auto-casted✅ Auto-casted❌ Disabled
Native types (int, string, etc.)❌ TypeError✅ Auto-casted❌ TypeError
Explicit #[Cast]✅ Applied✅ Applied❌ Disabled

Enable automatic native PHP type casting for primitive types (opt-in).

use event4u\DataHelpers\SimpleDto\Attributes\AutoCast;
// Class-level: Enable for ALL properties
#[AutoCast]
class UserDto extends SimpleDto
{
public readonly int $id; // "123" → 123 ✅
public readonly string $name; // 123 → "123" ✅
}
// Property-level: Enable for specific properties
class UserDto extends SimpleDto
{
#[AutoCast]
public readonly int $id; // "123" → 123 ✅
public readonly string $name; // Must be string (no conversion)
}

Key Points:

  • ✅ Automatic type casting is opt-in (not enabled by default)
  • ✅ Only affects native PHP types (int, string, float, bool, array)
  • ✅ Nested DTOs are always auto-casted (even without #[AutoCast])
  • ✅ Explicit casts (like #[Cast]) ALWAYS work, regardless of #[AutoCast]
  • ✅ Use #[AutoCast] for CSV/XML imports or inconsistent data sources
  • ✅ Skip #[AutoCast] for better performance and strict type checking

Nested DTOs are always automatically casted, even without #[AutoCast]:

class AddressDto extends SimpleDto
{
public function __construct(
public readonly string $street,
public readonly string $city,
) {}
}
class UserDto extends SimpleDto
{
public function __construct(
public readonly string $name,
public readonly AddressDto $address, // ← Always auto-casted!
) {}
}
// Array is automatically converted to AddressDto
$user = UserDto::fromArray([
'name' => 'John',
'address' => ['street' => 'Main St', 'city' => 'NYC'], // ← Array → AddressDto
]);

To disable nested DTO casting, use #[NoCasts]:

use event4u\DataHelpers\SimpleDto\Attributes\NoCasts;
#[NoCasts]
class StrictUserDto extends SimpleDto
{
public function __construct(
public readonly string $name,
public readonly AddressDto $address, // ← Must be AddressDto object!
) {}
}
// ❌ TypeError - array is not auto-casted with #[NoCasts]
$user = StrictUserDto::fromArray([
'name' => 'John',
'address' => ['street' => 'Main St', 'city' => 'NYC'],
]);
// ✅ Works - pass AddressDto object
$user = StrictUserDto::fromArray([
'name' => 'John',
'address' => AddressDto::fromArray(['street' => 'Main St', 'city' => 'NYC']),
]);

Cast property to a specific type (always applied, regardless of #[AutoCast]):

use event4u\DataHelpers\SimpleDto\Attributes\Cast;
use event4u\DataHelpers\SimpleDto\Casts\DateTimeCast;
#[Cast(DateTimeCast::class)]
public readonly Carbon $createdAt;

Note: #[Cast] attributes are disabled by #[NoCasts] for maximum performance.

#[Cast(StringCast::class)] // Cast to string
#[Cast(IntCast::class)] // Cast to integer
#[Cast(FloatCast::class)] // Cast to float
#[Cast(BoolCast::class)] // Cast to boolean
#[Cast(ArrayCast::class)] // Cast to array
#[Cast(DateTimeCast::class)] // Cast to Carbon
#[Cast(DateCast::class)] // Cast to Carbon (date only)
#[Cast(TimestampCast::class)] // Cast from timestamp
#[Cast(EnumCast::class)] // Cast to enum
#[Cast(BackedEnumCast::class)] // Cast to backed enum
#[Cast(CollectionCast::class)] // Cast to Collection
#[Cast(DataCollectionCast::class)] // Cast to DataCollection
#[Cast(ObjectCast::class)] // Cast to object
#[Cast(JsonCast::class)] // Cast JSON to array
#[Cast(EncryptedCast::class)] // Encrypt/decrypt
#[Cast(HashCast::class)] // One-way hash
#[Cast(DecimalCast::class)] // Cast to decimal
#[Cast(UuidCast::class)] // Cast to UUID
#[Cast(IpAddressCast::class)] // Cast to IP address
#[Cast(UrlCast::class)] // Cast to URL
// Date format
#[Cast(DateTimeCast::class, format: 'Y-m-d H:i:s')]
public readonly Carbon $createdAt;
// Nullable cast
#[Cast(IntCast::class, nullable: true)]
public readonly ?int $age;
// Default value
#[Cast(StringCast::class, default: 'N/A')]
public readonly string $name;
use event4u\DataHelpers\SimpleDto\Contracts\Cast;
class UpperCaseCast implements Cast
{
public function cast(mixed $value): string
{
return strtoupper((string) $value);
}
}
#[Cast(UpperCaseCast::class)]
public readonly string $name;
class UserDto extends SimpleDto
{
public function __construct(
#[Cast(StringCast::class)]
public readonly string $name,
#[Cast(DateTimeCast::class)]
public readonly Carbon $birthDate,
#[Cast(EnumCast::class)]
public readonly Status $status,
#[Cast(EncryptedCast::class)]
public readonly string $ssn,
) {}
}

Performance attributes can disable casting operations:

use event4u\DataHelpers\SimpleDto\Attributes\NoCasts;
#[NoCasts]
class StrictDto extends SimpleDto
{
public function __construct(
public readonly int $age,
public readonly AddressDto $address,
#[Cast(DateTimeCast::class)]
public readonly Carbon $date,
) {}
}
// ❌ ALL casts are disabled:
// - Native types: TypeError if wrong type
// - Nested DTOs: TypeError if array (must be object)
// - Explicit #[Cast]: Disabled

#[NoAttributes] - Disables Attribute-Based Casts

Section titled “#[NoAttributes] - Disables Attribute-Based Casts”
use event4u\DataHelpers\SimpleDto\Attributes\NoAttributes;
#[NoAttributes]
class SimpleDto extends SimpleDto
{
public function __construct(
public readonly int $age,
public readonly AddressDto $address,
#[Cast(DateTimeCast::class)] // ← Disabled!
public readonly Carbon $date,
) {}
}
// ✅ Nested DTOs still work (always auto-casted)
// ❌ #[Cast] attributes are disabled
// ❌ Native types: TypeError if wrong type (no AutoCast)
AttributeNested DTOsNative TypesExplicit #[Cast]
None✅ Auto❌ TypeError✅ Applied
#[AutoCast]✅ Auto✅ Auto✅ Applied
#[NoCasts]❌ TypeError❌ TypeError❌ Disabled
#[NoAttributes]✅ Auto❌ TypeError❌ Disabled

See Performance Attributes for more details.