PHPackages                             fab2s/dt0 - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Validation &amp; Sanitization](/categories/validation)
4. /
5. fab2s/dt0

ActiveLibrary[Validation &amp; Sanitization](/categories/validation)

fab2s/dt0
=========

Immutable DTOs with bidirectional casting. No framework required. 8x faster than the alternative.

1.0.1(2mo ago)101.6k↓40.9%1MITPHPPHP ^8.1CI passing

Since Apr 28Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/fab2s/dt0)[ Packagist](https://packagist.org/packages/fab2s/dt0)[ Docs](https://github.com/fab2s/dt0)[ RSS](/packages/fab2s-dt0/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (3)Dependencies (11)Versions (6)Used By (1)

Dt0
===

[](#dt0)

[![CI](https://github.com/fab2s/dt0/actions/workflows/ci.yml/badge.svg)](https://github.com/fab2s/dt0/actions/workflows/ci.yml) [![QA](https://github.com/fab2s/dt0/actions/workflows/qa.yml/badge.svg)](https://github.com/fab2s/dt0/actions/workflows/qa.yml) [![codecov](https://camo.githubusercontent.com/6f758d0b081c90580465c93cd4eaa3e822a42d9c1486f4b6a274135232143338/68747470733a2f2f636f6465636f762e696f2f67682f66616232732f6474302f67726170682f62616467652e7376673f746f6b656e3d56525831365555423759)](https://codecov.io/gh/fab2s/dt0) [![PHPStan](https://camo.githubusercontent.com/83dd3d35cebed0eab9ee97ff1a5849c1344cda6a8ee9cac2cda20f5aa55b67bd/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c253230392d627269676874677265656e2e7376673f7374796c653d666c6174)](https://phpstan.org/) [![Latest Stable Version](https://camo.githubusercontent.com/368150bbee5ec183c202afd3af1cf99fc7f4d39e5e44aead23604f17e048bbd5/687474703a2f2f706f7365722e707567782e6f72672f66616232732f6474302f76)](https://packagist.org/packages/fab2s/dt0) [![Total Downloads](https://camo.githubusercontent.com/9916d595bdce58e403eb12cd932aba84bad890c9061e13501293ed19d3a03237/687474703a2f2f706f7365722e707567782e6f72672f66616232732f6474302f646f776e6c6f616473)](https://packagist.org/packages/fab2s/dt0) [![PRs Welcome](https://camo.githubusercontent.com/7d9ed3c8f22eceb1711573169b1390cc0b1194467340dc815205060c162b5309/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5052732d77656c636f6d652d627269676874677265656e2e7376673f7374796c653d666c6174)](http://makeapullrequest.com) [![License](https://camo.githubusercontent.com/5c3c9c2003c7ff695dc0651907838d8447304e275a0121a2a89b1c0551d440cb/687474703a2f2f706f7365722e707567782e6f72672f66616232732f6474302f6c6963656e7365)](https://packagist.org/packages/fab2s/dt0)

**Immutable PHP DTOs with bidirectional casting. No framework required. [~8x faster](#benchmarks) than the alternative.**

`Dt0` (*DeeTO*) is a PHP 8.1+ [Data Transfer Object](https://en.wikipedia.org/wiki/Data_transfer_object) implementation that uses native `readonly` properties for true immutability. One `#[Cast]` attribute handles input transformation, output formatting, defaults, and property renaming. Compiled once per class, fast always.

Quick Start
-----------

[](#quick-start)

```
use fab2s\Dt0\Dt0;
use fab2s\Dt0\Attribute\Cast;
use fab2s\Dt0\Caster\DateTimeCaster;
use fab2s\Dt0\Caster\DateTimeFormatCaster;

class UserDto extends Dt0
{
    public readonly int $id;
    public readonly string $name;
    public readonly string $email;

    #[Cast(
        in: DateTimeCaster::class,
        out: new DateTimeFormatCaster('Y-m-d'),
    )]
    public readonly DateTimeImmutable $createdAt;

    #[Cast(default: 'user')]
    public readonly string $role;
}

// Create from anything
$user = UserDto::make(id: 1, name: 'Jane', email: 'jane@example.com', createdAt: '2024-01-15');
$user = UserDto::fromArray($apiResponse);
$user = UserDto::fromJson($jsonString);

// Access properties
$user->name;       // 'Jane'
$user->createdAt;  // DateTimeImmutable instance
$user->role;       // 'user' (default applied)

// Output with casting applied
$user->toArray();  // [..., 'createdAt' => DateTimeImmutable, ...]
$user->toJson();   // {..., "createdAt": "2024-01-15", ...}

// Immutable updates
$admin = $user->update(role: 'admin');
$user->role;   // 'user' (unchanged)
$admin->role;  // 'admin' (new instance)
```

Installation
------------

[](#installation)

```
composer require fab2s/dt0
```

For Laravel model casting integration, see [laravel-dt0](https://github.com/fab2s/laravel-dt0).

Why Dt0
-------

[](#why-dt0)

- **Real immutability, enforced by PHP.** Native `readonly` properties — accidental writes cause fatal errors, not silent bugs.
- **One attribute to rule them all.** `#[Cast]` handles input transformation, output formatting, defaults, and property renaming in a single, composable attribute.
- **Framework-agnostic.** Use anywhere PHP runs — including [standalone validation](./docs/validation.md) powered by Laravel's validation engine, without the framework.
- **Compiled once, fast always.** Reflection and metadata processed once per class, then cached. [~8x faster than spatie/laravel-data](#benchmarks) for typical operations.

Creating Instances
------------------

[](#creating-instances)

MethodInputOn Failure`make(...$args)`Named/positional argsThrows`fromArray(array)`Associative arrayThrows`fromJson(string)`JSON stringThrows`fromGz(string)`Gzipped JSONThrows`from(mixed)`Array, JSON, or Dt0Throws`tryFrom(mixed)`Array, JSON, or Dt0Returns `null`Custom constructors with promoted properties are supported. See [Creating Instances](./docs/creating-instances.md) for constructors, `new` vs factory methods, and edge cases.

Casting
-------

[](#casting)

Dt0 supports bidirectional casting: transform values on the way **in** (hydration) and **out** (serialization).

```
use fab2s\Dt0\Attribute\Cast;
use fab2s\Dt0\Caster\DateTimeCaster;
use fab2s\Dt0\Caster\DateTimeFormatCaster;
use fab2s\Dt0\Caster\ScalarCaster;
use fab2s\Dt0\Caster\ScalarType;

class ArticleDto extends Dt0
{
    public readonly string $title;

    #[Cast(in: new ScalarCaster(ScalarType::int))]
    public readonly int $viewCount;

    #[Cast(
        in: DateTimeCaster::class,
        out: new DateTimeFormatCaster(DateTimeFormatCaster::ISO),
    )]
    public readonly DateTimeImmutable $publishedAt;
}

$article = ArticleDto::make(
    title: 'Hello World',
    viewCount: '42',           // string -> int
    publishedAt: '2024-01-15', // string -> DateTimeImmutable
);
```

Class-level casting with `#[Casts]`:

```
#[Casts(
    status: new Cast(default: 'pending'),
    priority: new Cast(default: 0),
    createdAt: new Cast(in: DateTimeCaster::class),
)]
class TaskDto extends Dt0
{
    public readonly string $title;
    public readonly string $status;
    public readonly int $priority;
    public readonly DateTime $createdAt;
}
```

**Built-in types** — Enums and nested Dt0 classes are handled automatically, no caster needed:

```
class OrderDto extends Dt0
{
    public readonly Status $status;      // BackedEnum — auto-cast from string/int
    public readonly AddressDto $address; // Nested Dt0 — auto-cast from array/JSON
}
```

### Available Casters

[](#available-casters)

CasterDescription[`ScalarCaster`](./src/Caster/ScalarCaster.php)Cast to `int`, `float`, `bool`, `string`[`JsonCaster`](./src/Caster/JsonCaster.php)Decode JSON on input, encode on output[`TrimCaster`](./src/Caster/TrimCaster.php)Trim strings (`ltrim`, `rtrim`, custom characters)[`Base64Caster`](./src/Caster/Base64Caster.php)Decode base64 on input, encode on output[`DateTimeCaster`](./src/Caster/DateTimeCaster.php)Parse to `DateTime` or `DateTimeImmutable`[`DateTimeFormatCaster`](./src/Caster/DateTimeFormatCaster.php)Format DateTime for output[`CarbonCaster`](./src/Caster/CarbonCaster.php)Parse to Carbon (requires `nesbot/carbon`)[`Dt0Caster`](./src/Caster/Dt0Caster.php)Cast to nested Dt0 instances[`ArrayOfCaster`](./src/Caster/ArrayOfCaster.php)Cast typed arrays (Dt0, Enum, or scalar)[`ClassCaster`](./src/Caster/ClassCaster.php)Instantiate arbitrary classes[`MathCaster`](./src/Caster/MathCaster.php)Precision numbers (requires `fab2s/math`)[`CasterCollection`](./src/Caster/CasterCollection.php)Chain multiple casters in a pipelineSee [Casters Documentation](./docs/casters.md) for detailed usage, bidirectional casting, and custom casters.

Validation
----------

[](#validation)

Dt0 provides [standalone validation](./docs/validation.md) powered by Laravel's validation engine — no Laravel framework required. In Laravel applications, it auto-detects the framework and uses Laravel's validator transparently.

Requires `illuminate/validation` and `illuminate/translation` (v11+):

```
composer require "illuminate/validation:^11.0|^12.0" "illuminate/translation:^11.0|^12.0"
```

```
use fab2s\Dt0\Dt0;
use fab2s\Dt0\Attribute\Rule;
use fab2s\Dt0\Attribute\Rules;
use fab2s\Dt0\Attribute\Validate;
use fab2s\Dt0\Validator\Validator;

#[Validate(Validator::class)]
#[Rules(
    email: new Rule('required|email'),
)]
class ContactDto extends Dt0
{
    public readonly string $email;

    #[Rule('required|string|min:2|max:100')]
    public readonly string $name;

    #[Rule('nullable|string|max:1000')]
    public readonly ?string $message;
}

// Throws ValidationException on failure
$contact = ContactDto::withValidation(
    email: 'test@example.com',
    name: 'John',
    message: 'Hello!',
);
```

Rules can be defined at three levels with clear priority: property `#[Rule]` &gt; class `#[Rules]` &gt; `#[Validate]` rules. The `ValidatorInterface` is open for custom implementations.

See [Validation Documentation](./docs/validation.md) for locale configuration, custom translations, and custom validators.

Output
------

[](#output)

```
$dto->toArray();      // Array with objects intact, output casters applied
$dto->toJsonArray();  // Array with objects serialized (JsonSerializable)
$dto->toJson();       // JSON string
$dto->toGz();         // Gzipped JSON string
json_encode($dto);    // JSON (implements JsonSerializable)
(string) $dto;        // JSON (implements Stringable)
```

### Output Filtering

[](#output-filtering)

```
// Exclude sensitive fields
$dto->without('password', 'apiKey')->toJson();

// Include only specific fields
$dto->only('id', 'name')->toArray();

// Add computed or protected properties
$dto->with('fullName', fn($d) => "$d->firstName $d->lastName")->toArray();
$dto->with('total', true)->toArray();       // calls getTotal()
$dto->with('internalField')->toArray();     // exposes protected property
```

Declarative output control with `#[With]` is also supported. See [Output Documentation](./docs/output.md) for details.

Immutable Operations
--------------------

[](#immutable-operations)

```
$copy = $dto->clone();
$updated = $dto->update(name: 'Jane', role: 'admin');
$dto->equals($updated);  // false

// Serialization round-trip
$restored = unserialize(serialize($dto));
$dto->equals($restored);  // true
```

Property Renaming
-----------------

[](#property-renaming)

Map between external names (APIs, databases) and internal property names:

```
class ApiResponseDto extends Dt0
{
    #[Cast(renameFrom: 'created_at', renameTo: 'createdAtStr')]
    public readonly string $createdAt;

    // Accept multiple input names
    #[Cast(renameFrom: ['user_name', 'username', 'login'])]
    public readonly string $userName;
}
```

All `renameTo` values are automatically added to `renameFrom`, ensuring round-trip consistency.

Default Values
--------------

[](#default-values)

```
class ConfigDto extends Dt0
{
    #[Cast(default: 3600)]
    public readonly int $ttl;

    #[Cast(default: null)]
    public readonly ?string $prefix;

    #[Cast(default: true)]
    public readonly bool $enabled;
}

$config = ConfigDto::make();  // All defaults applied
```

**Resolution order**: provided value &gt; Cast default &gt; nullable default &gt; promoted parameter default.

Attribute Inheritance
---------------------

[](#attribute-inheritance)

Dt0 resolves attributes up the parent class chain — both property-level and class-level:

```
class TimestampedDto extends Dt0
{
    #[Cast(in: DateTimeCaster::class, out: new DateTimeFormatCaster(DateTimeFormatCaster::ISO))]
    public readonly DateTimeImmutable $createdAt;
}

// Inherits Cast from TimestampedDto
class ArticleDto extends TimestampedDto
{
    public readonly string $title;
    public readonly DateTimeImmutable $createdAt;  // Redeclare for PHP < 8.4
}

$article = ArticleDto::make(title: 'Hello', createdAt: '2024-01-15');
$article->createdAt;  // DateTimeImmutable (inherited cast applied)
```

See [Inheritance Documentation](./docs/inheritance.md) for multi-level inheritance, class attribute inheritance, and override patterns.

Performance
-----------

[](#performance)

Reflection and attribute metadata compiled **once per class, per process**. Subsequent instantiations reuse cached data with zero reflection overhead.

### Benchmarks

[](#benchmarks)

#### Dt0 vs spatie/laravel-data (PHP 8.4, 10,000 iterations)

[](#dt0-vs-spatielaravel-data-php-84-10000-iterations)

OperationDt0spatie/laravel-dataSpeedupSimple DTO (8 props, 5 casts)141.6 µs1,158 µs**~8.2x faster**Complex DTO (nested + arrays)741.9 µs3,628 µs**~4.9x faster**Round-trip (json-&gt;dto-&gt;json)248.4 µs2,004 µs**~8.1x faster****Repeated serialization (same instance):**

OperationDt0spatie/laravel-dataSpeeduptoArray() (simple)3.6 µs679.4 µs**~188.7x faster**toArray() (nested)3.6 µs2,056 µs**~571.1x faster**toJson()2.8 µs681.8 µs**~243.5x faster**Output caching delivers 188-571x improvements when serializing the same instance multiple times (API + logging, event sourcing, queue + monitoring, caching layers).

```
php benchmark/compare-spatie.php
```

Extending
---------

[](#extending)

Dt0's attributes, casters, and validators are extensible. See [Extending Documentation](./docs/extending.md) for interfaces, abstract classes, and compiled metadata access.

Exceptions
----------

[](#exceptions)

All exceptions extend [`ContextException`](https://github.com/fab2s/ContextException) with structured context for debugging:

ExceptionUsage`Dt0Exception`General DTO errors (missing properties, invalid input)`CasterException`Casting failures`AttributeException`Attribute configuration errorsRequirements
------------

[](#requirements)

- PHP 8.1, 8.2, 8.3, or 8.4

Dependencies
------------

[](#dependencies)

- [`fab2s/context-exception`](https://github.com/fab2s/ContextException) - Contextual exceptions
- [`fab2s/enumerate`](https://github.com/fab2s/Enumerate) - Enum utilities

### Optional

[](#optional)

- [`illuminate/validation`](https://github.com/illuminate/validation) + [`illuminate/translation`](https://github.com/illuminate/translation) - For standalone `Validator`
- [`nesbot/carbon`](https://github.com/briannesbitt/Carbon) - For `CarbonCaster`
- [`fab2s/math`](https://github.com/fab2s/Math) - For `MathCaster`

Contributing
------------

[](#contributing)

Contributions are welcome. Please open issues and submit pull requests.

```
composer fix        # Code style (Laravel Pint)
composer test       # Run tests
composer cov        # Tests with coverage
composer stan       # PHPStan level 9 (src/)
composer stan-tests # PHPStan level 5 (tests/)
```

License
-------

[](#license)

Dt0 is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT).

###  Health Score

46

—

FairBetter than 93% of packages

Maintenance83

Actively maintained with recent releases

Popularity27

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~221 days

Total

4

Last Release

86d ago

Major Versions

0.0.1 → 1.0.02026-02-08

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/7323989?v=4)[fab2s](/maintainers/fab2s)[@fab2s](https://github.com/fab2s)

---

Top Contributors

[![fab2s](https://avatars.githubusercontent.com/u/7323989?v=4)](https://github.com/fab2s "fab2s (13 commits)")

---

Tags

data-transfer-objectdtoimmutabilityimmutablelaravelphpreadonlysymfonyjsonsymfonylaravelvalidationValue Objectserializationdata-transfer-objectdtoimmutablecastingphp8readonly

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/fab2s-dt0/health.svg)

```
[![Health](https://phpackages.com/badges/fab2s-dt0/health.svg)](https://phpackages.com/packages/fab2s-dt0)
```

###  Alternatives

[wendelladriel/laravel-validated-dto

Data Transfer Objects with validation for Laravel applications

759569.4k13](/packages/wendelladriel-laravel-validated-dto)[yorcreative/laravel-argonaut-dto

Argonaut is a lightweight Data Transfer Object (DTO) package for Laravel that supports nested casting, recursive serialization, and validation out of the box. Ideal for service layers, APIs, and clean architecture workflows.

1062.8k1](/packages/yorcreative-laravel-argonaut-dto)[event4u/data-helpers

Framework-agnostic PHP library for data mapping, DTOs and utilities. Includes DataMapper, SimpleDto/LiteDto, DataAccessor/Mutator/Filter and helper classes (MathHelper, EnvHelper, etc.). Works with Laravel, Symfony/Doctrine or standalone PHP.

1421.5k](/packages/event4u-data-helpers)[iamfarhad/validation

🇮🇷 Complete Laravel Persian validation package - Iranian national ID, mobile numbers, Shamsi dates, IBAN/Sheba, postal codes &amp; more. Modern Laravel 10-12 support with both ValidationRule objects &amp; string-based rules.

2917.3k](/packages/iamfarhad-validation)[sbsaga/toon

🧠 TOON for Laravel — a compact, human-readable, and token-efficient data format for AI prompts &amp; LLM contexts. Perfect for ChatGPT, Gemini, Claude, Mistral, and OpenAI integrations (JSON ⇄ TOON).

6115.6k](/packages/sbsaga-toon)[friendsofhyperf/validated-dto

The Data Transfer Objects with validation for Hyperf.

1412.9k](/packages/friendsofhyperf-validated-dto)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
