PHPackages                             ufo-tech/dto-transformer - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. ufo-tech/dto-transformer

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

ufo-tech/dto-transformer
========================

The library provides tools for two-way transformation of DTO objects ⇄ arrays, respecting typing, contracts, and flexible transformation logic.

3.0.1(1mo ago)32.2k5MITPHPPHP &gt;=8.3

Since Sep 22Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/UFO-Tech/dto-transformer)[ Packagist](https://packagist.org/packages/ufo-tech/dto-transformer)[ Docs](https://docs.ufo-tech.space/bin/view/docs/DTOTransformer/?language=en)[ RSS](/packages/ufo-tech-dto-transformer/feed)WikiDiscussions main Synced 3w ago

READMEChangelogDependencies (17)Versions (7)Used By (5)

DTO Transformer
===============

[](#dto-transformer)

`ufo-tech/dto-transformer` is a PHP 8.3+ library for converting DTO objects to arrays and hydrating DTO objects from arrays.

The current implementation is built around replaceable services:

- `DTOTransformer` is the main facade/service.
- `DTOFromArrayTransformer` hydrates arrays into DTO objects.
- `DTOToArrayTransformer` normalizes DTO objects into arrays.
- `ParamHydratorInterface` implementations resolve typed values during hydration.
- `PropertyNormalizerInterface` implementations normalize values during serialization.
- Reflection metadata, DocBlocks, strict mode and type schemas are centralized and cacheable.

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

[](#installation)

```
composer require ufo-tech/dto-transformer
```

Requirements:

- PHP `>=8.3`
- `ext-intl`
- `symfony/serializer`
- `symfony/validator`
- `symfony/cache-contracts`
- `phpdocumentor/reflection-docblock`
- `phpdocumentor/type-resolver`

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

[](#quick-start)

```
use Ufo\DTO\Factory\DefaultDTOTransformerFactory;

final class UserDto
{
    public function __construct(
        public string $name,
        public string $email,
    ) {}
}

$transformer = DefaultDTOTransformerFactory::default()->create();

$user = $transformer->transformFromArray(UserDto::class, [
    'name' => 'Alex',
    'email' => 'alex@example.com',
]);

$array = $transformer->transformToArray($user);
```

Static calls are still supported, but the transformer must be booted first:

```
use Ufo\DTO\DTOTransformer;
use Ufo\DTO\Factory\DefaultDTOTransformerFactory;

DTOTransformer::boot(DefaultDTOTransformerFactory::default()->create());

$dto = DTOTransformer::fromArray(UserDto::class, $payload);
$array = DTOTransformer::toArray($dto);
```

If the static facade is used before `DTOTransformer::boot()`, `NotInitializeException` is thrown.

Default Factories
-----------------

[](#default-factories)

Use `DefaultDTOTransformerFactory::default()` for the standard composition:

```
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Ufo\DTO\Factory\DefaultDTOTransformerFactory;

$cache = new FilesystemAdapter(namespace: 'dto_transformer');

$transformer = DefaultDTOTransformerFactory::default(
    persistentCache: $cache,
)->create();
```

The default factory wires:

- `RuntimeReflectionCache`
- `ReflectionMetadataProvider`
- `StrictModeResolver`
- `ReflectionTypeSchemaResolver`
- `DefaultDTOTransformerFromArrayFactory`
- `DefaultDTOTransformerToArrayFactory`
- `DefaultPropertyNormalizerFactory`

For custom wiring, instantiate `DefaultDTOTransformerFactory` with your own `DTOTransformerFromArrayFactoryInterface` and `DTOTransformerToArrayFactoryInterface` implementations.

Object To Array
---------------

[](#object-to-array)

```
$array = $transformer->transformToArray(
    dto: $dto,
    renameKey: ['name' => 'full_name'],
    asSmartArray: false,
    publicOnly: true,
    context: [],
);
```

The default normalizer chain is created by `DefaultPropertyNormalizerFactory`:

```
new PropertyNormalizer([
    new ScalarValueConverter(),
    new EnumNormalizer(),
    new DateTimeNormalizer($dateTimeValueConverter),
    new ArrayNormalizer(),
    new DtoNormalizer($metadataProvider, $serializationContextProvider),
]);
```

Normalizers implement:

```
use Ufo\DTO\Interfaces\Normalizer\PropertyNormalizerInterface;
use Ufo\DTO\VO\NormalizationContext;

interface PropertyNormalizerInterface
{
    public function supports(mixed $data, NormalizationContext $context): bool;

    public function normalize(mixed $data, NormalizationContext $context): mixed;
}
```

`DtoNormalizer` reads DTO properties through metadata, applies `renameKey`, `publicOnly`, `SerializationContext`, smart-array output and recursively delegates nested values back to the chain.

### Serialization Context

[](#serialization-context)

```
use DateTimeInterface;
use Ufo\DTO\Attributes\SerializationContext;
use Ufo\DTO\VO\NormalizationContext;

final class EventDto
{
    public function __construct(
        #[SerializationContext([
            NormalizationContext::DATE_FORMAT => DateTimeInterface::ATOM,
            NormalizationContext::DATE_TIMEZONE => 'UTC',
        ])]
        public DateTimeImmutable $createdAt,
    ) {}
}
```

Default enum output:

- backed enums are normalized to their backing value;
- unit enums are normalized to the case name.

Default DateTime output format is `Y-m-d H:i:s`. Use `SerializationContext` or call-level `context` to change date format, timezone or timestamp output.

Array To Object
---------------

[](#array-to-object)

```
$dto = $transformer->transformFromArray(UserDto::class, [
    'name' => 'Alex',
    'email' => 'alex@example.com',
]);
```

The default hydration chain is created by `DefaultDTOTransformerFromArrayFactory` and `DefaultParamHydratorFactory`:

```
new UnionParamHydrator();
new EnumParamHydrator();
new ScalarParamHydrator();
new ReflectionClassHydrator();
new ReflectionParameterHydrator();
new ReflectionPropertyHydrator();
new DateTimeHydrator();
new DtoHydrator($transformer, $customTransformers);
new ArrayItemsHydrator();
new AdditionalHydrator();
new MixedHydrator();
```

Hydrators implement:

```
use Ufo\DTO\Interfaces\Hydrator\ParamHydratorInterface;
use Ufo\DTO\VO\TransformationContext;

interface ParamHydratorInterface
{
    public function supports(array $schema): bool;

    public function resolve(
        array $schema,
        mixed $value,
        TransformationContext $context,
    ): mixed;
}
```

`ReflectionTypeSchemaResolver` builds schemas from native PHP types, DocBlocks, `AttrDTO`, namespaces and strict-mode metadata. If no schema is available, the original value is kept.

### Native Types

[](#native-types)

```
final class ProfileDto
{
    public function __construct(
        public string $name,
        public int $age,
        public bool $active,
    ) {}
}
```

### Nested DTOs

[](#nested-dtos)

```
final class OrderDto
{
    public function __construct(
        public UserDto $user,
    ) {}
}

$order = DTOTransformer::fromArray(OrderDto::class, [
    'user' => [
        'name' => 'Alex',
        'email' => 'alex@example.com',
    ],
]);
```

### Collections

[](#collections)

Collections can be described with DocBlocks:

```
final class TeamDto
{
    /**
     * @param UserDto[] $users
     */
    public function __construct(
        public array $users,
    ) {}
}
```

Or with `AttrDTO`:

```
use Ufo\DTO\Attributes\AttrDTO;

final class TeamDto
{
    public function __construct(
        #[AttrDTO(UserDto::class, context: [
            AttrDTO::C_COLLECTION => true,
        ])]
        public array $users,
    ) {}
}
```

### Smart Arrays

[](#smart-arrays)

Smart arrays include the DTO class name in the payload:

```
$array = DTOTransformer::toArray($dto, asSmartArray: true);

// [
//     'name' => 'Alex',
//     '$className' => App\Dto\UserDto::class,
// ]
```

They can be restored with namespace aliases:

```
$dto = DTOTransformer::fromSmartArray($array, namespaces: [
    DTOTransformer::DTO_NS_KEY => App\Dto::class,
]);
```

### DateTime

[](#datetime)

Hydration supports `DateTimeImmutable`, `DateTime`, `DateTimeInterface`, strings, integer timestamps and float timestamps with microseconds.

```
final class EventDto
{
    public function __construct(
        public DateTimeImmutable $createdAt,
    ) {}
}

$dto = DTOTransformer::fromArray(EventDto::class, [
    'createdAt' => '2026-05-08 14:30:00',
]);
```

### Enums

[](#enums)

Backed enums use `tryFrom()`. Integer-backed enums also accept numeric strings. Unit enums are resolved by case name, case-insensitively.

```
enum Status: string
{
    case Active = 'active';
}

final class UserDto
{
    public function __construct(
        public Status $status,
    ) {}
}
```

Attributes
----------

[](#attributes)

### `AttrDTO`

[](#attrdto)

`AttrDTO` gives explicit denormalization hints:

```
use Ufo\DTO\Attributes\AttrDTO;

final class WrapperDto
{
    public function __construct(
        #[AttrDTO(UserDto::class, context: [
            AttrDTO::C_COLLECTION => true,
            AttrDTO::C_STRICT => true,
        ])]
        public array $users,
    ) {}
}
```

Supported context keys:

- `AttrDTO::C_COLLECTION`
- `AttrDTO::C_STRICT`
- `AttrDTO::C_NS`
- `AttrDTO::C_RENAME_KEYS`
- `AttrDTO::C_TRANSFORMER`
- `AttrDTO::C_IS_ENUM`
- `AttrDTO::C_PROPERTY`

### `StrictMode`

[](#strictmode)

Strict mode turns hydration failures into `BadParamException`. Without strict mode, invalid nested values may be kept as-is.

```
use Ufo\DTO\Attributes\StrictMode;

final class UserDto
{
    public function __construct(
        #[StrictMode]
        public int $age,
    ) {}
}
```

DocBlock strict mode is also supported:

```
/**
 * @strictMode true
 */
final class UserDto
{
}
```

Extensibility
-------------

[](#extensibility)

Add custom object-to-array behavior with `PropertyNormalizerInterface`:

```
use Ufo\DTO\Interfaces\Normalizer\PropertyNormalizerInterface;
use Ufo\DTO\VO\NormalizationContext;

final class MoneyNormalizer implements PropertyNormalizerInterface
{
    public function supports(mixed $data, NormalizationContext $context): bool
    {
        return $data instanceof Money;
    }

    public function normalize(mixed $data, NormalizationContext $context): string
    {
        return $data->currency . ' ' . number_format($data->amount / 100, 2);
    }
}
```

Add custom array-to-object behavior with `ParamHydratorInterface`:

```
use Ufo\DTO\Interfaces\Hydrator\ParamHydratorInterface;
use Ufo\DTO\VO\TransformationContext;

final class UuidHydrator implements ParamHydratorInterface
{
    public function supports(array $schema): bool
    {
        return ($schema['format'] ?? null) === 'uuid';
    }

    public function resolve(array $schema, mixed $value, TransformationContext $context): Uuid
    {
        return Uuid::fromString((string) $value);
    }
}
```

For full control, provide your own factory implementations or construct `DTOFromArrayTransformer` / `DTOToArrayTransformer` with custom chains.

Symfony Integration
-------------------

[](#symfony-integration)

Install dependencies:

```
composer require ufo-tech/dto-transformer
composer require phpdocumentor/reflection-docblock
```

Configure services:

```
services:
  _defaults:
    autowire: true
    autoconfigure: true

  _instanceof:
    Ufo\DTO\Interfaces\Hydrator\ParamHydratorInterface:
      tags:
        - dto.param_hydrator

    Ufo\DTO\Interfaces\Normalizer\PropertyNormalizerInterface:
      tags:
        - dto.param_normalizer

  Ufo\DTO\:
    resource: '../vendor/ufo-tech/dto-transformer/src'
    exclude:
      - '../vendor/ufo-tech/dto-transformer/src/Annotations/'
      - '../vendor/ufo-tech/dto-transformer/src/Attributes/'

  phpDocumentor\Reflection\DocBlockFactoryInterface:
    class: phpDocumentor\Reflection\DocBlockFactory
    factory: [ 'phpDocumentor\Reflection\DocBlockFactory', 'createInstance' ]

  Ufo\DTO\Interfaces\Normalizer\PropertyNormalizerChainInterface:
    class: Ufo\DTO\Transformer\Normalizer\PropertyNormalizer
    arguments:
      $converters: !tagged_iterator dto.param_normalizer

  Ufo\DTO\Interfaces\DTOFromArrayTransformerInterface:
    factory: [ '@Ufo\DTO\Factory\DefaultDTOTransformerFromArrayFactory', 'create' ]

  Ufo\DTO\Interfaces\DTOToArrayTransformerInterface:
    factory: [ '@Ufo\DTO\Factory\DefaultDTOTransformerToArrayFactory', 'create' ]

  Ufo\DTO\DTOTransformer:
    public: true
    arguments:
      $fromArrayTransformer: '@Ufo\DTO\Interfaces\DTOFromArrayTransformerInterface'
      $toArrayTransformer: '@Ufo\DTO\Interfaces\DTOToArrayTransformerInterface'
```

`public: true` is required if the transformer is accessed through:

```
$container->get(DTOTransformer::class)
```

or inside `Bundle::boot()`.

Cache adapters are optional.

```
ufo.dto.cache:
  public: true
  class: Symfony\Component\Cache\Adapter\FilesystemAdapter
  arguments:
    $namespace: 'meta'
    $directory: '%rpc_cache_dirrectory%/dto-transformer'
    $defaultLifetime: '%rpc_filecache_ttl%'
```

Example:

```
use Ufo\DTO\DTOTransformer;

final class UserService
{
    public function __construct(
        private readonly DTOTransformer $transformer,
    ) {
    }
}
```

```
$user = $this->transformer->fromArray(UserDTO::class, [
    'name' => 'Alex',
]);

$array = $this->transformer->toArray($user);
```

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

[](#performance)

Recommendations:

- reuse one `DTOTransformer` service instance;
- pass a Symfony cache adapter to `DefaultDTOTransformerFactory::default()`;
- prefer native types where possible;
- use DocBlocks for collections and unions where native types are not enough;
- avoid rebuilding custom hydrator or normalizer chains per request.

Testing
-------

[](#testing)

```
docker exec php_dto php vendor/bin/phpunit
```

Benchmarks:

```
composer bench
```

License
-------

[](#license)

MIT

###  Health Score

50

—

FairBetter than 95% of packages

Maintenance94

Actively maintained with recent releases

Popularity23

Limited adoption so far

Community18

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 60% 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 ~49 days

Recently: every ~23 days

Total

6

Last Release

30d ago

Major Versions

1.4.0 → 2.0.62026-02-23

2.1.1 → 3.0.02026-05-25

### Community

Maintainers

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

![](https://www.gravatar.com/avatar/87aea0bcf81c64ced7c3a14a1746d603ec6e8acfba62ef85746df40e8c376e7f?d=identicon)[Alex Maistrenko](/maintainers/Alex%20Maistrenko)

---

Top Contributors

[![Ashterix](https://avatars.githubusercontent.com/u/5172394?v=4)](https://github.com/Ashterix "Ashterix (36 commits)")[![valiknik225](https://avatars.githubusercontent.com/u/137418080?v=4)](https://github.com/valiknik225 "valiknik225 (24 commits)")

---

Tags

phpjsonsymfonyarrayrpcValue Objectserializerhydratordata-transfer-objecttransformerdtodeserializerUfo

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/ufo-tech-dto-transformer/health.svg)

```
[![Health](https://phpackages.com/badges/ufo-tech-dto-transformer/health.svg)](https://phpackages.com/packages/ufo-tech-dto-transformer)
```

###  Alternatives

[web-auth/webauthn-framework

FIDO2/Webauthn library for PHP and Symfony Bundle.

51390.8k2](/packages/web-auth-webauthn-framework)[api-platform/core

Build a fully-featured hypermedia or GraphQL API in minutes!

2.6k50.1M314](/packages/api-platform-core)[api-platform/serializer

API Platform core Serializer

244.3M71](/packages/api-platform-serializer)[oro/platform

Business Application Platform (BAP)

642140.7k104](/packages/oro-platform)[shopware/core

Shopware platform is the core for all Shopware ecommerce products.

585.4M518](/packages/shopware-core)[cognesy/instructor-php

The complete AI toolkit for PHP: unified LLM API, structured outputs, agents, and coding agent control

318117.1k1](/packages/cognesy-instructor-php)

PHPackages © 2026

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