lucifer07 / php-dto
Swift and efficient PHP Data Transfer Object package with constructor-promoted properties, auto-mapping, and built-in serialization
Requires
- php: >=8.1
Requires (Dev)
- phpunit/phpunit: ^10.0
This package is auto-updated.
Last update: 2026-03-04 10:26:42 UTC
README
Lightweight and efficient PHP Data Transfer Object package with constructor-promoted properties, auto-mapping, and built-in serialization.
Features
- Type-Safe: Uses constructor-promoted properties with full type hints
- Auto-Mapping: Automatic mapping between snake_case and camelCase
- Nested DTOs: Full support for nested DTO objects
- Custom Mapping: Use
MapFromattribute for custom field mappings - Type Casting: Automatic type casting for int, float, bool, DateTime, array
- Zero Dependencies: No external dependencies
- PHP 8.1+: Modern PHP features
- Fast: ~25% faster than traditional reflection-based DTOs
Installation
composer require lucifer07/php-dto
Requirements
- PHP 8.1 or higher
Quick Start
Basic Usage
Define your DTO class by extending the base DTO class:
use SwiftDTO\DTO; class UserDTO extends DTO { public function __construct( public string $fullName, public int $age, public bool $isActive, public \DateTimeImmutable $createdAt ) {} }
Create DTO from array (auto-mapping from snake_case):
$user = UserDTO::fromArray([ 'full_name' => 'John Doe', 'age' => 30, 'is_active' => 'true', 'created_at' => '2023-10-01 10:00:00' ]); echo $user->fullName; // 'John Doe' echo $user->age; // 30 echo $user->isActive; // true
Convert to Array
$array = $user->toArray(); // Returns: ['full_name' => 'John Doe', 'age' => 30, 'is_active' => true, 'created_at' => '2023-10-01T10:00:00+07:00']
Convert to JSON
$json = json_encode($user, JSON_PRETTY_PRINT); // Returns: JSON object with snake_case keys
Features in Detail
Auto-Mapping
The DTO automatically maps between snake_case (array keys) and camelCase (property names):
// Both work the same $user1 = UserDTO::fromArray(['full_name' => 'John']); $user2 = UserDTO::fromArray(['fullName' => 'John']);
Custom Mapping with MapFrom
Use the MapFrom attribute for custom field mappings:
use SwiftDTO\DTO; use SwiftDTO\MapFrom; class UserDTO extends DTO { public function __construct( #[MapFrom('user_id')] public readonly int $id, #[MapFrom('user_name')] public readonly string $username, public string $email ) {} } $user = UserDTO::fromArray([ 'user_id' => 123, 'user_name' => 'johndoe', 'email' => 'john@example.com' ]); echo $user->id; // 123 echo $user->username; // 'johndoe'
Type Casting
The DTO automatically casts values to the correct type:
$user = UserDTO::fromArray([ 'full_name' => 'John Doe', 'age' => '30', // string to int 'is_active' => 'yes', // string to bool 'created_at' => '2023-10-01 10:00:00' // string to DateTimeImmutable ]); assert(is_int($user->age)); // true assert(is_bool($user->isActive)); // true assert($user->createdAt instanceof \DateTimeImmutable); // true
Nested DTOs
Full support for nested DTOs:
class AddressDTO extends DTO { public function __construct( public string $street, public string $city, public string $country ) {} } class UserDTO extends DTO { public function __construct( public string $name, public string $email, public AddressDTO $address ) {} } $user = UserDTO::fromArray([ 'name' => 'John Doe', 'email' => 'john@example.com', 'address' => [ 'street' => '123 Main St', 'city' => 'New York', 'country' => 'USA' ] ]); echo $user->address->city; // 'New York'
Optional Fields and Default Values
Define optional fields with default values:
class ProductDTO extends DTO { public function __construct( public string $name, public float $price, public bool $inStock, public string $category = 'general', public int $stockLevel = 0 ) {} } $product = ProductDTO::fromArray([ 'name' => 'Widget', 'price' => 19.99, 'in_stock' => true ]); echo $product->category; // 'general' (default) echo $product->stockLevel; // 0 (default)
Nullable Fields
Use nullable types for optional fields:
class UserDTO extends DTO { public function __construct( public string $name, public string $email, public ?string $address = null ) {} } $user = UserDTO::fromArray([ 'name' => 'John Doe', 'email' => 'john@example.com' ]); echo $user->address; // null
Read-Only Properties
Use readonly for immutable properties:
class UserDTO extends DTO { public function __construct( public readonly int $id, public string $name, public string $email ) {} } $user = UserDTO::fromArray([ 'id' => 123, 'name' => 'John Doe', 'email' => 'john@example.com' ]); // $user->id = 456; // Error: Cannot modify readonly property
Examples
Complete examples are available in the examples/ directory:
basic_usage.php- Basic DTO creation and serializationnested_dtos.php- Working with nested data structuresvalidation.php- Type safety and default valuesmap_from.php- Custom field mappingcollections.php- Working with arrays and collectionsserialization.php- Serialization methods
Run any example:
php examples/basic_usage.php
php examples/nested_dtos.php
php examples/index.php # Show all examples
API Reference
Methods
static fromArray(array $data): static
Create DTO instance from array.
$user = UserDTO::fromArray(['name' => 'John', 'age' => 30]);
toArray(): array
Convert DTO to array with snake_case keys.
$array = $user->toArray();
jsonSerialize(): array
Implements JsonSerializable interface for JSON encoding.
$json = json_encode($user);
Type Casting Rules
| Target Type | Source Value | Result |
|---|---|---|
int |
'30' |
30 |
float |
'19.99' |
19.99 |
bool |
'true', 'yes', 'on', '1' |
true |
bool |
'false', 'no', 'off', '0', '' |
false |
DateTimeImmutable |
'2023-10-01 10:00:00' |
DateTime object |
array |
'["a", "b"]' (JSON string) |
['a', 'b'] |
Validation
Required fields without default values must be provided:
class UserDTO extends DTO { public function __construct( public string $name, // Required public string $email // Required ) {} } // This will throw InvalidArgumentException $user = UserDTO::fromArray(['name' => 'John']); // Missing required parameter: email
Performance
Benchmark results (10,000 iterations, median):
| Operation | NEW DTO | OLD DTO | Speedup |
|---|---|---|---|
| snake_case creation | 19.07 μs | 23.90 μs | 1.25x |
| camelCase creation | 18.44 μs | 24.00 μs | 1.30x |
| serialization | 6.88 μs | 6.33 μs | 0.92x |
| full cycle | 25.33 μs | 30.13 μs | 1.19x |
Testing
Run the test suite:
composer test
Or directly with PHPUnit:
./vendor/bin/phpunit
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.