Runtime API
This guide collects the core runtime methods exposed by generated DTOs and the shared base Dto class.
Most day-to-day usage happens through generated getters, setters, and with*() methods, but the base API is useful when you need dynamic access, key-type conversion, or framework-level helpers.
Construction
Constructor
$dto = new UserDto($data, ignoreMissing: false, type: null);data: initial array payloadignoreMissing: ignore unknown input keys instead of throwingtype: input key style such as underscored or dashed
create()
Convenience wrapper around new:
$dto = UserDto::create(['name' => 'Jane']);createFromArray()
Generated DTOs expose a typed static constructor:
$dto = UserDto::createFromArray([
'id' => 1,
'email' => 'jane@example.com',
]);This is usually the most readable entry point when you want shaped-array type information in static analysis.
fromArray()
Mutable DTOs also expose an instance-level hydrator:
$dto = new UserDto();
$dto->fromArray(['name' => 'Jane']);This mutates the current instance. Immutable DTOs use create(), createFromArray(), or generated with*() methods instead.
fromUnserialized()
Create a DTO from a JSON string produced by serialize():
$dto = UserDto::fromUnserialized($json);Mutable DTOs also expose an instance-level unserialize() method when you want to rehydrate an existing object in place.
Reading Data
Generated Getters
Generated DTOs expose typed getters such as:
$dto->getName();
$dto->getAddress();
$dto->getAddressOrFail();Dynamic get() and has()
Use these when the field name is only known at runtime:
$value = $dto->get('name');
$present = $dto->has('name');Both methods also accept an optional key type.
has() checks whether the field currently has a value, not whether the field exists in the DTO definition.
read()
Safely traverse nested DTOs, arrays, and ArrayAccess-backed structures:
$city = $order->read(['customer', 'address', 'city']);
$firstEmail = $company->read(['departments', 0, 'members', 0, 'email'], 'unknown');Path segments can be field names or collection indexes. When any segment is missing, read() returns the provided default value.
fields() and touchedFields()
$allFields = $dto->fields();
$changedFields = $dto->touchedFields();fields()returns all DTO field namestouchedFields()returns only fields that were set or mutated
validationRules()
Returns built-in validation metadata in a framework-agnostic format:
$rules = $dto->validationRules();
// ['email' => ['pattern' => '/.../'], 'age' => ['min' => 0]]Serialization
toArray()
Convert the DTO to an array:
$data = $dto->toArray();
$snake = $dto->toArray(UserDto::TYPE_UNDERSCORED);
$subset = $dto->toArray(fields: ['id', 'email']);The optional arguments are:
type: output key stylefields: only serialize a subset of fieldstouched: internal flag used bytouchedToArray()
touchedToArray()
Serialize only fields that were touched:
$changes = $dto->touchedToArray();This is useful for PATCH payloads, change tracking, and emitting only modified state.
serialize() and __toString()
serialize() returns a JSON string of touched fields:
$json = $dto->serialize();
echo $dto; // same touched-field JSON representationThis is different from PHP's native serialize($dto).
Native serialize() / unserialize()
DTOs implement __serialize() and __unserialize() for PHP's native serialization:
$serialized = serialize($dto);
$restored = unserialize($serialized);Native serialization also works on touched fields, not the full DTO state.
jsonSerialize()
DTOs implement JsonSerializable, so json_encode($dto) uses toArray().
Mutation
Mutable DTOs
Mutable DTOs support generated setters and dynamic set():
$dto->setName('Jane');
$dto->set('name', 'Jane');Immutable DTOs
Immutable DTOs support generated with*() methods and dynamic with():
$updated = $dto->withEmail('new@example.com');
$updated = $dto->with('email', 'new@example.com');These return a new instance.
clone()
Creates a deep clone of the DTO, including nested DTOs, arrays, and collections:
$copy = $dto->clone();Lazy field payloads are preserved in the clone as well.
Key Types
The base Dto class supports multiple key styles for input and output:
TYPE_DEFAULTTYPE_CAMELTYPE_UNDERSCOREDTYPE_DASHED
Examples:
$dto->fromArray($request->getData(), false, UserDto::TYPE_UNDERSCORED);
$query = $dto->toArray(UserDto::TYPE_DASHED);Global Default Key Type
You can set a global default for all DTOs:
use PhpCollective\Dto\Dto\Dto;
Dto::setDefaultKeyType(Dto::TYPE_UNDERSCORED);This affects calls where no explicit key type is passed.
Collections
setCollectionFactory()
Override collection instantiation globally:
use PhpCollective\Dto\Dto\Dto;
Dto::setCollectionFactory(fn (array $items) => collect($items));This is useful for framework-native collection classes in Laravel, CakePHP, or Symfony integrations.
Resetting Global Runtime State
Because collection factories and default key types are static global settings, tests should reset them explicitly:
Dto::setCollectionFactory(null);
Dto::setDefaultKeyType(null);Runtime Exceptions Worth Knowing
Common runtime failures include:
- missing required fields
- unknown dynamic field access in
get(),set(), orhas() - invalid regex patterns in built-in validation rules
- incompatible factory return types
- unknown fields during native
unserialize()
See Troubleshooting for the full exception guide.